diff options
author | 2014-07-30 19:33:57 +0000 | |
---|---|---|
committer | 2014-07-24 15:35:58 +0000 | |
commit | 4579fd2d0c23f92546633e472439f9207acb3ae0 (patch) | |
tree | 3b92d67937fcff50f3a7552fa8886c415d6cab05 | |
parent | 078646c57b2b90d5cf1dfa2f45183e39a814bce8 (diff) | |
parent | 4600f9b60753adab4e65258a05744a46938fce86 (diff) |
Merge "Strip RemoteViews from Notifications, re-create them in SysUI" into lmp-dev
7 files changed, 559 insertions, 35 deletions
diff --git a/api/current.txt b/api/current.txt index e43fae4d5847..cee64a384e75 100644 --- a/api/current.txt +++ b/api/current.txt @@ -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...); } diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 983e347f50d8..518a6c281a66 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -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; @@ -691,6 +693,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)}. */ @@ -1714,6 +1723,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. * * Provides a convenient way to set the various fields of a {@link Notification} and generate @@ -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: * @@ -1823,6 +1901,40 @@ public class Notification implements Parcelable } /** + * 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 * {@link #setShowWhen(boolean) setShowWhen} to control this. @@ -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; + } + } } diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index 3ee430639ab1..bd9363d27164 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -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]); } diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java index 7c8624c5ffbe..1f01bc843b29 100644 --- a/core/java/android/service/notification/NotificationListenerService.java +++ b/core/java/android/service/notification/NotificationListenerService.java @@ -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. * diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index 82637a19ff10..5c7a43b541a2 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -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 * diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java index 87b1f8e21960..f8cdd407aac4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java @@ -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) { diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index f9230a3ad087..a277f91cd453 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -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); } } |