Merge "Notification actions API."
diff --git a/api/current.txt b/api/current.txt
index 1393eb0..20b0be6 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -3694,6 +3694,7 @@
public static class Notification.Builder {
ctor public Notification.Builder(android.content.Context);
+ method public android.app.Notification.Builder addAction(int, java.lang.CharSequence, android.app.PendingIntent);
method public android.app.Notification.Builder addKind(java.lang.String);
method public android.app.Notification getNotification();
method public android.app.Notification.Builder setAutoCancel(boolean);
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 5325af0..04ab407 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -23,9 +23,11 @@
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Bundle;
+import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
+import android.util.IntProperty;
import android.view.View;
import android.widget.ProgressBar;
import android.widget.RemoteViews;
@@ -185,6 +187,13 @@
*/
public RemoteViews contentView;
+
+ /**
+ * The view that will represent this notification in the pop-up "intruder alert" dialog.
+ * @hide
+ */
+ public RemoteViews intruderView;
+
/**
* The bitmap that may escape the bounds of the panel and bar.
*/
@@ -418,6 +427,64 @@
private Bundle extras;
/**
+ * Structure to encapsulate an "action", including title and icon, that can be attached to a Notification.
+ * @hide
+ */
+ private static class Action implements Parcelable {
+ public int icon;
+ public CharSequence title;
+ public PendingIntent actionIntent;
+ @SuppressWarnings("unused")
+ public Action() { }
+ private Action(Parcel in) {
+ icon = in.readInt();
+ title = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+ if (in.readInt() == 1) {
+ actionIntent = PendingIntent.CREATOR.createFromParcel(in);
+ }
+ }
+ public Action(int icon_, CharSequence title_, PendingIntent intent_) {
+ this.icon = icon_;
+ this.title = title_;
+ this.actionIntent = intent_;
+ }
+ @Override
+ public Action clone() {
+ return new Action(
+ this.icon,
+ this.title.toString(),
+ this.actionIntent // safe to alias
+ );
+ }
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(icon);
+ TextUtils.writeToParcel(title, out, flags);
+ if (actionIntent != null) {
+ out.writeInt(1);
+ actionIntent.writeToParcel(out, flags);
+ } else {
+ out.writeInt(0);
+ }
+ }
+ public static final Parcelable.Creator<Action> CREATOR
+ = new Parcelable.Creator<Action>() {
+ public Action createFromParcel(Parcel in) {
+ return new Action(in);
+ }
+ public Action[] newArray(int size) {
+ return new Action[size];
+ }
+ };
+ }
+
+ private Action[] actions;
+
+ /**
* Constructs a Notification object with default values.
* You might want to consider using {@link Builder} instead.
*/
@@ -506,12 +573,17 @@
}
priority = parcel.readInt();
-
+
kind = parcel.createStringArray(); // may set kind to null
if (parcel.readInt() != 0) {
extras = parcel.readBundle();
}
+
+ actions = parcel.createTypedArray(Action.CREATOR);
+ if (parcel.readInt() != 0) {
+ intruderView = RemoteViews.CREATOR.createFromParcel(parcel);
+ }
}
@Override
@@ -571,6 +643,14 @@
}
+ that.actions = new Action[this.actions.length];
+ for(int i=0; i<this.actions.length; i++) {
+ that.actions[i] = this.actions[i].clone();
+ }
+ if (this.intruderView != null) {
+ that.intruderView = this.intruderView.clone();
+ }
+
return that;
}
@@ -658,6 +738,15 @@
} else {
parcel.writeInt(0);
}
+
+ parcel.writeTypedArray(actions, 0);
+
+ if (intruderView != null) {
+ parcel.writeInt(1);
+ intruderView.writeToParcel(parcel, 0);
+ } else {
+ parcel.writeInt(0);
+ }
}
/**
@@ -769,7 +858,14 @@
sb.append(this.kind[i]);
}
}
- sb.append("])");
+ sb.append("]");
+ if (actions != null) {
+ sb.append(" ");
+ sb.append(actions.length);
+ sb.append(" action");
+ if (actions.length > 1) sb.append("s");
+ }
+ sb.append(")");
return sb.toString();
}
@@ -821,6 +917,7 @@
private ArrayList<String> mKindList = new ArrayList<String>(1);
private Bundle mExtras;
private int mPriority;
+ private ArrayList<Action> mActions = new ArrayList<Action>(3);
/**
* Constructs a new Builder with the defaults:
@@ -1203,6 +1300,19 @@
return this;
}
+ /**
+ * Add an action to this notification. Actions are typically displayed by
+ * the system as a button adjacent to the notification content.
+ *
+ * @param icon Resource ID of a drawable that represents the action.
+ * @param title Text describing the action.
+ * @param intent PendingIntent to be fired when the action is invoked.
+ */
+ public Builder addAction(int icon, CharSequence title, PendingIntent intent) {
+ mActions.add(new Action(icon, title, intent));
+ return this;
+ }
+
private void setFlag(int mask, boolean value) {
if (value) {
mFlags |= mask;
@@ -1284,6 +1394,44 @@
}
}
+ private RemoteViews makeIntruderView() {
+ RemoteViews intruderView = new RemoteViews(mContext.getPackageName(),
+ R.layout.notification_intruder_content);
+ if (mLargeIcon != null) {
+ intruderView.setImageViewBitmap(R.id.icon, mLargeIcon);
+ intruderView.setViewVisibility(R.id.icon, View.VISIBLE);
+ } else if (mSmallIcon != 0) {
+ intruderView.setImageViewResource(R.id.icon, mSmallIcon);
+ intruderView.setViewVisibility(R.id.icon, View.VISIBLE);
+ } else {
+ intruderView.setViewVisibility(R.id.icon, View.GONE);
+ }
+ if (mContentTitle != null) {
+ intruderView.setTextViewText(R.id.title, mContentTitle);
+ }
+ if (mContentText != null) {
+ intruderView.setTextViewText(R.id.text, mContentText);
+ }
+ if (mActions.size() > 0) {
+ intruderView.setViewVisibility(R.id.actions, View.VISIBLE);
+ int N = mActions.size();
+ if (N>3) N=3;
+ final int[] BUTTONS = { R.id.action0, R.id.action1, R.id.action2 };
+ for (int i=0; i<N; i++) {
+ final Action action = mActions.get(i);
+ final int buttonId = BUTTONS[i];
+
+ intruderView.setViewVisibility(buttonId, View.VISIBLE);
+ intruderView.setImageViewResource(buttonId, action.icon);
+ intruderView.setContentDescription(buttonId, action.title);
+ intruderView.setOnClickPendingIntent(buttonId, action.actionIntent);
+ }
+ } else {
+ intruderView.setViewVisibility(R.id.actions, View.GONE);
+ }
+ return intruderView;
+ }
+
/**
* Combine all of the options that have been set and return a new {@link Notification}
* object.
@@ -1309,6 +1457,7 @@
n.ledOffMS = mLedOffMs;
n.defaults = mDefaults;
n.flags = mFlags;
+ n.intruderView = makeIntruderView();
if (mLedOnMs != 0 && mLedOffMs != 0) {
n.flags |= FLAG_SHOW_LIGHTS;
}
@@ -1323,6 +1472,10 @@
}
n.priority = mPriority;
n.extras = mExtras != null ? new Bundle(mExtras) : null;
+ if (mActions.size() > 0) {
+ n.actions = new Action[mActions.size()];
+ mActions.toArray(n.actions);
+ }
return n;
}
}
diff --git a/core/res/res/layout/notification_intruder_content.xml b/core/res/res/layout/notification_intruder_content.xml
new file mode 100644
index 0000000..61be5f5
--- /dev/null
+++ b/core/res/res/layout/notification_intruder_content.xml
@@ -0,0 +1,69 @@
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:background="#FF333333"
+ android:padding="4dp"
+ >
+ <ImageView android:id="@+id/icon"
+ android:layout_width="32dp"
+ android:layout_height="32dp"
+ android:scaleType="center"
+ android:padding="4dp"
+ />
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="40dp"
+ android:layout_marginLeft="40dp"
+ android:orientation="vertical"
+ >
+ <TextView android:id="@+id/title"
+ android:textAppearance="@style/TextAppearance.StatusBar.EventContent.Title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:fadingEdge="horizontal"
+ />
+ <TextView android:id="@+id/text"
+ android:textAppearance="@style/TextAppearance.StatusBar.EventContent"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_marginTop="-4dp"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:fadingEdge="horizontal"
+ />
+ </LinearLayout>
+ <LinearLayout
+ android:id="@+id/actions"
+ android:layout_width="match_parent"
+ android:layout_height="40dp"
+ android:layout_marginTop="48dp"
+ android:orientation="horizontal"
+ android:visibility="gone"
+ >
+ <ImageView
+ android:id="@+id/action0"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:visibility="gone"
+ />
+ <ImageView
+ android:id="@+id/action1"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:visibility="gone"
+ />
+ <ImageView
+ android:id="@+id/action2"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:visibility="gone"
+ />
+ </LinearLayout>
+</FrameLayout>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index ea1a70a..047e6ed 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -194,6 +194,10 @@
<java-symbol type="id" name="zoomIn" />
<java-symbol type="id" name="zoomMagnify" />
<java-symbol type="id" name="zoomOut" />
+ <java-symbol type="id" name="actions" />
+ <java-symbol type="id" name="action0" />
+ <java-symbol type="id" name="action1" />
+ <java-symbol type="id" name="action2" />
<java-symbol type="attr" name="actionModeShareDrawable" />
<java-symbol type="attr" name="alertDialogCenterButtons" />
@@ -1062,6 +1066,7 @@
<java-symbol type="layout" name="zoom_container" />
<java-symbol type="layout" name="zoom_controls" />
<java-symbol type="layout" name="zoom_magnify" />
+ <java-symbol type="layout" name="notification_intruder_content" />
<java-symbol type="anim" name="slide_in_child_bottom" />
<java-symbol type="anim" name="slide_in_right" />