diff options
| author | 2018-11-12 16:29:18 -0800 | |
|---|---|---|
| committer | 2018-12-04 02:33:53 -0800 | |
| commit | c12d31c3b5080f203c374bd82a7702b226a9e276 (patch) | |
| tree | f21ce57ffaa2b148843ac5450ad98b8bc834afea | |
| parent | 0d7a9a2789a19426f27faa88a3c1e35a6b0d25de (diff) | |
Adding API to specify a dark text specific layout in app-widgets
Bug: 109954539
Test: atest CtsAppWidgetTestCases
Change-Id: I785931469888a09685c45949afcf2e3633233c60
| -rw-r--r-- | api/current.txt | 2 | ||||
| -rw-r--r-- | core/java/android/app/Notification.java | 4 | ||||
| -rw-r--r-- | core/java/android/appwidget/AppWidgetHostView.java | 14 | ||||
| -rw-r--r-- | core/java/android/widget/RemoteViews.java | 144 | ||||
| -rw-r--r-- | core/java/android/widget/RemoteViewsAdapter.java | 13 | ||||
| -rw-r--r-- | core/java/android/widget/RemoteViewsListAdapter.java | 2 | ||||
| -rw-r--r-- | core/java/android/widget/RemoteViewsService.java | 2 | ||||
| -rw-r--r-- | packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java | 2 |
8 files changed, 127 insertions, 56 deletions
diff --git a/api/current.txt b/api/current.txt index d86b8b11a60e..57d8f5ccb921 100644 --- a/api/current.txt +++ b/api/current.txt @@ -7690,6 +7690,7 @@ package android.appwidget { method protected void prepareView(android.view.View); method public void setAppWidget(int, android.appwidget.AppWidgetProviderInfo); method public void setExecutor(java.util.concurrent.Executor); + method public void setOnLightBackground(boolean); method public void updateAppWidget(android.widget.RemoteViews); method public void updateAppWidgetOptions(android.os.Bundle); method public void updateAppWidgetSize(android.os.Bundle, int, int, int, int); @@ -54939,6 +54940,7 @@ package android.widget { method public void setInt(int, java.lang.String, int); method public void setIntent(int, java.lang.String, android.content.Intent); method public void setLabelFor(int, int); + method public void setLightBackgroundLayoutId(int); method public void setLong(int, java.lang.String, long); method public void setOnClickFillInIntent(int, android.content.Intent); method public void setOnClickPendingIntent(int, android.app.PendingIntent); diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 5002a8125d44..09df11570b76 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -8232,7 +8232,7 @@ public class Notification implements Parcelable customContent = customContent.clone(); remoteViews.removeAllViewsExceptId(R.id.notification_main_column, R.id.progress); remoteViews.addView(R.id.notification_main_column, customContent, 0 /* index */); - remoteViews.setReapplyDisallowed(); + remoteViews.addFlags(RemoteViews.FLAG_REAPPLY_DISALLOWED); } // also update the end margin if there is an image Resources resources = mBuilder.mContext.getResources(); @@ -8363,7 +8363,7 @@ public class Notification implements Parcelable customContent.overrideTextColors(mBuilder.getPrimaryTextColor(mBuilder.mParams)); remoteViews.removeAllViews(id); remoteViews.addView(id, customContent); - remoteViews.setReapplyDisallowed(); + remoteViews.addFlags(RemoteViews.FLAG_REAPPLY_DISALLOWED); } return remoteViews; } diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java index 318dbee99e5a..c740c42c9c0d 100644 --- a/core/java/android/appwidget/AppWidgetHostView.java +++ b/core/java/android/appwidget/AppWidgetHostView.java @@ -88,6 +88,7 @@ public class AppWidgetHostView extends FrameLayout { int mViewMode = VIEW_MODE_NOINIT; int mLayoutId = -1; private OnClickHandler mOnClickHandler; + private boolean mOnLightBackground; private Executor mAsyncExecutor; private CancellationSignal mLastExecutionSignal; @@ -374,6 +375,15 @@ public class AppWidgetHostView extends FrameLayout { } /** + * Sets whether the widget should is being displayed on a light/white background and use an + * alternate UI if available. + * @see RemoteViews#setLightBackgroundLayoutId(int) + */ + public void setOnLightBackground(boolean useDarkTextLayout) { + mOnLightBackground = useDarkTextLayout; + } + + /** * Update the AppWidgetProviderInfo for this view, and reset it to the * initial layout. */ @@ -413,6 +423,10 @@ public class AppWidgetHostView extends FrameLayout { mLayoutId = -1; mViewMode = VIEW_MODE_DEFAULT; } else { + if (mOnLightBackground) { + remoteViews = remoteViews.getDarkTextViews(); + } + if (mAsyncExecutor != null && useAsyncIfPossible) { inflateAsync(remoteViews); return; diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index 7b39efed0c3a..3b916d16b2b4 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -18,6 +18,8 @@ package android.widget; import android.annotation.ColorInt; import android.annotation.DimenRes; +import android.annotation.IntDef; +import android.annotation.LayoutRes; import android.annotation.NonNull; import android.annotation.StyleRes; import android.annotation.UnsupportedAppUsage; @@ -131,6 +133,12 @@ public class RemoteViews implements Parcelable, Filter { static final String EXTRA_REMOTEADAPTER_APPWIDGET_ID = "remoteAdapterAppWidgetId"; /** + * The intent extra that contains {@code true} if inflating as dak text theme. + * @hide + */ + static final String EXTRA_REMOTEADAPTER_ON_LIGHT_BACKGROUND = "remoteAdapterOnLightBackground"; + + /** * The intent extra that contains the bounds for all shared elements. */ public static final String EXTRA_SHARED_ELEMENT_BOUNDS = @@ -163,6 +171,36 @@ public class RemoteViews implements Parcelable, Filter { private static final int SET_RIPPLE_DRAWABLE_COLOR_TAG = 21; private static final int SET_INT_TAG_TAG = 22; + /** @hide **/ + @IntDef(flag = true, value = { + FLAG_REAPPLY_DISALLOWED, + FLAG_WIDGET_IS_COLLECTION_CHILD, + FLAG_USE_LIGHT_BACKGROUND_LAYOUT + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ApplyFlags {} + /** + * Whether reapply is disallowed on this remoteview. This maybe be true if some actions modify + * the layout in a way that isn't recoverable, since views are being removed. + * @hide + */ + public static final int FLAG_REAPPLY_DISALLOWED = 1; + /** + * This flag indicates whether this RemoteViews object is being created from a + * RemoteViewsService for use as a child of a widget collection. This flag is used + * to determine whether or not certain features are available, in particular, + * setting on click extras and setting on click pending intents. The former is enabled, + * and the latter disabled when this flag is true. + * @hide + */ + public static final int FLAG_WIDGET_IS_COLLECTION_CHILD = 2; + /** + * When this flag is set, the views is inflated with {@link #mLightBackgroundLayoutId} instead + * of {link #mLayoutId} + * @hide + */ + public static final int FLAG_USE_LIGHT_BACKGROUND_LAYOUT = 4; + /** * Application that hosts the remote views. * @@ -178,6 +216,11 @@ public class RemoteViews implements Parcelable, Filter { private final int mLayoutId; /** + * The resource ID of the layout file in dark text mode. (Added to the parcel) + */ + private int mLightBackgroundLayoutId = 0; + + /** * An array of actions to perform on the view tree once it has been * inflated */ @@ -197,12 +240,6 @@ public class RemoteViews implements Parcelable, Filter { private boolean mIsRoot = true; /** - * Whether reapply is disallowed on this remoteview. This maybe be true if some actions modify - * the layout in a way that isn't recoverable, since views are being removed. - */ - private boolean mReapplyDisallowed; - - /** * Constants to whether or not this RemoteViews is composed of a landscape and portrait * RemoteViews. */ @@ -218,14 +255,8 @@ public class RemoteViews implements Parcelable, Filter { @UnsupportedAppUsage private RemoteViews mPortrait = null; - /** - * This flag indicates whether this RemoteViews object is being created from a - * RemoteViewsService for use as a child of a widget collection. This flag is used - * to determine whether or not certain features are available, in particular, - * setting on click extras and setting on click pending intents. The former is enabled, - * and the latter disabled when this flag is true. - */ - private boolean mIsWidgetCollectionChild = false; + @ApplyFlags + private int mApplyFlags = 0; /** Class cookies of the Parcel this instance was read from. */ private final Map<Class, Object> mClassCookies; @@ -289,18 +320,15 @@ public class RemoteViews implements Parcelable, Filter { * * @hide */ - public void setReapplyDisallowed() { - mReapplyDisallowed = true; + public void addFlags(@ApplyFlags int flags) { + mApplyFlags = mApplyFlags | flags; } /** - * @return Whether it is disallowed to reapply another remoteview with the same layout as this - * view. True if this remoteview has actions that destroyed view tree of the base layout. - * * @hide */ - public boolean isReapplyDisallowed() { - return mReapplyDisallowed; + public boolean hasFlags(@ApplyFlags int flag) { + return (mApplyFlags & flag) == flag; } /** @@ -768,7 +796,10 @@ public class RemoteViews implements Parcelable, Filter { // Embed the AppWidget Id for use in RemoteViewsAdapter when connecting to the intent // RemoteViewsService AppWidgetHostView host = (AppWidgetHostView) rootParent; - intent.putExtra(EXTRA_REMOTEADAPTER_APPWIDGET_ID, host.getAppWidgetId()); + intent.putExtra(EXTRA_REMOTEADAPTER_APPWIDGET_ID, host.getAppWidgetId()) + .putExtra(EXTRA_REMOTEADAPTER_ON_LIGHT_BACKGROUND, + hasFlags(FLAG_USE_LIGHT_BACKGROUND_LAYOUT)); + if (target instanceof AbsListView) { AbsListView v = (AbsListView) target; v.setRemoteViewsAdapter(intent, isAsync); @@ -829,7 +860,7 @@ public class RemoteViews implements Parcelable, Filter { // If the view is an AdapterView, setting a PendingIntent on click doesn't make // much sense, do they mean to set a PendingIntent template for the // AdapterView's children? - if (mIsWidgetCollectionChild) { + if (hasFlags(FLAG_WIDGET_IS_COLLECTION_CHILD)) { Log.w(LOG_TAG, "Cannot SetOnClickResponse for collection item " + "(id: " + viewId + ")"); ApplicationInfo appInfo = root.getContext().getApplicationInfo(); @@ -843,7 +874,7 @@ public class RemoteViews implements Parcelable, Filter { } target.setTagInternal(R.id.pending_intent_tag, mResponse.mPendingIntent); } else if (mResponse.mFillIntent != null) { - if (!mIsWidgetCollectionChild) { + if (!hasFlags(FLAG_WIDGET_IS_COLLECTION_CHILD)) { Log.e(LOG_TAG, "The method setOnClickFillInIntent is available " + "only from RemoteViewsFactory (ie. on collection items)."); return; @@ -1545,6 +1576,7 @@ public class RemoteViews implements Parcelable, Filter { viewId = parcel.readInt(); mIndex = parcel.readInt(); mNestedViews = new RemoteViews(parcel, bitmapCache, info, depth, classCookies); + mNestedViews.addFlags(mApplyFlags); } public void writeToParcel(Parcel dest, int flags) { @@ -2190,7 +2222,7 @@ public class RemoteViews implements Parcelable, Filter { * * @hide */ - public RemoteViews(String packageName, int userId, int layoutId) { + public RemoteViews(String packageName, int userId, @LayoutRes int layoutId) { this(getApplicationInfo(packageName, userId), layoutId); } @@ -2203,7 +2235,7 @@ public class RemoteViews implements Parcelable, Filter { * * @hide */ - protected RemoteViews(ApplicationInfo application, int layoutId) { + protected RemoteViews(ApplicationInfo application, @LayoutRes int layoutId) { mApplication = application; mLayoutId = layoutId; mBitmapCache = new BitmapCache(); @@ -2229,7 +2261,8 @@ public class RemoteViews implements Parcelable, Filter { throw new RuntimeException("Both RemoteViews must share the same package and user"); } mApplication = portrait.mApplication; - mLayoutId = portrait.getLayoutId(); + mLayoutId = portrait.mLayoutId; + mLightBackgroundLayoutId = portrait.mLightBackgroundLayoutId; mLandscape = landscape; mPortrait = portrait; @@ -2250,8 +2283,8 @@ public class RemoteViews implements Parcelable, Filter { mApplication = src.mApplication; mIsRoot = src.mIsRoot; mLayoutId = src.mLayoutId; - mIsWidgetCollectionChild = src.mIsWidgetCollectionChild; - mReapplyDisallowed = src.mReapplyDisallowed; + mLightBackgroundLayoutId = src.mLightBackgroundLayoutId; + mApplyFlags = src.mApplyFlags; mClassCookies = src.mClassCookies; if (src.hasLandscapeAndPortraitLayouts()) { @@ -2309,7 +2342,7 @@ public class RemoteViews implements Parcelable, Filter { mApplication = parcel.readInt() == 0 ? info : ApplicationInfo.CREATOR.createFromParcel(parcel); mLayoutId = parcel.readInt(); - mIsWidgetCollectionChild = parcel.readInt() == 1; + mLightBackgroundLayoutId = parcel.readInt(); readActionsFromParcel(parcel, depth); } else { @@ -2318,9 +2351,10 @@ public class RemoteViews implements Parcelable, Filter { mPortrait = new RemoteViews(parcel, mBitmapCache, mLandscape.mApplication, depth, mClassCookies); mApplication = mPortrait.mApplication; - mLayoutId = mPortrait.getLayoutId(); + mLayoutId = mPortrait.mLayoutId; + mLightBackgroundLayoutId = mPortrait.mLightBackgroundLayoutId; } - mReapplyDisallowed = parcel.readInt() == 0; + mApplyFlags = parcel.readInt(); } private void readActionsFromParcel(Parcel parcel, int depth) { @@ -2409,19 +2443,8 @@ public class RemoteViews implements Parcelable, Filter { * @return the layout id. */ public int getLayoutId() { - return mLayoutId; - } - - /* - * This flag indicates whether this RemoteViews object is being created from a - * RemoteViewsService for use as a child of a widget collection. This flag is used - * to determine whether or not certain features are available, in particular, - * setting on click extras and setting on click pending intents. The former is enabled, - * and the latter disabled when this flag is true. - */ - @UnsupportedAppUsage - void setIsWidgetCollectionChild(boolean isWidgetCollectionChild) { - mIsWidgetCollectionChild = isWidgetCollectionChild; + return hasFlags(FLAG_USE_LIGHT_BACKGROUND_LAYOUT) && (mLightBackgroundLayoutId != 0) + ? mLightBackgroundLayoutId : mLayoutId; } /** @@ -3292,6 +3315,33 @@ public class RemoteViews implements Parcelable, Filter { setInt(viewId, "setLabelFor", labeledId); } + /** + * Provides an alternate layout ID, which can be used to inflate this view. This layout will be + * used by the host when the widgets displayed on a light-background where foreground elements + * and text can safely draw using a dark color without any additional background protection. + */ + public void setLightBackgroundLayoutId(@LayoutRes int layoutId) { + mLightBackgroundLayoutId = layoutId; + } + + /** + * If this view supports dark text versions, creates a copy representing that version, + * otherwise returns itself. + * @hide + */ + public RemoteViews getDarkTextViews() { + if (hasFlags(FLAG_USE_LIGHT_BACKGROUND_LAYOUT)) { + return this; + } + + try { + addFlags(FLAG_USE_LIGHT_BACKGROUND_LAYOUT); + return new RemoteViews(this); + } finally { + mApplyFlags &= ~FLAG_USE_LIGHT_BACKGROUND_LAYOUT; + } + } + private RemoteViews getRemoteViewsToApply(Context context) { if (hasLandscapeAndPortraitLayouts()) { int orientation = context.getResources().getConfiguration().orientation; @@ -3652,7 +3702,7 @@ public class RemoteViews implements Parcelable, Filter { mApplication.writeToParcel(dest, flags); } dest.writeInt(mLayoutId); - dest.writeInt(mIsWidgetCollectionChild ? 1 : 0); + dest.writeInt(mLightBackgroundLayoutId); writeActionsToParcel(dest); } else { dest.writeInt(MODE_HAS_LANDSCAPE_AND_PORTRAIT); @@ -3665,7 +3715,7 @@ public class RemoteViews implements Parcelable, Filter { // Both RemoteViews already share the same package and user mPortrait.writeToParcel(dest, flags | PARCELABLE_ELIDE_DUPLICATES); } - dest.writeInt(mReapplyDisallowed ? 1 : 0); + dest.writeInt(mApplyFlags); } private void writeActionsToParcel(Parcel parcel) { diff --git a/core/java/android/widget/RemoteViewsAdapter.java b/core/java/android/widget/RemoteViewsAdapter.java index d17c7c58ee74..c5cd1a1ece35 100644 --- a/core/java/android/widget/RemoteViewsAdapter.java +++ b/core/java/android/widget/RemoteViewsAdapter.java @@ -16,6 +16,9 @@ package android.widget; +import static android.widget.RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID; +import static android.widget.RemoteViews.EXTRA_REMOTEADAPTER_ON_LIGHT_BACKGROUND; + import android.annotation.UnsupportedAppUsage; import android.annotation.WorkerThread; import android.app.IServiceConnection; @@ -97,6 +100,7 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback private final Context mContext; private final Intent mIntent; private final int mAppWidgetId; + private final boolean mOnLightBackground; private final Executor mAsyncViewLoadExecutor; private OnClickHandler mRemoteViewsOnClickHandler; @@ -817,13 +821,13 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback throw new IllegalArgumentException("Non-null Intent must be specified."); } - mAppWidgetId = intent.getIntExtra(RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID, -1); + mAppWidgetId = intent.getIntExtra(EXTRA_REMOTEADAPTER_APPWIDGET_ID, -1); mRequestedViews = new RemoteViewsFrameLayoutRefSet(); + mOnLightBackground = intent.getBooleanExtra(EXTRA_REMOTEADAPTER_ON_LIGHT_BACKGROUND, false); // Strip the previously injected app widget id from service intent - if (intent.hasExtra(RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID)) { - intent.removeExtra(RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID); - } + intent.removeExtra(EXTRA_REMOTEADAPTER_APPWIDGET_ID); + intent.removeExtra(EXTRA_REMOTEADAPTER_ON_LIGHT_BACKGROUND); // Initialize the worker thread mWorkerThread = new HandlerThread("RemoteViewsCache-loader"); @@ -1107,6 +1111,7 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback } else { layout = new RemoteViewsFrameLayout(parent.getContext(), mCache); layout.setExecutor(mAsyncViewLoadExecutor); + layout.setOnLightBackground(mOnLightBackground); } if (isInCache) { diff --git a/core/java/android/widget/RemoteViewsListAdapter.java b/core/java/android/widget/RemoteViewsListAdapter.java index e490458b38a8..b80fe4871616 100644 --- a/core/java/android/widget/RemoteViewsListAdapter.java +++ b/core/java/android/widget/RemoteViewsListAdapter.java @@ -85,7 +85,7 @@ public class RemoteViewsListAdapter extends BaseAdapter { public View getView(int position, View convertView, ViewGroup parent) { if (position < getCount()) { RemoteViews rv = mRemoteViewsList.get(position); - rv.setIsWidgetCollectionChild(true); + rv.addFlags(RemoteViews.FLAG_WIDGET_IS_COLLECTION_CHILD); View v; if (convertView != null && rv != null && convertView.getId() == rv.getLayoutId()) { diff --git a/core/java/android/widget/RemoteViewsService.java b/core/java/android/widget/RemoteViewsService.java index 2827f634b389..214e5cc01b9e 100644 --- a/core/java/android/widget/RemoteViewsService.java +++ b/core/java/android/widget/RemoteViewsService.java @@ -163,7 +163,7 @@ public abstract class RemoteViewsService extends Service { try { rv = mFactory.getViewAt(position); if (rv != null) { - rv.setIsWidgetCollectionChild(true); + rv.addFlags(RemoteViews.FLAG_WIDGET_IS_COLLECTION_CHILD); } } catch (Exception ex) { Thread t = Thread.currentThread(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java index 7086025836cb..ef343fac5afa 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java @@ -697,7 +697,7 @@ public class NotificationInflater { && newView.getPackage() != null && newView.getPackage().equals(oldView.getPackage()) && newView.getLayoutId() == oldView.getLayoutId() - && !oldView.isReapplyDisallowed()); + && !oldView.hasFlags(RemoteViews.FLAG_REAPPLY_DISALLOWED)); } public void setInflationCallback(InflationCallback callback) { |