From 68079d54f3690885df9c5bbb691090633f3ef541 Mon Sep 17 00:00:00 2001 From: Dan Sandler Date: Wed, 22 Jul 2015 10:45:30 -0400 Subject: [PATCH 1/2] Fix media notification action icons. In addition to cleaning up some bare references to the icon slot, we now apply updates to notification RemoteViews in the context of the supplying app's package. This ensures we can find the drawables inside any Icon objects that were constructed without a proper package name, such as is the case with Actions (because the builder and constructor are Context-free and so don't know the package name). This CL also makes clear what was previously only implied: Non-resource action icons are not actually supported yet since they can't be pushed to TextView compound drawables using today's RemoteViews APIs. That will require an API change. Bug: 22600607 Change-Id: Ie6b88aed36e4db05be35f843ea3bc1898d4a5c96 --- core/java/android/app/Notification.java | 18 ++++++++------ .../notification/StatusBarNotification.java | 24 +++++++++++++++++++ .../systemui/statusbar/BaseStatusBar.java | 24 +++++++++++++------ 3 files changed, 52 insertions(+), 14 deletions(-) diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index b0d85419947a0..0937af66a07ae 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -930,9 +930,9 @@ public class Notification implements Parcelable private Action(Parcel in) { if (in.readInt() != 0) { mIcon = Icon.CREATOR.createFromParcel(in); - } - if (mIcon.getType() == Icon.TYPE_RESOURCE) { - icon = mIcon.getResId(); + if (mIcon.getType() == Icon.TYPE_RESOURCE) { + icon = mIcon.getResId(); + } } title = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); if (in.readInt() == 1) { @@ -3171,7 +3171,12 @@ public class Notification implements Parcelable RemoteViews button = new BuilderRemoteViews(mContext.getApplicationInfo(), tombstone ? getActionTombstoneLayoutResource() : getActionLayoutResource()); - button.setTextViewCompoundDrawablesRelative(R.id.action0, action.icon, 0, 0, 0); + // TODO: support Bitmaps in action icons when TextView compound drawables support Icon + final Icon ai = action.getIcon(); + final int icon = (ai.getType() == Icon.TYPE_RESOURCE) + ? ai.getResId() + : 0; + button.setTextViewCompoundDrawablesRelative(R.id.action0, icon, 0, 0, 0); button.setTextViewText(R.id.action0, processLegacyText(action.title)); if (!tombstone) { button.setOnClickPendingIntent(R.id.action0, action.actionIntent); @@ -3190,7 +3195,7 @@ public class Notification implements Parcelable } private void processLegacyAction(Action action, RemoteViews button) { - if (!isLegacy() || mColorUtil.isGrayscaleIcon(mContext, action.icon)) { + if (!isLegacy() || mColorUtil.isGrayscaleIcon(mContext, action.getIcon())) { button.setTextViewCompoundDrawablesRelativeColorFilter(R.id.action0, 0, mContext.getColor(R.color.notification_action_color_filter), PorterDuff.Mode.MULTIPLY); @@ -3605,7 +3610,6 @@ public class Notification implements Parcelable mContentText = extras.getCharSequence(EXTRA_TEXT); mSubText = extras.getCharSequence(EXTRA_SUB_TEXT); mContentInfo = extras.getCharSequence(EXTRA_INFO_TEXT); - mSmallIcon = extras.getParcelable(EXTRA_SMALL_ICON); mProgress = extras.getInt(EXTRA_PROGRESS); mProgressMax = extras.getInt(EXTRA_PROGRESS_MAX); mProgressIndeterminate = extras.getBoolean(EXTRA_PROGRESS_INDETERMINATE); @@ -4439,7 +4443,7 @@ public class Notification implements Parcelable final boolean tombstone = (action.actionIntent == null); RemoteViews button = new BuilderRemoteViews(mBuilder.mContext.getApplicationInfo(), R.layout.notification_material_media_action); - button.setImageViewResource(R.id.action0, action.icon); + button.setImageViewIcon(R.id.action0, action.getIcon()); button.setDrawableParameters(R.id.action0, false, -1, 0xFFFFFFFF, PorterDuff.Mode.SRC_ATOP, -1); diff --git a/core/java/android/service/notification/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java index 0eda69238aa74..2cab914bed62b 100644 --- a/core/java/android/service/notification/StatusBarNotification.java +++ b/core/java/android/service/notification/StatusBarNotification.java @@ -17,6 +17,9 @@ package android.service.notification; import android.app.Notification; +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; import android.os.Parcel; import android.os.Parcelable; import android.os.UserHandle; @@ -40,6 +43,7 @@ public class StatusBarNotification implements Parcelable { private final long postTime; private final int score; + private Context mContext; // used for inflation & icon expansion /** @hide */ public StatusBarNotification(String pkg, String opPkg, int id, String tag, int uid, @@ -261,4 +265,24 @@ public class StatusBarNotification implements Parcelable { public String getGroupKey() { return groupKey; } + + /** + * @hide + */ + public Context getPackageContext(Context context) { + if (mContext == null) { + try { + ApplicationInfo ai = context.getPackageManager() + .getApplicationInfo(pkg, PackageManager.GET_UNINSTALLED_PACKAGES); + mContext = context.createApplicationContext(ai, + Context.CONTEXT_RESTRICTED); + } catch (PackageManager.NameNotFoundException e) { + mContext = null; + } + } + if (mContext == null) { + mContext = context; + } + return mContext; + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java index a2e6632f2379a..d5da179b3c714 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java @@ -1328,14 +1328,20 @@ public abstract class BaseStatusBar extends SystemUI implements View bigContentViewLocal = null; View headsUpContentViewLocal = null; try { - contentViewLocal = contentView.apply(mContext, contentContainer, + contentViewLocal = contentView.apply( + sbn.getPackageContext(mContext), + contentContainer, mOnClickHandler); if (bigContentView != null) { - bigContentViewLocal = bigContentView.apply(mContext, contentContainer, + bigContentViewLocal = bigContentView.apply( + sbn.getPackageContext(mContext), + contentContainer, mOnClickHandler); } if (headsUpContentView != null) { - headsUpContentViewLocal = headsUpContentView.apply(mContext, contentContainer, + headsUpContentViewLocal = headsUpContentView.apply( + sbn.getPackageContext(mContext), + contentContainer, mOnClickHandler); } } @@ -1362,7 +1368,8 @@ public abstract class BaseStatusBar extends SystemUI implements View publicViewLocal = null; if (publicNotification != null) { try { - publicViewLocal = publicNotification.contentView.apply(mContext, + publicViewLocal = publicNotification.contentView.apply( + sbn.getPackageContext(mContext), contentContainerPublic, mOnClickHandler); if (publicViewLocal != null) { @@ -1981,15 +1988,18 @@ public abstract class BaseStatusBar extends SystemUI implements // Reapply the RemoteViews contentView.reapply(mContext, entry.getContentView(), mOnClickHandler); if (bigContentView != null && entry.getExpandedContentView() != null) { - bigContentView.reapply(mContext, entry.getExpandedContentView(), + bigContentView.reapply(notification.getPackageContext(mContext), + entry.getExpandedContentView(), mOnClickHandler); } View headsUpChild = entry.getHeadsUpContentView(); if (headsUpContentView != null && headsUpChild != null) { - headsUpContentView.reapply(mContext, headsUpChild, mOnClickHandler); + headsUpContentView.reapply(notification.getPackageContext(mContext), + headsUpChild, mOnClickHandler); } if (publicContentView != null && entry.getPublicContentView() != null) { - publicContentView.reapply(mContext, entry.getPublicContentView(), mOnClickHandler); + publicContentView.reapply(notification.getPackageContext(mContext), + entry.getPublicContentView(), mOnClickHandler); } // update the contentIntent mNotificationClicker.register(entry.row, notification); From 912282e7319370cb74727895a49c0598fb0a7b02 Mon Sep 17 00:00:00 2001 From: Dan Sandler Date: Tue, 28 Jul 2015 22:49:30 -0400 Subject: [PATCH 2/2] Fully support Icons in Notification actions. RemoteViews now allows Icons as TextView compound drawables in RemoteViews, but not yet as public API. Bug: 22600607 Change-Id: I986a0ce3bede09746f0b121884184679f39a79f5 --- core/java/android/app/Notification.java | 6 +- core/java/android/widget/RemoteViews.java | 128 ++++++++++++++++++++-- core/java/android/widget/TextView.java | 3 + 3 files changed, 121 insertions(+), 16 deletions(-) diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 0937af66a07ae..16fa08ccfd2ed 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -3171,12 +3171,8 @@ public class Notification implements Parcelable RemoteViews button = new BuilderRemoteViews(mContext.getApplicationInfo(), tombstone ? getActionTombstoneLayoutResource() : getActionLayoutResource()); - // TODO: support Bitmaps in action icons when TextView compound drawables support Icon final Icon ai = action.getIcon(); - final int icon = (ai.getType() == Icon.TYPE_RESOURCE) - ? ai.getResId() - : 0; - button.setTextViewCompoundDrawablesRelative(R.id.action0, icon, 0, 0, 0); + button.setTextViewCompoundDrawablesRelative(R.id.action0, ai, null, null, null); button.setTextViewText(R.id.action0, processLegacyText(action.title)); if (!tombstone) { button.setOnClickPendingIntent(R.id.action0, action.actionIntent); diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index 74843ee0f06b6..d1583136c4088 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -1412,39 +1412,108 @@ public class RemoteViews implements Parcelable, Filter { public TextViewDrawableAction(int viewId, boolean isRelative, int d1, int d2, int d3, int d4) { this.viewId = viewId; this.isRelative = isRelative; + this.useIcons = false; this.d1 = d1; this.d2 = d2; this.d3 = d3; this.d4 = d4; } + public TextViewDrawableAction(int viewId, boolean isRelative, + Icon i1, Icon i2, Icon i3, Icon i4) { + this.viewId = viewId; + this.isRelative = isRelative; + this.useIcons = true; + this.i1 = i1; + this.i2 = i2; + this.i3 = i3; + this.i4 = i4; + } + public TextViewDrawableAction(Parcel parcel) { viewId = parcel.readInt(); isRelative = (parcel.readInt() != 0); - d1 = parcel.readInt(); - d2 = parcel.readInt(); - d3 = parcel.readInt(); - d4 = parcel.readInt(); + useIcons = (parcel.readInt() != 0); + if (useIcons) { + if (parcel.readInt() != 0) { + i1 = Icon.CREATOR.createFromParcel(parcel); + } + if (parcel.readInt() != 0) { + i2 = Icon.CREATOR.createFromParcel(parcel); + } + if (parcel.readInt() != 0) { + i3 = Icon.CREATOR.createFromParcel(parcel); + } + if (parcel.readInt() != 0) { + i4 = Icon.CREATOR.createFromParcel(parcel); + } + } else { + d1 = parcel.readInt(); + d2 = parcel.readInt(); + d3 = parcel.readInt(); + d4 = parcel.readInt(); + } } public void writeToParcel(Parcel dest, int flags) { dest.writeInt(TAG); dest.writeInt(viewId); dest.writeInt(isRelative ? 1 : 0); - dest.writeInt(d1); - dest.writeInt(d2); - dest.writeInt(d3); - dest.writeInt(d4); + dest.writeInt(useIcons ? 1 : 0); + if (useIcons) { + if (i1 != null) { + dest.writeInt(1); + i1.writeToParcel(dest, 0); + } else { + dest.writeInt(0); + } + if (i2 != null) { + dest.writeInt(1); + i2.writeToParcel(dest, 0); + } else { + dest.writeInt(0); + } + if (i3 != null) { + dest.writeInt(1); + i3.writeToParcel(dest, 0); + } else { + dest.writeInt(0); + } + if (i4 != null) { + dest.writeInt(1); + i4.writeToParcel(dest, 0); + } else { + dest.writeInt(0); + } + } else { + dest.writeInt(d1); + dest.writeInt(d2); + dest.writeInt(d3); + dest.writeInt(d4); + } } @Override public void apply(View root, ViewGroup rootParent, OnClickHandler handler) { final TextView target = (TextView) root.findViewById(viewId); if (target == null) return; - if (isRelative) { - target.setCompoundDrawablesRelativeWithIntrinsicBounds(d1, d2, d3, d4); + if (useIcons) { + final Context ctx = target.getContext(); + final Drawable id1 = i1 == null ? null : i1.loadDrawable(ctx); + final Drawable id2 = i2 == null ? null : i2.loadDrawable(ctx); + final Drawable id3 = i3 == null ? null : i3.loadDrawable(ctx); + final Drawable id4 = i4 == null ? null : i4.loadDrawable(ctx); + if (isRelative) { + target.setCompoundDrawablesRelativeWithIntrinsicBounds(id1, id2, id3, id4); + } else { + target.setCompoundDrawablesWithIntrinsicBounds(id1, id2, id3, id4); + } } else { - target.setCompoundDrawablesWithIntrinsicBounds(d1, d2, d3, d4); + if (isRelative) { + target.setCompoundDrawablesRelativeWithIntrinsicBounds(d1, d2, d3, d4); + } else { + target.setCompoundDrawablesWithIntrinsicBounds(d1, d2, d3, d4); + } } } @@ -1453,7 +1522,9 @@ public class RemoteViews implements Parcelable, Filter { } boolean isRelative = false; + boolean useIcons = false; int d1, d2, d3, d4; + Icon i1, i2, i3, i4; public final static int TAG = 11; } @@ -2066,6 +2137,41 @@ public class RemoteViews implements Parcelable, Filter { addAction(new TextViewDrawableColorFilterAction(viewId, true, index, color, mode)); } + /** + * Equivalent to calling {@link + * TextView#setCompoundDrawablesWithIntrinsicBounds(Drawable, Drawable, Drawable, Drawable)} + * using the drawables yielded by {@link Icon#loadDrawable(Context)}. + * + * @param viewId The id of the view whose text should change + * @param left an Icon to place to the left of the text, or 0 + * @param top an Icon to place above the text, or 0 + * @param right an Icon to place to the right of the text, or 0 + * @param bottom an Icon to place below the text, or 0 + * + * @hide + */ + public void setTextViewCompoundDrawables(int viewId, Icon left, Icon top, Icon right, Icon bottom) { + addAction(new TextViewDrawableAction(viewId, false, left, top, right, bottom)); + } + + /** + * Equivalent to calling {@link + * TextView#setCompoundDrawablesRelativeWithIntrinsicBounds(Drawable, Drawable, Drawable, Drawable)} + * using the drawables yielded by {@link Icon#loadDrawable(Context)}. + * + * @param viewId The id of the view whose text should change + * @param start an Icon to place before the text (relative to the + * layout direction), or 0 + * @param top an Icon to place above the text, or 0 + * @param end an Icon to place after the text, or 0 + * @param bottom an Icon to place below the text, or 0 + * + * @hide + */ + public void setTextViewCompoundDrawablesRelative(int viewId, Icon start, Icon top, Icon end, Icon bottom) { + addAction(new TextViewDrawableAction(viewId, true, start, top, end, bottom)); + } + /** * Equivalent to calling ImageView.setImageResource * diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 131ba4674755f..a1462c4886a0b 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -2270,6 +2270,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * @attr ref android.R.styleable#TextView_drawableRight * @attr ref android.R.styleable#TextView_drawableBottom */ + @android.view.RemotableViewMethod public void setCompoundDrawablesWithIntrinsicBounds(@Nullable Drawable left, @Nullable Drawable top, @Nullable Drawable right, @Nullable Drawable bottom) { @@ -2302,6 +2303,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * @attr ref android.R.styleable#TextView_drawableEnd * @attr ref android.R.styleable#TextView_drawableBottom */ + @android.view.RemotableViewMethod public void setCompoundDrawablesRelative(@Nullable Drawable start, @Nullable Drawable top, @Nullable Drawable end, @Nullable Drawable bottom) { Drawables dr = mDrawables; @@ -2472,6 +2474,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * @attr ref android.R.styleable#TextView_drawableEnd * @attr ref android.R.styleable#TextView_drawableBottom */ + @android.view.RemotableViewMethod public void setCompoundDrawablesRelativeWithIntrinsicBounds(@Nullable Drawable start, @Nullable Drawable top, @Nullable Drawable end, @Nullable Drawable bottom) {