am 572532d0: Merge "Strip RemoteViews from Notifications, re-create them in SysUI" into lmp-dev

* commit '572532d089d15057c5e00a1d7fcf926d7555299f':
  Strip RemoteViews from Notifications, re-create them in SysUI
This commit is contained in:
Christoph Studer
2014-07-30 19:36:45 +00:00
committed by Android Git Automerger
7 changed files with 559 additions and 35 deletions

View File

@@ -4637,6 +4637,7 @@ package android.app {
field public static final int DEFAULT_SOUND = 1; // 0x1
field public static final int DEFAULT_VIBRATE = 2; // 0x2
field public static final java.lang.String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri";
field public static final java.lang.String EXTRA_BIG_TEXT = "android.bigText";
field public static final java.lang.String EXTRA_COMPACT_ACTIONS = "android.compactActions";
field public static final java.lang.String EXTRA_INFO_TEXT = "android.infoText";
field public static final java.lang.String EXTRA_LARGE_ICON = "android.largeIcon";
@@ -4825,7 +4826,6 @@ package android.app {
public static class Notification.MediaStyle extends android.app.Notification.Style {
ctor public Notification.MediaStyle();
ctor public Notification.MediaStyle(android.app.Notification.Builder);
method public android.app.Notification buildStyled(android.app.Notification);
method public android.app.Notification.MediaStyle setMediaSession(android.media.session.MediaSession.Token);
method public android.app.Notification.MediaStyle setShowActionsInCompactView(int...);
}

View File

@@ -21,6 +21,7 @@ import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
@@ -50,6 +51,7 @@ import com.android.internal.util.NotificationColorUtil;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Constructor;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Arrays;
@@ -690,6 +692,13 @@ public class Notification implements Parcelable
*/
public static final String EXTRA_SUMMARY_TEXT = "android.summaryText";
/**
* {@link #extras} key: this is the longer text shown in the big form of a
* {@link BigTextStyle} notification, as supplied to
* {@link BigTextStyle#bigText(CharSequence)}.
*/
public static final String EXTRA_BIG_TEXT = "android.bigText";
/**
* {@link #extras} key: this is the resource ID of the notification's main small icon, as
* supplied to {@link Builder#setSmallIcon(int)}.
@@ -1713,6 +1722,15 @@ public class Notification implements Parcelable
}
}
/**
* @hide
*/
public boolean isValid() {
// Would like to check for icon!=0 here, too, but NotificationManagerService accepts that
// for legacy reasons.
return contentView != null || extras.getBoolean(Builder.EXTRA_REBUILD_CONTENT_VIEW);
}
/**
* Builder class for {@link Notification} objects.
*
@@ -1737,6 +1755,55 @@ public class Notification implements Parcelable
public static class Builder {
private static final int MAX_ACTION_BUTTONS = 3;
/**
* @hide
*/
public static final String EXTRA_NEEDS_REBUILD = "android.rebuild";
/**
* @hide
*/
public static final String EXTRA_REBUILD_LARGE_ICON = "android.rebuild.largeIcon";
/**
* @hide
*/
public static final String EXTRA_REBUILD_CONTENT_VIEW = "android.rebuild.contentView";
/**
* @hide
*/
public static final String EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT =
"android.rebuild.contentViewActionCount";
/**
* @hide
*/
public static final String EXTRA_REBUILD_BIG_CONTENT_VIEW
= "android.rebuild.bigView";
/**
* @hide
*/
public static final String EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT
= "android.rebuild.bigViewActionCount";
/**
* @hide
*/
public static final String EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW
= "android.rebuild.hudView";
/**
* @hide
*/
public static final String EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT
= "android.rebuild.hudViewActionCount";
/**
* The package name of the context used to create the notification via a Builder.
*/
private static final String EXTRA_REBUILD_CONTEXT_PACKAGE =
"android.rebuild.contextPackage";
// Whether to enable stripping (at post time) & rebuilding (at listener receive time) of
// memory intensive resources.
private static final boolean STRIP_AND_REBUILD = true;
private Context mContext;
private long mWhen;
@@ -1781,6 +1848,17 @@ public class Notification implements Parcelable
private ArrayList<String> mPeople;
private int mColor = COLOR_DEFAULT;
/**
* Contains extras related to rebuilding during the build phase.
*/
private Bundle mRebuildBundle = new Bundle();
/**
* Contains the notification to rebuild when this Builder is in "rebuild" mode.
* Null otherwise.
*/
private Notification mRebuildNotification = null;
/**
* Constructs a new Builder with the defaults:
*
@@ -1822,6 +1900,40 @@ public class Notification implements Parcelable
mColorUtil = NotificationColorUtil.getInstance();
}
/**
* Creates a Builder for rebuilding the given Notification.
* <p>
* Call {@link #rebuild()} to retrieve the rebuilt version of 'n'.
*/
private Builder(Context context, Notification n) {
this(context);
mRebuildNotification = n;
restoreFromNotification(n);
Style style = null;
Bundle extras = n.extras;
String templateClass = extras.getString(EXTRA_TEMPLATE);
if (!TextUtils.isEmpty(templateClass)) {
Class<? extends Style> styleClass = getNotificationStyleClass(templateClass);
if (styleClass == null) {
Log.d(TAG, "Unknown style class: " + styleClass);
return;
}
try {
Constructor<? extends Style> constructor = styleClass.getConstructor();
style = constructor.newInstance();
style.restoreFromExtras(extras);
} catch (Throwable t) {
Log.e(TAG, "Could not create Style", t);
return;
}
}
if (style != null) {
setStyle(style);
}
}
/**
* Add a timestamp pertaining to the notification (usually the time the event occurred).
* It will be shown in the notification content view by default; use
@@ -2459,7 +2571,7 @@ public class Notification implements Parcelable
private RemoteViews applyStandardTemplate(int resId, boolean fitIn1U) {
Bitmap profileIcon = getProfileBadge();
RemoteViews contentView = new RemoteViews(mContext.getPackageName(), resId);
RemoteViews contentView = new BuilderRemoteViews(mContext.getPackageName(), resId);
boolean showLine3 = false;
boolean showLine2 = false;
@@ -2743,7 +2855,7 @@ public class Notification implements Parcelable
n.color = sanitizeColor();
n.contentView = makeContentView();
setBuilderContentView(n, makeContentView());
n.contentIntent = mContentIntent;
n.deleteIntent = mDeleteIntent;
n.fullScreenIntent = mFullScreenIntent;
@@ -2759,8 +2871,8 @@ public class Notification implements Parcelable
n.ledOffMS = mLedOffMs;
n.defaults = mDefaults;
n.flags = mFlags;
n.bigContentView = makeBigContentView();
n.headsUpContentView = makeHeadsUpContentView();
setBuilderBigContentView(n, makeBigContentView());
setBuilderHeadsUpContentView(n, makeHeadsUpContentView());
if (mLedOnMs != 0 || mLedOffMs != 0) {
n.flags |= FLAG_SHOW_LIGHTS;
}
@@ -2781,7 +2893,7 @@ public class Notification implements Parcelable
n.publicVersion = new Notification();
mPublicVersion.cloneInto(n.publicVersion, true);
}
// Note: If you're adding new fields, also update restoreFromNotitification().
return n;
}
@@ -2792,6 +2904,7 @@ public class Notification implements Parcelable
*/
public void populateExtras(Bundle extras) {
// Store original information used in the construction of this object
extras.putString(EXTRA_REBUILD_CONTEXT_PACKAGE, mContext.getPackageName());
extras.putCharSequence(EXTRA_TITLE, mContentTitle);
extras.putCharSequence(EXTRA_TEXT, mContentText);
extras.putCharSequence(EXTRA_SUB_TEXT, mSubText);
@@ -2808,6 +2921,233 @@ public class Notification implements Parcelable
if (!mPeople.isEmpty()) {
extras.putStringArray(EXTRA_PEOPLE, mPeople.toArray(new String[mPeople.size()]));
}
// NOTE: If you're adding new extras also update restoreFromNotification().
}
/**
* @hide
*/
public static void stripForDelivery(Notification n) {
if (!STRIP_AND_REBUILD) {
return;
}
String templateClass = n.extras.getString(EXTRA_TEMPLATE);
// Only strip views for known Styles because we won't know how to
// re-create them otherwise.
boolean stripViews = TextUtils.isEmpty(templateClass) ||
getNotificationStyleClass(templateClass) != null;
boolean isStripped = false;
if (n.largeIcon != null && n.extras.containsKey(EXTRA_LARGE_ICON)) {
// TODO: Would like to check for equality here, but if the notification
// has been cloned, we can't.
n.largeIcon = null;
n.extras.putBoolean(EXTRA_REBUILD_LARGE_ICON, true);
isStripped = true;
}
// Get rid of unmodified BuilderRemoteViews.
if (stripViews &&
n.contentView instanceof BuilderRemoteViews &&
n.extras.getInt(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT, -1) ==
n.contentView.getSequenceNumber()) {
n.contentView = null;
n.extras.putBoolean(EXTRA_REBUILD_CONTENT_VIEW, true);
n.extras.remove(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT);
isStripped = true;
}
if (stripViews &&
n.bigContentView instanceof BuilderRemoteViews &&
n.extras.getInt(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT, -1) ==
n.bigContentView.getSequenceNumber()) {
n.bigContentView = null;
n.extras.putBoolean(EXTRA_REBUILD_BIG_CONTENT_VIEW, true);
n.extras.remove(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT);
isStripped = true;
}
if (stripViews &&
n.headsUpContentView instanceof BuilderRemoteViews &&
n.extras.getInt(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT, -1) ==
n.headsUpContentView.getSequenceNumber()) {
n.headsUpContentView = null;
n.extras.putBoolean(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW, true);
n.extras.remove(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT);
isStripped = true;
}
if (isStripped) {
n.extras.putBoolean(EXTRA_NEEDS_REBUILD, true);
}
}
/**
* @hide
*/
public static Notification rebuild(Context context, Notification n) {
Bundle extras = n.extras;
if (!extras.getBoolean(EXTRA_NEEDS_REBUILD)) return n;
extras.remove(EXTRA_NEEDS_REBUILD);
// Re-create notification context so we can access app resources.
String packageName = extras.getString(EXTRA_REBUILD_CONTEXT_PACKAGE);
Context builderContext;
try {
builderContext = context.createPackageContext(packageName,
Context.CONTEXT_RESTRICTED);
} catch (NameNotFoundException e) {
Log.e(TAG, "Package name " + packageName + " not found");
builderContext = context; // try with our context
}
Builder b = new Builder(builderContext, n);
return b.rebuild();
}
/**
* Rebuilds the notification passed in to the rebuild-constructor
* {@link #Builder(Context, Notification)}.
*
* <p>
* Throws IllegalStateException when invoked on a Builder that isn't in rebuild mode.
*
* @hide
*/
private Notification rebuild() {
if (mRebuildNotification == null) {
throw new IllegalStateException("rebuild() only valid when in 'rebuild' mode.");
}
Bundle extras = mRebuildNotification.extras;
if (extras.getBoolean(EXTRA_REBUILD_LARGE_ICON)) {
mRebuildNotification.largeIcon = extras.getParcelable(EXTRA_LARGE_ICON);
}
extras.remove(EXTRA_REBUILD_LARGE_ICON);
if (extras.getBoolean(EXTRA_REBUILD_CONTENT_VIEW)) {
setBuilderContentView(mRebuildNotification, makeContentView());
if (mStyle != null) {
mStyle.populateContentView(mRebuildNotification);
}
}
extras.remove(EXTRA_REBUILD_CONTENT_VIEW);
if (extras.getBoolean(EXTRA_REBUILD_BIG_CONTENT_VIEW)) {
setBuilderBigContentView(mRebuildNotification, makeBigContentView());
if (mStyle != null) {
mStyle.populateBigContentView(mRebuildNotification);
}
}
extras.remove(EXTRA_REBUILD_BIG_CONTENT_VIEW);
if (extras.getBoolean(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW)) {
setBuilderHeadsUpContentView(mRebuildNotification, makeHeadsUpContentView());
if (mStyle != null) {
mStyle.populateHeadsUpContentView(mRebuildNotification);
}
}
extras.remove(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW);
return mRebuildNotification;
}
private static Class<? extends Style> getNotificationStyleClass(String templateClass) {
Class<? extends Style>[] classes = new Class[]{
BigTextStyle.class, BigPictureStyle.class, InboxStyle.class, MediaStyle.class};
for (Class<? extends Style> innerClass : classes) {
if (templateClass.equals(innerClass.getName())) {
return innerClass;
}
}
return null;
}
private void setBuilderContentView(Notification n, RemoteViews contentView) {
n.contentView = contentView;
if (contentView instanceof BuilderRemoteViews) {
mRebuildBundle.putInt(Builder.EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT,
contentView.getSequenceNumber());
}
}
private void setBuilderBigContentView(Notification n, RemoteViews bigContentView) {
n.bigContentView = bigContentView;
if (bigContentView instanceof BuilderRemoteViews) {
mRebuildBundle.putInt(Builder.EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT,
bigContentView.getSequenceNumber());
}
}
private void setBuilderHeadsUpContentView(Notification n,
RemoteViews headsUpContentView) {
n.headsUpContentView = headsUpContentView;
if (headsUpContentView instanceof BuilderRemoteViews) {
mRebuildBundle.putInt(Builder.EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT,
headsUpContentView.getSequenceNumber());
}
}
private void restoreFromNotification(Notification n) {
// Notification fields.
mWhen = n.when;
mSmallIcon = n.icon;
mSmallIconLevel = n.iconLevel;
mNumber = n.number;
mColor = n.color;
mContentView = n.contentView;
mDeleteIntent = n.deleteIntent;
mFullScreenIntent = n.fullScreenIntent;
mTickerText = n.tickerText;
mTickerView = n.tickerView;
mLargeIcon = n.largeIcon;
mSound = n.sound;
mAudioStreamType = n.audioStreamType;
mAudioAttributes = n.audioAttributes;
mVibrate = n.vibrate;
mLedArgb = n.ledARGB;
mLedOnMs = n.ledOnMS;
mLedOffMs = n.ledOffMS;
mDefaults = n.defaults;
mFlags = n.flags;
mCategory = n.category;
mGroupKey = n.mGroupKey;
mSortKey = n.mSortKey;
mPriority = n.priority;
mActions.clear();
if (n.actions != null) {
Collections.addAll(mActions, n.actions);
}
mVisibility = n.visibility;
mPublicVersion = n.publicVersion;
// Extras.
Bundle extras = n.extras;
mContentTitle = extras.getCharSequence(EXTRA_TITLE);
mContentText = extras.getCharSequence(EXTRA_TEXT);
mSubText = extras.getCharSequence(EXTRA_SUB_TEXT);
mContentInfo = extras.getCharSequence(EXTRA_INFO_TEXT);
mSmallIcon = extras.getInt(EXTRA_SMALL_ICON);
mProgress = extras.getInt(EXTRA_PROGRESS);
mProgressMax = extras.getInt(EXTRA_PROGRESS_MAX);
mProgressIndeterminate = extras.getBoolean(EXTRA_PROGRESS_INDETERMINATE);
mUseChronometer = extras.getBoolean(EXTRA_SHOW_CHRONOMETER);
mShowWhen = extras.getBoolean(EXTRA_SHOW_WHEN);
if (extras.containsKey(EXTRA_LARGE_ICON)) {
mLargeIcon = extras.getParcelable(EXTRA_LARGE_ICON);
}
if (extras.containsKey(EXTRA_PEOPLE)) {
mPeople.clear();
Collections.addAll(mPeople, extras.getStringArray(EXTRA_PEOPLE));
}
}
/**
@@ -2829,7 +3169,14 @@ public class Notification implements Parcelable
n = mStyle.buildStyled(n);
}
n.extras = mExtras != null ? new Bundle(mExtras) : new Bundle();
if (mExtras != null) {
n.extras.putAll(mExtras);
}
if (mRebuildBundle.size() > 0) {
n.extras.putAll(mRebuildBundle);
mRebuildBundle.clear();
}
populateExtras(n.extras);
if (mStyle != null) {
@@ -2849,7 +3196,6 @@ public class Notification implements Parcelable
return n;
}
private int getBaseLayoutResource() {
return R.layout.notification_template_material_base;
}
@@ -2924,12 +3270,16 @@ public class Notification implements Parcelable
protected RemoteViews getStandardView(int layoutId) {
checkBuilder();
// Nasty.
CharSequence oldBuilderContentTitle = mBuilder.mContentTitle;
if (mBigContentTitle != null) {
mBuilder.setContentTitle(mBigContentTitle);
}
RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(layoutId);
mBuilder.mContentTitle = oldBuilderContentTitle;
if (mBigContentTitle != null && mBigContentTitle.equals("")) {
contentView.setViewVisibility(R.id.line1, View.GONE);
} else {
@@ -2968,7 +3318,47 @@ public class Notification implements Parcelable
/**
* @hide
*/
public abstract Notification buildStyled(Notification wip);
protected void restoreFromExtras(Bundle extras) {
if (extras.containsKey(EXTRA_SUMMARY_TEXT)) {
mSummaryText = extras.getCharSequence(EXTRA_SUMMARY_TEXT);
mSummaryTextSet = true;
}
if (extras.containsKey(EXTRA_TITLE_BIG)) {
mBigContentTitle = extras.getCharSequence(EXTRA_TITLE_BIG);
}
}
/**
* @hide
*/
public Notification buildStyled(Notification wip) {
populateTickerView(wip);
populateContentView(wip);
populateBigContentView(wip);
populateHeadsUpContentView(wip);
return wip;
}
// The following methods are split out so we can re-create notification partially.
/**
* @hide
*/
protected void populateTickerView(Notification wip) {}
/**
* @hide
*/
protected void populateContentView(Notification wip) {}
/**
* @hide
*/
protected void populateBigContentView(Notification wip) {}
/**
* @hide
*/
protected void populateHeadsUpContentView(Notification wip) {}
/**
* Calls {@link android.app.Notification.Builder#build()} on the Builder this Style is
@@ -3069,12 +3459,21 @@ public class Notification implements Parcelable
* @hide
*/
@Override
public Notification buildStyled(Notification wip) {
if (mBigLargeIconSet ) {
mBuilder.mLargeIcon = mBigLargeIcon;
protected void restoreFromExtras(Bundle extras) {
super.restoreFromExtras(extras);
if (extras.containsKey(EXTRA_LARGE_ICON_BIG)) {
mBigLargeIcon = extras.getParcelable(EXTRA_LARGE_ICON_BIG);
}
wip.bigContentView = makeBigContentView();
return wip;
mPicture = extras.getParcelable(EXTRA_PICTURE);
}
/**
* @hide
*/
@Override
public void populateBigContentView(Notification wip) {
mBuilder.setBuilderBigContentView(wip, makeBigContentView());
}
}
@@ -3137,16 +3536,31 @@ public class Notification implements Parcelable
public void addExtras(Bundle extras) {
super.addExtras(extras);
extras.putCharSequence(EXTRA_TEXT, mBigText);
extras.putCharSequence(EXTRA_BIG_TEXT, mBigText);
}
/**
* @hide
*/
@Override
protected void restoreFromExtras(Bundle extras) {
super.restoreFromExtras(extras);
mBigText = extras.getCharSequence(EXTRA_BIG_TEXT);
}
private RemoteViews makeBigContentView() {
// Remove the content text so line3 only shows if you have a summary
final boolean hadThreeLines = (mBuilder.mContentText != null && mBuilder.mSubText != null);
// Nasty
CharSequence oldBuilderContentText = mBuilder.mContentText;
mBuilder.mContentText = null;
RemoteViews contentView = getStandardView(mBuilder.getBigTextLayoutResource());
mBuilder.mContentText = oldBuilderContentText;
if (hadThreeLines) {
// vertical centering
contentView.setViewPadding(R.id.line1, 0, 0, 0, 0);
@@ -3163,12 +3577,8 @@ public class Notification implements Parcelable
* @hide
*/
@Override
public Notification buildStyled(Notification wip) {
wip.bigContentView = makeBigContentView();
wip.extras.putCharSequence(EXTRA_TEXT, mBigText);
return wip;
public void populateBigContentView(Notification wip) {
mBuilder.setBuilderBigContentView(wip, makeBigContentView());
}
}
@@ -3232,15 +3642,35 @@ public class Notification implements Parcelable
*/
public void addExtras(Bundle extras) {
super.addExtras(extras);
CharSequence[] a = new CharSequence[mTexts.size()];
extras.putCharSequenceArray(EXTRA_TEXT_LINES, mTexts.toArray(a));
}
/**
* @hide
*/
@Override
protected void restoreFromExtras(Bundle extras) {
super.restoreFromExtras(extras);
mTexts.clear();
if (extras.containsKey(EXTRA_TEXT_LINES)) {
Collections.addAll(mTexts, extras.getCharSequenceArray(EXTRA_TEXT_LINES));
}
}
private RemoteViews makeBigContentView() {
// Remove the content text so line3 disappears unless you have a summary
// Nasty
CharSequence oldBuilderContentText = mBuilder.mContentText;
mBuilder.mContentText = null;
RemoteViews contentView = getStandardView(mBuilder.getInboxLayoutResource());
mBuilder.mContentText = oldBuilderContentText;
contentView.setViewVisibility(R.id.text2, View.GONE);
int[] rowIds = {R.id.inbox_text0, R.id.inbox_text1, R.id.inbox_text2, R.id.inbox_text3,
@@ -3275,10 +3705,8 @@ public class Notification implements Parcelable
* @hide
*/
@Override
public Notification buildStyled(Notification wip) {
wip.bigContentView = makeBigContentView();
return wip;
public void populateBigContentView(Notification wip) {
mBuilder.setBuilderBigContentView(wip, makeBigContentView());
}
}
@@ -3353,16 +3781,34 @@ public class Notification implements Parcelable
return this;
}
/**
* @hide
*/
@Override
public Notification buildStyled(Notification wip) {
wip.contentView = makeMediaContentView();
wip.bigContentView = makeMediaBigContentView();
super.buildStyled(wip);
if (wip.category == null) {
wip.category = Notification.CATEGORY_TRANSPORT;
}
return wip;
}
/**
* @hide
*/
@Override
public void populateContentView(Notification wip) {
mBuilder.setBuilderContentView(wip, makeMediaContentView());
}
/**
* @hide
*/
@Override
public void populateBigContentView(Notification wip) {
mBuilder.setBuilderBigContentView(wip, makeMediaBigContentView());
}
/** @hide */
@Override
public void addExtras(Bundle extras) {
@@ -3376,6 +3822,21 @@ public class Notification implements Parcelable
}
}
/**
* @hide
*/
@Override
protected void restoreFromExtras(Bundle extras) {
super.restoreFromExtras(extras);
if (extras.containsKey(EXTRA_MEDIA_SESSION)) {
mToken = extras.getParcelable(EXTRA_MEDIA_SESSION);
}
if (extras.containsKey(EXTRA_COMPACT_ACTIONS)) {
mActionsToShowInCompact = extras.getIntArray(EXTRA_COMPACT_ACTIONS);
}
}
private RemoteViews generateMediaActionButton(Action action) {
final boolean tombstone = (action.actionIntent == null);
RemoteViews button = new RemoteViews(mBuilder.mContext.getPackageName(),
@@ -3429,6 +3890,9 @@ public class Notification implements Parcelable
}
}
// When adding a new Style subclass here, don't forget to update
// Builder.getNotificationStyleClass.
/**
* Extender interface for use with {@link Builder#extend}. Extenders may be used to add
* metadata or change options on a notification builder.
@@ -4096,4 +4560,24 @@ public class Notification implements Parcelable
bundle.putParcelableArray(key, typedArray);
return typedArray;
}
private static class BuilderRemoteViews extends RemoteViews {
public BuilderRemoteViews(Parcel parcel) {
super(parcel);
}
public BuilderRemoteViews(String packageName, int layoutId) {
super(packageName, layoutId);
}
@Override
public BuilderRemoteViews clone() {
Parcel p = Parcel.obtain();
writeToParcel(p, 0);
p.setDataPosition(0);
BuilderRemoteViews brv = new BuilderRemoteViews(p);
p.recycle();
return brv;
}
}
}

View File

@@ -16,6 +16,7 @@
package android.app;
import android.app.Notification.Builder;
import android.content.Context;
import android.os.Handler;
import android.os.IBinder;
@@ -132,9 +133,11 @@ public class NotificationManager
}
}
if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")");
Notification stripped = notification.clone();
Builder.stripForDelivery(stripped);
try {
service.enqueueNotificationWithTag(pkg, mContext.getOpPackageName(), tag, id,
notification, idOut, UserHandle.myUserId());
stripped, idOut, UserHandle.myUserId());
if (id != idOut[0]) {
Log.w(TAG, "notify: id corrupted: sent " + id + ", got back " + idOut[0]);
}
@@ -157,9 +160,11 @@ public class NotificationManager
}
}
if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")");
Notification stripped = notification.clone();
Builder.stripForDelivery(stripped);
try {
service.enqueueNotificationWithTag(pkg, mContext.getOpPackageName(), tag, id,
notification, idOut, user.getIdentifier());
stripped, idOut, user.getIdentifier());
if (id != idOut[0]) {
Log.w(TAG, "notify: id corrupted: sent " + id + ", got back " + idOut[0]);
}

View File

@@ -19,6 +19,8 @@ package android.service.notification;
import android.annotation.SystemApi;
import android.annotation.SdkConstant;
import android.app.INotificationManager;
import android.app.Notification;
import android.app.Notification.Builder;
import android.app.Service;
import android.content.ComponentName;
import android.content.Context;
@@ -70,6 +72,11 @@ public abstract class NotificationListenerService extends Service {
/** Only valid after a successful call to (@link registerAsService}. */
private int mCurrentUser;
// This context is required for system services since NotificationListenerService isn't
// started as a real Service and hence no context is available.
private Context mSystemContext;
/**
* The {@link Intent} that must be declared as handled by the service.
*/
@@ -290,7 +297,13 @@ public abstract class NotificationListenerService extends Service {
ParceledListSlice<StatusBarNotification> parceledList =
getNotificationInterface().getActiveNotificationsFromListener(mWrapper);
List<StatusBarNotification> list = parceledList.getList();
return list.toArray(new StatusBarNotification[list.size()]);
int N = list.size();
for (int i = 0; i < N; i++) {
Notification notification = list.get(i).getNotification();
Builder.rebuild(getContext(), notification);
}
return list.toArray(new StatusBarNotification[N]);
} catch (android.os.RemoteException ex) {
Log.v(TAG, "Unable to contact notification manager", ex);
}
@@ -379,13 +392,16 @@ public abstract class NotificationListenerService extends Service {
* <p>Only system services may use this call. It will fail for non-system callers.
* Apps should ask the user to add their listener in Settings.
*
* @param context Context required for accessing resources. Since this service isn't
* launched as a real Service when using this method, a context has to be passed in.
* @param componentName the component that will consume the notification information
* @param currentUser the user to use as the stream filter
* @hide
*/
@SystemApi
public void registerAsSystemService(ComponentName componentName, int currentUser)
throws RemoteException {
public void registerAsSystemService(Context context, ComponentName componentName,
int currentUser) throws RemoteException {
mSystemContext = context;
if (mWrapper == null) {
mWrapper = new INotificationListenerWrapper();
}
@@ -413,6 +429,8 @@ public abstract class NotificationListenerService extends Service {
@Override
public void onNotificationPosted(StatusBarNotification sbn,
NotificationRankingUpdate update) {
Notification.Builder.rebuild(getContext(), sbn.getNotification());
// protect subclass from concurrent modifications of (@link mNotificationKeys}.
synchronized (mWrapper) {
applyUpdate(update);
@@ -475,6 +493,13 @@ public abstract class NotificationListenerService extends Service {
mRankingMap = new RankingMap(update);
}
private Context getContext() {
if (mSystemContext != null) {
return mSystemContext;
}
return this;
}
/**
* Stores ranking related information on a currently active notification.
*

View File

@@ -2575,6 +2575,15 @@ public class RemoteViews implements Parcelable, Filter {
return c;
}
/**
* Returns the number of actions in this RemoteViews. Can be used as a sequence number.
*
* @hide
*/
public int getSequenceNumber() {
return (mActions == null) ? 0 : mActions.size();
}
/* (non-Javadoc)
* Used to restrict the views which can be inflated
*

View File

@@ -462,7 +462,7 @@ public abstract class BaseStatusBar extends SystemUI implements
// Set up the initial notification state.
try {
mNotificationListener.registerAsSystemService(
mNotificationListener.registerAsSystemService(mContext,
new ComponentName(mContext.getPackageName(), getClass().getCanonicalName()),
UserHandle.USER_ALL);
} catch (RemoteException e) {

View File

@@ -29,6 +29,7 @@ import android.app.IActivityManager;
import android.app.INotificationManager;
import android.app.ITransientNotification;
import android.app.Notification;
import android.app.Notification.Builder;
import android.app.PendingIntent;
import android.app.StatusBarManager;
import android.content.BroadcastReceiver;
@@ -1554,8 +1555,8 @@ public class NotificationManagerService extends SystemService {
+ " id=" + id + " notification=" + notification);
}
if (notification.icon != 0) {
if (notification.contentView == null) {
throw new IllegalArgumentException("contentView required: pkg=" + pkg
if (!notification.isValid()) {
throw new IllegalArgumentException("Invalid notification (): pkg=" + pkg
+ " id=" + id + " notification=" + notification);
}
}