Add app navigation helper code to the support library.

Add TaskStackBuilder for synthesizing back stacks for cross-task
navigation on Android 3.0 and newer.

Add NavUtils for general navigation helper code.

Add NotificationCompat.Builder as a partial backport of Android 3.0's
Notification.Builder. (It does not currently support progress
notifications.)

Change-Id: I2d7d4dab3dd26e34f6b2210d0f0f3909f2162d6f
diff --git a/v4/honeycomb/android/support/v4/app/ActivityCompatHoneycomb.java b/v4/honeycomb/android/support/v4/app/ActivityCompatHoneycomb.java
index 615bc44..51e37b3 100644
--- a/v4/honeycomb/android/support/v4/app/ActivityCompatHoneycomb.java
+++ b/v4/honeycomb/android/support/v4/app/ActivityCompatHoneycomb.java
@@ -17,6 +17,7 @@
 package android.support.v4.app;
 
 import android.app.Activity;
+import android.content.Intent;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -29,6 +30,10 @@
         activity.invalidateOptionsMenu();
     }
 
+    static void startActivities(Activity activity, Intent[] intents) {
+        activity.startActivities(intents);
+    }
+
     static void dump(Activity activity, String prefix, FileDescriptor fd,
             PrintWriter writer, String[] args) {
         activity.dump(prefix, fd, writer, args);
diff --git a/v4/honeycomb/android/support/v4/app/NotificationCompatHoneycomb.java b/v4/honeycomb/android/support/v4/app/NotificationCompatHoneycomb.java
new file mode 100644
index 0000000..1e59a1b
--- /dev/null
+++ b/v4/honeycomb/android/support/v4/app/NotificationCompatHoneycomb.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.v4.app;
+
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.widget.RemoteViews;
+
+class NotificationCompatHoneycomb {
+    static Notification add(Context context, Notification n,
+            CharSequence contentTitle, CharSequence contentText, CharSequence contentInfo,
+            RemoteViews tickerView, int number,
+            PendingIntent contentIntent, PendingIntent fullScreenIntent, Bitmap largeIcon) {
+        Notification.Builder b = new Notification.Builder(context)
+                .setWhen(n.when)
+                .setSmallIcon(n.icon, n.iconLevel)
+                .setContent(n.contentView)
+                .setTicker(n.tickerText, tickerView)
+                .setSound(n.sound, n.audioStreamType)
+                .setVibrate(n.vibrate)
+                .setLights(n.ledARGB, n.ledOnMS, n.ledOffMS)
+                .setOngoing((n.flags & Notification.FLAG_ONGOING_EVENT) != 0)
+                .setOnlyAlertOnce((n.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0)
+                .setAutoCancel((n.flags & Notification.FLAG_AUTO_CANCEL) != 0)
+                .setDefaults(n.defaults)
+                .setContentTitle(contentTitle)
+                .setContentText(contentText)
+                .setContentInfo(contentInfo)
+                .setContentIntent(contentIntent)
+                .setDeleteIntent(n.deleteIntent)
+                .setFullScreenIntent(fullScreenIntent,
+                        (n.flags & Notification.FLAG_HIGH_PRIORITY) != 0)
+                .setLargeIcon(largeIcon);
+
+        return b.getNotification();
+    }
+}
diff --git a/v4/honeycomb/android/support/v4/app/TaskStackBuilderHoneycomb.java b/v4/honeycomb/android/support/v4/app/TaskStackBuilderHoneycomb.java
new file mode 100644
index 0000000..24aba07
--- /dev/null
+++ b/v4/honeycomb/android/support/v4/app/TaskStackBuilderHoneycomb.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.v4.app;
+
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+
+/**
+ * Implementation of TaskStackBuilder that can call Honeycomb APIs.
+ */
+public class TaskStackBuilderHoneycomb {
+    public static PendingIntent getActivitiesPendingIntent(Context context, int requestCode,
+            Intent[] intents, int flags) {
+        return PendingIntent.getActivities(context, requestCode, intents, flags);
+    }
+}
diff --git a/v4/java/android/support/v4/app/ActivityCompat.java b/v4/java/android/support/v4/app/ActivityCompat.java
index d471c8f..bf86786 100644
--- a/v4/java/android/support/v4/app/ActivityCompat.java
+++ b/v4/java/android/support/v4/app/ActivityCompat.java
@@ -17,6 +17,7 @@
 package android.support.v4.app;
 
 import android.app.Activity;
+import android.content.Intent;
 import android.os.Build;
 
 /**
@@ -63,4 +64,37 @@
         }
         return false;
     }
+
+    /**
+     * Start a set of activities as a synthesized task stack, if able.
+     *
+     * <p>In API level 11 (Android 3.0/Honeycomb) the recommended conventions for
+     * app navigation using the back key changed. The back key's behavior is local
+     * to the current task and does not capture navigation across different tasks.
+     * Navigating across tasks and easily reaching the previous task is accomplished
+     * through the "recents" UI, accessible through the software-provided Recents key
+     * on the navigation or system bar. On devices with the older hardware button configuration
+     * the recents UI can be accessed with a long press on the Home key.</p>
+     *
+     * <p>When crossing from one task stack to another post-Android 3.0,
+     * the application should synthesize a back stack/history for the new task so that
+     * the user may navigate out of the new task and back to the Launcher by repeated
+     * presses of the back key. Back key presses should not navigate across task stacks.</p>
+     *
+     * <p>startActivities provides a mechanism for constructing a synthetic task stack of
+     * multiple activities. If the underlying API is not available on the system this method
+     * will return false.</p>
+     *
+     * @param activity Start activities using this activity as the starting context
+     * @param intents Array of intents defining the activities that will be started. The element
+     *                length-1 will correspond to the top activity on the resulting task stack.
+     * @return true if the underlying API was available and the call was successful, false otherwise
+     */
+    public static boolean startActivities(Activity activity, Intent[] intents) {
+        if (Build.VERSION.SDK_INT >= 11) {
+            ActivityCompatHoneycomb.startActivities(activity, intents);
+            return true;
+        }
+        return false;
+    }
 }
diff --git a/v4/java/android/support/v4/app/NavUtils.java b/v4/java/android/support/v4/app/NavUtils.java
new file mode 100644
index 0000000..4b43a75
--- /dev/null
+++ b/v4/java/android/support/v4/app/NavUtils.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.v4.app;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+
+/**
+ * NavUtils provides helper functionality for applications implementing
+ * recommended Android UI navigation patterns. For information about recommended
+ * navigation patterns see
+ * <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back Stack</a>
+ * from the developer guide and <a href="{@docRoot}design/patterns/navigation.html">Navigation</a>
+ * from the design guide.
+ */
+public class NavUtils {
+    private static final String TAG = "NavUtils";
+    public static final String PARENT_ACTIVITY = "android.support.PARENT_ACTIVITY";
+
+    /**
+     * Returns true if sourceActivity should recreate the task when navigating 'up'
+     * by using targetIntent.
+     *
+     * <p>If this method returns false the app can trivially call
+     * {@link #navigateUpTo(Activity, Intent)} using the same parameters to correctly perform
+     * up navigation. If this method returns false, the app should synthesize a new task stack
+     * by using {@link TaskStackBuilder} or another similar mechanism to perform up navigation.</p>
+     *
+     * @param sourceActivity The current activity from which the user is attempting to navigate up
+     * @param targetIntent An intent representing the target destination for up navigation
+     * @return true if navigating up should recreate a new task stack, false if the same task
+     *         should be used for the destination
+     */
+    public static boolean shouldUpRecreateTask(Activity sourceActivity, Intent targetIntent) {
+        String action = sourceActivity.getIntent().getAction();
+        return action != null && !action.equals(Intent.ACTION_MAIN);
+    }
+
+    /**
+     * Convenience method that is equivalent to calling
+     * <code>{@link #navigateUpTo(Activity, Intent) navigateUpTo}(sourceActivity,
+     * {@link #getParentActivityIntent(Activity) getParentActivityIntent} (sourceActivity))</code>.
+     * sourceActivity will be finished by this call.
+     *
+     * <p><em>Note:</em> This method should only be used when sourceActivity and the corresponding
+     * parent are within the same task. If up navigation should cross tasks in some cases, see
+     * {@link #shouldUpRecreateTask(Activity, Intent)}.</p>
+     *
+     * @param sourceActivity The current activity from which the user is attempting to navigate up
+     */
+    public static void navigateUpFromSameTask(Activity sourceActivity) {
+        Intent upIntent = getParentActivityIntent(sourceActivity);
+
+        if (upIntent == null) {
+            throw new IllegalArgumentException("Activity " +
+                    sourceActivity.getClass().getSimpleName() +
+                    " does not have a parent activity name specified." +
+                    " (Did you forget to add the android.support.PARENT_ACTIVITY <meta-data> " +
+                    " element in your manifest?)");
+        }
+
+        navigateUpTo(sourceActivity, upIntent);
+    }
+
+    /**
+     * Navigate from sourceActivity to the activity specified by upIntent, finishing sourceActivity
+     * in the process. upIntent will have the flag {@link Intent#FLAG_ACTIVITY_CLEAR_TOP} set
+     * by this method, along with any others required for proper up navigation as outlined
+     * in the Android Design Guide.
+     *
+     * <p>This method should be used when performing up navigation from within the same task
+     * as the destination. If up navigation should cross tasks in some cases, see
+     * {@link #shouldUpRecreateTask(Activity, Intent)}.</p>
+     *
+     * @param sourceActivity The current activity from which the user is attempting to navigate up
+     * @param upIntent An intent representing the target destination for up navigation
+     */
+    public static void navigateUpTo(Activity sourceActivity, Intent upIntent) {
+        upIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+        sourceActivity.startActivity(upIntent);
+        sourceActivity.finish();
+    }
+
+    /**
+     * Obtain an {@link Intent} that will launch {@link Intent#ACTION_MAIN} with an explicit
+     * target activity specified by sourceActivity's {@link #PARENT_ACTIVITY} &lt;meta-data&gt;
+     * element in the application's manifest.
+     *
+     * @param sourceActivity Activity to fetch a parent intent for
+     * @return a new Intent targeting the defined parent activity of sourceActivity
+     */
+    public static Intent getParentActivityIntent(Activity sourceActivity) {
+        String parentActivity = getParentActivityName(sourceActivity);
+        if (parentActivity == null) return null;
+        return new Intent(Intent.ACTION_MAIN).setClassName(sourceActivity, parentActivity);
+    }
+
+    /**
+     * Obtain an {@link Intent} that will launch {@link Intent#ACTION_MAIN} with an explicit
+     * target activity specified by sourceActivityClass's {@link #PARENT_ACTIVITY} &lt;meta-data&gt;
+     * element in the application's manifest.
+     *
+     * @param context Context for looking up the activity component for sourceActivityClass
+     * @param sourceActivityClass {@link java.lang.Class} object for an Activity class
+     * @return a new Intent targeting the defined parent activity of sourceActivity
+     * @throws NameNotFoundException if the ComponentName for sourceActivityClass is invalid
+     */
+    public static Intent getParentActivityIntent(Context context, Class<?> sourceActivityClass)
+            throws NameNotFoundException {
+        String parentActivity = getParentActivityName(context,
+                new ComponentName(context, sourceActivityClass));
+        if (parentActivity == null) return null;
+        return new Intent(Intent.ACTION_MAIN).setClassName(context, parentActivity);
+    }
+
+    /**
+     * Obtain an {@link Intent} that will launch {@link Intent#ACTION_MAIN} with an explicit
+     * target activity specified by sourceActivityClass's {@link #PARENT_ACTIVITY} &lt;meta-data&gt;
+     * element in the application's manifest.
+     *
+     * @param context Context for looking up the activity component for the source activity
+     * @param componentName ComponentName for the source Activity
+     * @return a new Intent targeting the defined parent activity of sourceActivity
+     * @throws NameNotFoundException if the ComponentName for sourceActivityClass is invalid
+     */
+    public static Intent getParentActivityIntent(Context context, ComponentName componentName)
+            throws NameNotFoundException {
+        String parentActivity = getParentActivityName(context, componentName);
+        if (parentActivity == null) return null;
+        if (parentActivity.charAt(0) == '.') {
+            parentActivity = context.getPackageName() + parentActivity;
+        }
+        return new Intent(Intent.ACTION_MAIN).setClassName(context, parentActivity);
+    }
+
+    /**
+     * Return the fully qualified class name of sourceActivity's parent activity as specified by
+     * a {@link #PARENT_ACTIVITY} &lt;meta-data&gt; element within the activity element in
+     * the application's manifest.
+     *
+     * @param sourceActivity Activity to fetch a parent class name for
+     * @return The fully qualified class name of sourceActivity's parent activity or null if
+     *         it was not specified
+     */
+    public static String getParentActivityName(Activity sourceActivity) {
+        try {
+            return getParentActivityName(sourceActivity, sourceActivity.getComponentName());
+        } catch (NameNotFoundException e) {
+            // Component name of supplied activity does not exist...?
+            throw new IllegalArgumentException(e);
+        }
+    }
+    /**
+     * Return the fully qualified class name of a source activity's parent activity as specified by
+     * a {@link #PARENT_ACTIVITY} &lt;meta-data&gt; element within the activity element in
+     * the application's manifest. The source activity is provided by componentName.
+     *
+     * @param context Context for looking up the activity component for the source activity
+     * @param componentName ComponentName for the source Activity
+     * @return The fully qualified class name of sourceActivity's parent activity or null if
+     *         it was not specified
+     */
+    public static String getParentActivityName(Context context, ComponentName componentName)
+            throws NameNotFoundException {
+        PackageManager pm = context.getPackageManager();
+        ActivityInfo info = pm.getActivityInfo(componentName, PackageManager.GET_META_DATA);
+        if (info.metaData == null) return null;
+        String parentActivity = info.metaData.getString(PARENT_ACTIVITY);
+        if (parentActivity == null) return null;
+        if (parentActivity.charAt(0) == '.') {
+            parentActivity = context.getPackageName() + parentActivity;
+        }
+        return parentActivity;
+    }
+
+    /** No instances! */
+    private NavUtils() {
+    }
+}
diff --git a/v4/java/android/support/v4/app/NotificationCompat.java b/v4/java/android/support/v4/app/NotificationCompat.java
new file mode 100644
index 0000000..71ec14e
--- /dev/null
+++ b/v4/java/android/support/v4/app/NotificationCompat.java
@@ -0,0 +1,388 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.v4.app;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.media.AudioManager;
+import android.net.Uri;
+import android.os.Build;
+import android.widget.RemoteViews;
+
+public class NotificationCompat {
+    /**
+     * Bit to be bitwise-ored into the {@link Notification#flags} field that should be set if
+     * this notification represents a high-priority event that may be shown to the user even
+     * if notifications are otherwise unavailable (that is, when the status bar is hidden).
+     * This flag is ideally used in conjunction with fullScreenIntent.
+     *
+     * <p>This will only be respected on API level 9 and above.</p>
+     */
+    public static final int FLAG_HIGH_PRIORITY = 0x00000080;
+
+    private static final NotificationCompatImpl IMPL;
+
+    interface NotificationCompatImpl {
+        public Notification getNotification(Builder b);
+    }
+
+    static class NotificationCompatImplBase implements NotificationCompatImpl {
+        public Notification getNotification(Builder b) {
+            Notification result = (Notification) b.mNotification;
+            result.setLatestEventInfo(b.mContext, b.mContentTitle,
+                    b.mContentText, b.mContentIntent);
+            return result;
+        }
+    }
+
+    static class NotificationCompatImplHoneycomb implements NotificationCompatImpl {
+        public Notification getNotification(Builder b) {
+            return NotificationCompatHoneycomb.add(b.mContext, b.mNotification,
+                    b.mContentTitle, b.mContentText, b.mContentInfo, b.mTickerView,
+                    b.mNumber, b.mContentIntent, b.mFullScreenIntent, b.mLargeIcon);
+        }
+    }
+
+    static {
+        if (Build.VERSION.SDK_INT >= 11) {
+            IMPL = new NotificationCompatImplHoneycomb();
+        } else {
+            IMPL = new NotificationCompatImplBase();
+        }
+    }
+
+    /**
+     * Builder class for {@link Notification} objects.  Allows easier control over
+     * all the flags, as well as help constructing the typical notification layouts.
+     */
+    public static class Builder {
+        Context mContext;
+
+        CharSequence mContentTitle;
+        CharSequence mContentText;
+        PendingIntent mContentIntent;
+        PendingIntent mFullScreenIntent;
+        RemoteViews mTickerView;
+        Bitmap mLargeIcon;
+        CharSequence mContentInfo;
+        int mNumber;
+
+        Notification mNotification = new Notification();
+
+        /**
+         * Constructor.
+         *
+         * Automatically sets the when field to {@link System#currentTimeMillis()
+         * System.currentTimeMillis()} and the audio stream to the
+         * {@link Notification#STREAM_DEFAULT}.
+         *
+         * @param context A {@link Context} that will be used to construct the
+         *      RemoteViews. The Context will not be held past the lifetime of this
+         *      Builder object.
+         */
+        public Builder(Context context) {
+            mContext = context;
+
+            // Set defaults to match the defaults of a Notification
+            mNotification.when = System.currentTimeMillis();
+            mNotification.audioStreamType = Notification.STREAM_DEFAULT;
+        }
+
+        /**
+         * Set the time that the event occurred.  Notifications in the panel are
+         * sorted by this time.
+         */
+        public Builder setWhen(long when) {
+            mNotification.when = when;
+            return this;
+        }
+
+        /**
+         * Set the small icon to use in the notification layouts.  Different classes of devices
+         * may return different sizes.  See the UX guidelines for more information on how to
+         * design these icons.
+         *
+         * @param icon A resource ID in the application's package of the drawble to use.
+         */
+        public Builder setSmallIcon(int icon) {
+            mNotification.icon = icon;
+            return this;
+        }
+
+        /**
+         * A variant of {@link #setSmallIcon(int) setSmallIcon(int)} that takes an additional
+         * level parameter for when the icon is a {@link android.graphics.drawable.LevelListDrawable
+         * LevelListDrawable}.
+         *
+         * @param icon A resource ID in the application's package of the drawble to use.
+         * @param level The level to use for the icon.
+         *
+         * @see android.graphics.drawable.LevelListDrawable
+         */
+        public Builder setSmallIcon(int icon, int level) {
+            mNotification.icon = icon;
+            mNotification.iconLevel = level;
+            return this;
+        }
+
+        /**
+         * Set the title (first row) of the notification, in a standard notification.
+         */
+        public Builder setContentTitle(CharSequence title) {
+            mContentTitle = title;
+            return this;
+        }
+
+        /**
+         * Set the text (second row) of the notification, in a standard notification.
+         */
+        public Builder setContentText(CharSequence text) {
+            mContentText = text;
+            return this;
+        }
+
+        /**
+         * Set the large number at the right-hand side of the notification.  This is
+         * equivalent to setContentInfo, although it might show the number in a different
+         * font size for readability.
+         */
+        public Builder setNumber(int number) {
+            mNumber = number;
+            return this;
+        }
+
+        /**
+         * Set the large text at the right-hand side of the notification.
+         */
+        public Builder setContentInfo(CharSequence info) {
+            mContentInfo = info;
+            return this;
+        }
+
+        /**
+         * Set the progress this notification represents, which may be
+         * represented as a {@link ProgressBar}.
+         */
+        /* TODO
+        public Builder setProgress(int max, int progress, boolean indeterminate) {
+            mProgressMax = max;
+            mProgress = progress;
+            mProgressIndeterminate = indeterminate;
+            return this;
+        }*/
+
+        /**
+         * Supply a custom RemoteViews to use instead of the standard one.
+         */
+        public Builder setContent(RemoteViews views) {
+            mNotification.contentView = views;
+            return this;
+        }
+
+        /**
+         * Supply a {@link PendingIntent} to send when the notification is clicked.
+         * If you do not supply an intent, you can now add PendingIntents to individual
+         * views to be launched when clicked by calling {@link RemoteViews#setOnClickPendingIntent
+         * RemoteViews.setOnClickPendingIntent(int,PendingIntent)}.  Be sure to
+         * read {@link Notification#contentIntent Notification.contentIntent} for
+         * how to correctly use this.
+         */
+        public Builder setContentIntent(PendingIntent intent) {
+            mContentIntent = intent;
+            return this;
+        }
+
+        /**
+         * Supply a {@link PendingIntent} to send when the notification is cleared by the user
+         * directly from the notification panel.  For example, this intent is sent when the user
+         * clicks the "Clear all" button, or the individual "X" buttons on notifications.  This
+         * intent is not sent when the application calls {@link NotificationManager#cancel
+         * NotificationManager.cancel(int)}.
+         */
+        public Builder setDeleteIntent(PendingIntent intent) {
+            mNotification.deleteIntent = intent;
+            return this;
+        }
+
+        /**
+         * An intent to launch instead of posting the notification to the status bar.
+         * Only for use with extremely high-priority notifications demanding the user's
+         * <strong>immediate</strong> attention, such as an incoming phone call or
+         * alarm clock that the user has explicitly set to a particular time.
+         * If this facility is used for something else, please give the user an option
+         * to turn it off and use a normal notification, as this can be extremely
+         * disruptive.
+         *
+         * @param intent The pending intent to launch.
+         * @param highPriority Passing true will cause this notification to be sent
+         *          even if other notifications are suppressed.
+         */
+        public Builder setFullScreenIntent(PendingIntent intent, boolean highPriority) {
+            mFullScreenIntent = intent;
+            setFlag(FLAG_HIGH_PRIORITY, highPriority);
+            return this;
+        }
+
+        /**
+         * Set the text that is displayed in the status bar when the notification first
+         * arrives.
+         */
+        public Builder setTicker(CharSequence tickerText) {
+            mNotification.tickerText = tickerText;
+            return this;
+        }
+
+        /**
+         * Set the text that is displayed in the status bar when the notification first
+         * arrives, and also a RemoteViews object that may be displayed instead on some
+         * devices.
+         */
+        public Builder setTicker(CharSequence tickerText, RemoteViews views) {
+            mNotification.tickerText = tickerText;
+            mTickerView = views;
+            return this;
+        }
+
+        /**
+         * Set the large icon that is shown in the ticker and notification.
+         */
+        public Builder setLargeIcon(Bitmap icon) {
+            mLargeIcon = icon;
+            return this;
+        }
+
+        /**
+         * Set the sound to play.  It will play on the default stream.
+         */
+        public Builder setSound(Uri sound) {
+            mNotification.sound = sound;
+            mNotification.audioStreamType = Notification.STREAM_DEFAULT;
+            return this;
+        }
+
+        /**
+         * Set the sound to play.  It will play on the stream you supply.
+         *
+         * @see #STREAM_DEFAULT
+         * @see AudioManager for the <code>STREAM_</code> constants.
+         */
+        public Builder setSound(Uri sound, int streamType) {
+            mNotification.sound = sound;
+            mNotification.audioStreamType = streamType;
+            return this;
+        }
+
+        /**
+         * Set the vibration pattern to use.
+         *
+         * @see android.os.Vibrator for a discussion of the <code>pattern</code>
+         * parameter.
+         */
+        public Builder setVibrate(long[] pattern) {
+            mNotification.vibrate = pattern;
+            return this;
+        }
+
+        /**
+         * Set the argb value that you would like the LED on the device to blnk, as well as the
+         * rate.  The rate is specified in terms of the number of milliseconds to be on
+         * and then the number of milliseconds to be off.
+         */
+        public Builder setLights(int argb, int onMs, int offMs) {
+            mNotification.ledARGB = argb;
+            mNotification.ledOnMS = onMs;
+            mNotification.ledOffMS = offMs;
+            boolean showLights = mNotification.ledOnMS != 0 && mNotification.ledOffMS != 0;
+            mNotification.flags = (mNotification.flags & ~Notification.FLAG_SHOW_LIGHTS) |
+                    (showLights ? Notification.FLAG_SHOW_LIGHTS : 0);
+            return this;
+        }
+
+        /**
+         * Set whether this is an ongoing notification.
+         *
+         * <p>Ongoing notifications differ from regular notifications in the following ways:
+         * <ul>
+         *   <li>Ongoing notifications are sorted above the regular notifications in the
+         *   notification panel.</li>
+         *   <li>Ongoing notifications do not have an 'X' close button, and are not affected
+         *   by the "Clear all" button.
+         * </ul>
+         */
+        public Builder setOngoing(boolean ongoing) {
+            setFlag(Notification.FLAG_ONGOING_EVENT, ongoing);
+            return this;
+        }
+
+        /**
+         * Set this flag if you would only like the sound, vibrate
+         * and ticker to be played if the notification is not already showing.
+         */
+        public Builder setOnlyAlertOnce(boolean onlyAlertOnce) {
+            setFlag(Notification.FLAG_ONLY_ALERT_ONCE, onlyAlertOnce);
+            return this;
+        }
+
+        /**
+         * Setting this flag will make it so the notification is automatically
+         * canceled when the user clicks it in the panel.  The PendingIntent
+         * set with {@link #setDeleteIntent} will be broadcast when the notification
+         * is canceled.
+         */
+        public Builder setAutoCancel(boolean autoCancel) {
+            setFlag(Notification.FLAG_AUTO_CANCEL, autoCancel);
+            return this;
+        }
+
+        /**
+         * Set the default notification options that will be used.
+         * <p>
+         * The value should be one or more of the following fields combined with
+         * bitwise-or:
+         * {@link Notification#DEFAULT_SOUND}, {@link Notification#DEFAULT_VIBRATE},
+         * {@link Notification#DEFAULT_LIGHTS}.
+         * <p>
+         * For all default values, use {@link Notification#DEFAULT_ALL}.
+         */
+        public Builder setDefaults(int defaults) {
+            mNotification.defaults = defaults;
+            if ((defaults & Notification.DEFAULT_LIGHTS) != 0) {
+                mNotification.flags |= Notification.FLAG_SHOW_LIGHTS;
+            }
+            return this;
+        }
+
+        private void setFlag(int mask, boolean value) {
+            if (value) {
+                mNotification.flags |= mask;
+            } else {
+                mNotification.flags &= ~mask;
+            }
+        }
+
+        /**
+         * Combine all of the options that have been set and return a new {@link Notification}
+         * object.
+         */
+        public Notification getNotification() {
+            return (Notification) IMPL.getNotification(this);
+        }
+    }
+}
diff --git a/v4/java/android/support/v4/app/TaskStackBuilder.java b/v4/java/android/support/v4/app/TaskStackBuilder.java
new file mode 100644
index 0000000..bca13eb9
--- /dev/null
+++ b/v4/java/android/support/v4/app/TaskStackBuilder.java
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.v4.app;
+
+import android.app.Activity;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.Build;
+import android.support.v4.content.IntentCompat;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+
+/**
+ * Utility class for constructing synthetic back stacks for cross-task navigation
+ * on Android 3.0 and newer.
+ *
+ * <p>In API level 11 (Android 3.0/Honeycomb) the recommended conventions for
+ * app navigation using the back key changed. The back key's behavior is local
+ * to the current task and does not capture navigation across different tasks.
+ * Navigating across tasks and easily reaching the previous task is accomplished
+ * through the "recents" UI, accessible through the software-provided Recents key
+ * on the navigation or system bar. On devices with the older hardware button configuration
+ * the recents UI can be accessed with a long press on the Home key.</p>
+ *
+ * <p>When crossing from one task stack to another post-Android 3.0,
+ * the application should synthesize a back stack/history for the new task so that
+ * the user may navigate out of the new task and back to the Launcher by repeated
+ * presses of the back key. Back key presses should not navigate across task stacks.</p>
+ *
+ * <p>TaskStackBuilder provides a backward-compatible way to obey the correct conventions
+ * around cross-task navigation on the device's version of the platform. On devices running
+ * Android 3.0 or newer, calls to the {@link #startActivities()} method or sending the
+ * {@link PendingIntent} generated by {@link #getPendingIntent(int, int)} will construct
+ * the synthetic back stack as prescribed. On devices running older versions of the platform,
+ * these same calls will invoke the topmost activity in the supplied stack, ignoring
+ * the rest of the synthetic stack and allowing the back key to navigate back to the previous
+ * task.</p>
+ *
+ * <div class="special reference">
+ * <h3>About Navigation</h3>
+ * For more detailed information about tasks, the back stack, and navigation design guidelines,
+ * please read
+ * <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back Stack</a>
+ * from the developer guide and <a href="{@docRoot}design/patterns/navigation.html">Navigation</a>
+ * from the design guide.
+ * </div>
+ */
+public class TaskStackBuilder implements Iterable<Intent> {
+    private static final String TAG = "TaskStackBuilder";
+
+    interface TaskStackBuilderImpl {
+        PendingIntent getPendingIntent(Context context, Intent[] intents, int requestCode,
+                int flags);
+    }
+
+    static class TaskStackBuilderImplBase implements TaskStackBuilderImpl {
+        public PendingIntent getPendingIntent(Context context, Intent[] intents, int requestCode,
+                int flags) {
+            Intent topIntent = intents[intents.length - 1];
+            topIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            return PendingIntent.getActivity(context, requestCode, topIntent, flags);
+        }
+    }
+
+    static class TaskStackBuilderImplHoneycomb implements TaskStackBuilderImpl {
+        public PendingIntent getPendingIntent(Context context, Intent[] intents, int requestCode,
+                int flags) {
+            intents[0].addFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
+                    IntentCompat.FLAG_ACTIVITY_CLEAR_TASK);
+            return TaskStackBuilderHoneycomb.getActivitiesPendingIntent(context, requestCode,
+                    intents, flags);
+        }
+    }
+
+    private static final TaskStackBuilderImpl IMPL;
+
+    static {
+        if (Build.VERSION.SDK_INT >= 11) {
+            IMPL = new TaskStackBuilderImplHoneycomb();
+        } else {
+            IMPL = new TaskStackBuilderImplBase();
+        }
+    }
+
+    private final ArrayList<Intent> mIntents = new ArrayList<Intent>();
+    private final Context mSourceContext;
+
+    private TaskStackBuilder(Context a) {
+        mSourceContext = a;
+    }
+
+    /**
+     * Return a new TaskStackBuilder for launching a fresh task stack consisting
+     * of a series of activities.
+     *
+     * @param context The context that will launch the new task stack or generate a PendingIntent
+     * @return A new TaskStackBuilder
+     */
+    public static TaskStackBuilder from(Context context) {
+        return new TaskStackBuilder(context);
+    }
+
+    /**
+     * Add a new Intent to the task stack. The most recently added Intent will invoke
+     * the Activity at the top of the final task stack.
+     *
+     * @param nextIntent Intent for the next Activity in the synthesized task stack
+     * @return This TaskStackBuilder for method chaining
+     */
+    public TaskStackBuilder addNextIntent(Intent nextIntent) {
+        mIntents.add(nextIntent);
+        return this;
+    }
+
+    /**
+     * Add the activity parent chain as specified by manifest &lt;meta-data&gt; elements
+     * to the task stack builder.
+     *
+     * @param sourceActivity All parents of this activity will be added
+     * @return This TaskStackBuilder for method chaining
+     */
+    public TaskStackBuilder addParentStack(Activity sourceActivity) {
+        final int insertAt = mIntents.size();
+        Intent parent = NavUtils.getParentActivityIntent(sourceActivity);
+        while (parent != null) {
+            mIntents.add(insertAt, parent);
+            try {
+                parent = NavUtils.getParentActivityIntent(sourceActivity, parent.getComponent());
+            } catch (NameNotFoundException e) {
+                Log.e(TAG, "Bad ComponentName while traversing activity parent metadata");
+                throw new IllegalArgumentException(e);
+            }
+        }
+        return this;
+    }
+
+    /**
+     * Add the activity parent chain as specified by manifest &lt;meta-data&gt; elements
+     * to the task stack builder.
+     *
+     * @param sourceActivityClass All parents of this activity will be added
+     * @return This TaskStackBuilder for method chaining
+     */
+    public TaskStackBuilder addParentStack(Class<?> sourceActivityClass) {
+        final int insertAt = mIntents.size();
+        try {
+            Intent parent = NavUtils.getParentActivityIntent(mSourceContext, sourceActivityClass);
+            while (parent != null) {
+                mIntents.add(insertAt, parent);
+                parent = NavUtils.getParentActivityIntent(mSourceContext, parent.getComponent());
+            }
+        } catch (NameNotFoundException e) {
+            Log.e(TAG, "Bad ComponentName while traversing activity parent metadata");
+            throw new IllegalArgumentException(e);
+        }
+        return this;
+    }
+
+    /**
+     * @return the number of intents added so far.
+     */
+    public int getIntentCount() {
+        return mIntents.size();
+    }
+
+    /**
+     * Get the intent at the specified index.
+     * Useful if you need to modify the flags or extras of an intent that was previously added,
+     * for example with {@link #addParentStack(Activity)}.
+     *
+     * @param index Index from 0-getIntentCount()
+     * @return the intent at position index
+     */
+    public Intent getIntent(int index) {
+        return mIntents.get(index);
+    }
+
+    public Iterator<Intent> iterator() {
+        return mIntents.iterator();
+    }
+
+    /**
+     * Start the task stack constructed by this builder. The Context used to obtain
+     * this builder must be an Activity.
+     *
+     * <p>On devices that do not support API level 11 or higher the topmost activity
+     * will be started as a new task. On devices that do support API level 11 or higher
+     * the new task stack will be created in its entirety.</p>
+     */
+    public void startActivities() {
+        if (mIntents.isEmpty()) {
+            throw new IllegalStateException(
+                    "No intents added to TaskStackBuilder; cannot startActivities");
+        }
+
+        Intent[] intents = mIntents.toArray(new Intent[mIntents.size()]);
+        intents[0].addFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
+                IntentCompat.FLAG_ACTIVITY_CLEAR_TASK |
+                IntentCompat.FLAG_ACTIVITY_TASK_ON_HOME);
+        if (!ActivityCompat.startActivities((Activity) mSourceContext, intents)) {
+            Intent topIntent = intents[intents.length - 1];
+            topIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            mSourceContext.startActivity(topIntent);
+        }
+    }
+
+    /**
+     * Obtain a {@link PendingIntent} for launching the task constructed by this builder so far.
+     *
+     * @param requestCode Private request code for the sender
+     * @param flags May be {@link PendingIntent#FLAG_ONE_SHOT},
+     *              {@link PendingIntent#FLAG_NO_CREATE}, {@link PendingIntent#FLAG_CANCEL_CURRENT},
+     *              {@link PendingIntent#FLAG_UPDATE_CURRENT}, or any of the flags supported by
+     *              {@link Intent#fillIn(Intent, int)} to control which unspecified parts of the
+     *              intent that can be supplied when the actual send happens.
+     * @return The obtained PendingIntent
+     */
+    public PendingIntent getPendingIntent(int requestCode, int flags) {
+        Intent[] intents = mIntents.toArray(new Intent[mIntents.size()]);
+        return IMPL.getPendingIntent(mSourceContext, intents, requestCode, flags);
+    }
+}
diff --git a/v4/java/android/support/v4/content/IntentCompat.java b/v4/java/android/support/v4/content/IntentCompat.java
index cbcd3f7..022eb58 100644
--- a/v4/java/android/support/v4/content/IntentCompat.java
+++ b/v4/java/android/support/v4/content/IntentCompat.java
@@ -16,6 +16,8 @@
 
 package android.support.v4.content;
 
+import android.content.Context;
+
 /**
  * Helper for accessing features in {@link android.content.Intent}
  * introduced after API level 4 in a backwards compatible fashion.
@@ -93,4 +95,26 @@
      */
     public static final String EXTRA_CHANGED_UID_LIST =
             "android.intent.extra.changed_uid_list";
+
+    /**
+     * If set in an Intent passed to {@link Context#startActivity Context.startActivity()},
+     * this flag will cause a newly launching task to be placed on top of the current
+     * home activity task (if there is one). That is, pressing back from the task
+     * will always return the user to home even if that was not the last activity they
+     * saw. This can only be used in conjunction with
+     * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK}.
+     */
+    public static final int FLAG_ACTIVITY_TASK_ON_HOME = 0x00004000;
+
+    /**
+     * If set in an Intent passed to {@link Context#startActivity Context.startActivity()},
+     * this flag will cause any existing task that would be associated with the
+     * activity to be cleared before the activity is started.  That is, the activity
+     * becomes the new root of an otherwise empty task, and any old activities
+     * are finished.  This can only be used in conjunction with
+     * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK}.
+     *
+     * <p>This flag will only be obeyed on devices supporting API 11 or higher.</p>
+     */
+    public static final int FLAG_ACTIVITY_CLEAR_TASK = 0x00008000;
 }