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} <meta-data>
+ * 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} <meta-data>
+ * 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} <meta-data>
+ * 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} <meta-data> 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} <meta-data> 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 <meta-data> 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 <meta-data> 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;
}