diff options
| author | Pierre Barbier de Reuille <pbdr@google.com> | 2021-02-24 20:16:40 +0000 |
|---|---|---|
| committer | Android (Google) Code Review <android-gerrit@google.com> | 2021-02-24 20:16:40 +0000 |
| commit | 4e14c35736e115df735afefe0603e6d7d7ccf455 (patch) | |
| tree | c9e342db5041c189aa2c254831d3cb2c32e88863 /core/java | |
| parent | 5c9a0355f8abbbdb549e6eccbc454f98d5f03d94 (diff) | |
| parent | 4219de7ded64971f100f9187b3c6a75e03073152 (diff) | |
Merge "Framework implementation of go/widgets-dynamic-colors" into sc-dev
Diffstat (limited to 'core/java')
| -rw-r--r-- | core/java/android/appwidget/AppWidgetHostView.java | 69 | ||||
| -rw-r--r-- | core/java/android/widget/RemoteViews.java | 347 | ||||
| -rw-r--r-- | core/java/android/widget/RemoteViewsListAdapter.java | 9 |
3 files changed, 329 insertions, 96 deletions
diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java index 4277292e19a3..fc54c716d4ec 100644 --- a/core/java/android/appwidget/AppWidgetHostView.java +++ b/core/java/android/appwidget/AppWidgetHostView.java @@ -40,6 +40,7 @@ import android.util.AttributeSet; import android.util.Log; import android.util.Pair; import android.util.SparseArray; +import android.util.SparseIntArray; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; @@ -92,7 +93,10 @@ public class AppWidgetHostView extends FrameLayout { int mLayoutId = -1; private InteractionHandler mInteractionHandler; private boolean mOnLightBackground; - PointF mCurrentSize = null; + private PointF mCurrentSize = null; + private RemoteViews.ColorResources mColorResources = null; + // Stores the last remote views last inflated. + private RemoteViews mLastInflatedRemoteViews = null; private Executor mAsyncExecutor; private CancellationSignal mLastExecutionSignal; @@ -358,7 +362,7 @@ public class AppWidgetHostView extends FrameLayout { PointF newSize = new PointF(size.x - xPaddingDips, size.y - yPaddingDips); if (!newSize.equals(mCurrentSize)) { mCurrentSize = newSize; - mLayoutId = -1; // Prevents recycling the view. + reapplyLastRemoteViews(); } } @@ -368,7 +372,7 @@ public class AppWidgetHostView extends FrameLayout { public void clearCurrentSize() { if (mCurrentSize != null) { mCurrentSize = null; - mLayoutId = -1; + reapplyLastRemoteViews(); } } @@ -477,10 +481,18 @@ public class AppWidgetHostView extends FrameLayout { * AppWidget provider. Will animate into these new views as needed */ public void updateAppWidget(RemoteViews remoteViews) { + this.mLastInflatedRemoteViews = remoteViews; applyRemoteViews(remoteViews, true); } /** + * Reapply the last inflated remote views, or the default view is none was inflated. + */ + private void reapplyLastRemoteViews() { + applyRemoteViews(mLastInflatedRemoteViews, true); + } + + /** * @hide */ protected void applyRemoteViews(RemoteViews remoteViews, boolean useAsyncIfPossible) { @@ -518,7 +530,8 @@ public class AppWidgetHostView extends FrameLayout { // layout matches, try recycling it if (content == null && layoutId == mLayoutId) { try { - remoteViews.reapply(mContext, mView, mInteractionHandler); + remoteViews.reapply(mContext, mView, mInteractionHandler, mCurrentSize, + mColorResources); content = mView; recycled = true; if (LOGD) Log.d(TAG, "was able to recycle existing layout"); @@ -530,7 +543,8 @@ public class AppWidgetHostView extends FrameLayout { // Try normal RemoteView inflation if (content == null) { try { - content = remoteViews.apply(mContext, this, mInteractionHandler, mCurrentSize); + content = remoteViews.apply(mContext, this, mInteractionHandler, + mCurrentSize, mColorResources); if (LOGD) Log.d(TAG, "had to inflate new layout"); } catch (RuntimeException e) { exception = e; @@ -583,7 +597,8 @@ public class AppWidgetHostView extends FrameLayout { mAsyncExecutor, new ViewApplyListener(remoteViews, layoutId, true), mInteractionHandler, - mCurrentSize); + mCurrentSize, + mColorResources); } catch (Exception e) { // Reapply failed. Try apply } @@ -594,7 +609,8 @@ public class AppWidgetHostView extends FrameLayout { mAsyncExecutor, new ViewApplyListener(remoteViews, layoutId, false), mInteractionHandler, - mCurrentSize); + mCurrentSize, + mColorResources); } } @@ -662,9 +678,13 @@ public class AppWidgetHostView extends FrameLayout { protected Context getRemoteContext() { try { // Return if cloned successfully, otherwise default - return mContext.createApplicationContext( + Context newContext = mContext.createApplicationContext( mInfo.providerInfo.applicationInfo, Context.CONTEXT_RESTRICTED); + if (mColorResources != null) { + mColorResources.apply(newContext); + } + return newContext; } catch (NameNotFoundException e) { Log.e(TAG, "Package name " + mInfo.providerInfo.packageName + " not found"); return mContext; @@ -819,4 +839,37 @@ public class AppWidgetHostView extends FrameLayout { } }; } + + /** + * Set the dynamically overloaded color resources. + * + * {@code colorMapping} maps a predefined set of color resources to their ARGB + * representation. Any entry not in the predefined set of colors will be ignored. + * + * Calling this method will trigger a full re-inflation of the App Widget. + * + * The color resources that can be overloaded are the ones whose name is prefixed with + * {@code system_primary_}, {@code system_secondary_} or {@code system_neutral_}, for example + * {@link android.R.color#system_primary_500}. + */ + public void setColorResources(@NonNull SparseIntArray colorMapping) { + mColorResources = RemoteViews.ColorResources.create(mContext, colorMapping); + mLayoutId = -1; + reapplyLastRemoteViews(); + } + + /** + * Reset the dynamically overloaded resources, reverting to the default values for + * all the colors. + * + * If colors were defined before, calling this method will trigger a full re-inflation of the + * App Widget. + */ + public void resetColorResources() { + if (mColorResources != null) { + mColorResources = null; + mLayoutId = -1; + reapplyLastRemoteViews(); + } + } } diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index 514471783c6a..e0b4ec71b0a0 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -46,6 +46,8 @@ import android.content.res.ColorStateList; import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.TypedArray; +import android.content.res.loader.ResourcesLoader; +import android.content.res.loader.ResourcesProvider; import android.graphics.Bitmap; import android.graphics.BlendMode; import android.graphics.Outline; @@ -62,16 +64,19 @@ import android.os.Build; import android.os.Bundle; import android.os.CancellationSignal; import android.os.Parcel; +import android.os.ParcelFileDescriptor; import android.os.Parcelable; import android.os.Process; import android.os.StrictMode; import android.os.UserHandle; +import android.system.Os; import android.text.TextUtils; import android.util.ArrayMap; import android.util.DisplayMetrics; import android.util.IntArray; import android.util.Log; import android.util.Pair; +import android.util.SparseIntArray; import android.util.TypedValue; import android.util.TypedValue.ComplexDimensionUnit; import android.view.ContextThemeWrapper; @@ -92,6 +97,12 @@ import com.android.internal.R; import com.android.internal.util.ContrastColorUtil; import com.android.internal.util.Preconditions; +import java.io.ByteArrayOutputStream; +import java.io.FileDescriptor; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -520,8 +531,8 @@ public class RemoteViews implements Parcelable, Filter { * SUBCLASSES MUST BE IMMUTABLE SO CLONE WORKS!!!!! */ private abstract static class Action implements Parcelable { - public abstract void apply(View root, ViewGroup rootParent, - InteractionHandler handler) throws ActionException; + public abstract void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) throws ActionException; public static final int MERGE_REPLACE = 0; public static final int MERGE_APPEND = 1; @@ -551,8 +562,8 @@ public class RemoteViews implements Parcelable, Filter { * and return the final action which will run on the UI thread. * Override this if some of the tasks can be performed async. */ - public Action initActionAsync( - ViewTree root, ViewGroup rootParent, InteractionHandler handler) { + public Action initActionAsync(ViewTree root, ViewGroup rootParent, + InteractionHandler handler, ColorResources colorResources) { return this; } @@ -595,7 +606,9 @@ public class RemoteViews implements Parcelable, Filter { // Constant used during async execution. It is not parcelable. private static final Action ACTION_NOOP = new RuntimeAction() { @Override - public void apply(View root, ViewGroup rootParent, InteractionHandler handler) { } + public void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) { + } }; /** @@ -713,7 +726,8 @@ public class RemoteViews implements Parcelable, Filter { } @Override - public void apply(View root, ViewGroup rootParent, InteractionHandler handler) { + public void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) { final View view = root.findViewById(viewId); if (!(view instanceof AdapterView<?>)) return; @@ -748,7 +762,8 @@ public class RemoteViews implements Parcelable, Filter { } @Override - public void apply(View root, ViewGroup rootParent, final InteractionHandler handler) { + public void apply(View root, ViewGroup rootParent, final InteractionHandler handler, + ColorResources colorResources) { final View target = root.findViewById(viewId); if (target == null) return; @@ -820,7 +835,8 @@ public class RemoteViews implements Parcelable, Filter { } @Override - public void apply(View root, ViewGroup rootParent, InteractionHandler handler) { + public void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) { final View target = root.findViewById(viewId); if (target == null) return; @@ -843,7 +859,8 @@ public class RemoteViews implements Parcelable, Filter { if (a instanceof RemoteViewsListAdapter && viewTypeCount <= a.getViewTypeCount()) { ((RemoteViewsListAdapter) a).setViewsList(list); } else { - v.setAdapter(new RemoteViewsListAdapter(v.getContext(), list, viewTypeCount)); + v.setAdapter(new RemoteViewsListAdapter(v.getContext(), list, viewTypeCount, + colorResources)); } } else if (target instanceof AdapterViewAnimator) { AdapterViewAnimator v = (AdapterViewAnimator) target; @@ -851,7 +868,8 @@ public class RemoteViews implements Parcelable, Filter { if (a instanceof RemoteViewsListAdapter && viewTypeCount <= a.getViewTypeCount()) { ((RemoteViewsListAdapter) a).setViewsList(list); } else { - v.setAdapter(new RemoteViewsListAdapter(v.getContext(), list, viewTypeCount)); + v.setAdapter(new RemoteViewsListAdapter(v.getContext(), list, viewTypeCount, + colorResources)); } } } @@ -882,7 +900,8 @@ public class RemoteViews implements Parcelable, Filter { } @Override - public void apply(View root, ViewGroup rootParent, InteractionHandler handler) { + public void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) { final View target = root.findViewById(viewId); if (target == null) return; @@ -919,7 +938,7 @@ public class RemoteViews implements Parcelable, Filter { @Override public Action initActionAsync(ViewTree root, ViewGroup rootParent, - InteractionHandler handler) { + InteractionHandler handler, ColorResources colorResources) { SetRemoteViewsAdapterIntent copy = new SetRemoteViewsAdapterIntent(viewId, intent); copy.isAsync = true; return copy; @@ -958,7 +977,8 @@ public class RemoteViews implements Parcelable, Filter { } @Override - public void apply(View root, ViewGroup rootParent, final InteractionHandler handler) { + public void apply(View root, ViewGroup rootParent, final InteractionHandler handler, + ColorResources colorResources) { final View target = root.findViewById(viewId); if (target == null) return; @@ -1037,7 +1057,8 @@ public class RemoteViews implements Parcelable, Filter { } @Override - public void apply(View root, ViewGroup rootParent, final InteractionHandler handler) { + public void apply(View root, ViewGroup rootParent, final InteractionHandler handler, + ColorResources colorResources) { final View target = root.findViewById(viewId); if (target == null) return; if (!(target instanceof CompoundButton)) { @@ -1240,7 +1261,8 @@ public class RemoteViews implements Parcelable, Filter { } @Override - public void apply(View root, ViewGroup rootParent, InteractionHandler handler) { + public void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) { final View target = root.findViewById(viewId); if (target == null) return; @@ -1297,7 +1319,8 @@ public class RemoteViews implements Parcelable, Filter { } @Override - public void apply(View root, ViewGroup rootParent, InteractionHandler handler) { + public void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) { final View target = root.findViewById(viewId); if (target == null) return; @@ -1334,7 +1357,8 @@ public class RemoteViews implements Parcelable, Filter { } @Override - public void apply(View root, ViewGroup rootParent, InteractionHandler handler) { + public void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) { final View view = root.findViewById(viewId); if (view == null) return; @@ -1437,12 +1461,12 @@ public class RemoteViews implements Parcelable, Filter { } @Override - public void apply(View root, ViewGroup rootParent, - InteractionHandler handler) throws ActionException { + public void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) throws ActionException { ReflectionAction ra = new ReflectionAction(viewId, methodName, BaseReflectionAction.BITMAP, bitmap); - ra.apply(root, rootParent, handler); + ra.apply(root, rootParent, handler, colorResources); } @Override @@ -1518,7 +1542,8 @@ public class RemoteViews implements Parcelable, Filter { protected abstract Object getParameterValue(@Nullable View view) throws ActionException; @Override - public final void apply(View root, ViewGroup rootParent, InteractionHandler handler) { + public final void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) { final View view = root.findViewById(viewId); if (view == null) return; @@ -1536,7 +1561,7 @@ public class RemoteViews implements Parcelable, Filter { @Override public final Action initActionAsync(ViewTree root, ViewGroup rootParent, - InteractionHandler handler) { + InteractionHandler handler, ColorResources colorResources) { final View view = root.findViewById(viewId); if (view == null) return ACTION_NOOP; @@ -1938,7 +1963,8 @@ public class RemoteViews implements Parcelable, Filter { } @Override - public void apply(View root, ViewGroup rootParent, InteractionHandler handler) { + public void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) { mRunnable.run(); } } @@ -1993,7 +2019,8 @@ public class RemoteViews implements Parcelable, Filter { } @Override - public void apply(View root, ViewGroup rootParent, InteractionHandler handler) { + public void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) { final Context context = root.getContext(); final ViewGroup target = root.findViewById(viewId); @@ -2002,12 +2029,14 @@ public class RemoteViews implements Parcelable, Filter { } // Inflate nested views and add as children - target.addView(mNestedViews.apply(context, target, handler), mIndex); + target.addView( + mNestedViews.apply(context, target, handler, null /* size */, colorResources), + mIndex); } @Override public Action initActionAsync(ViewTree root, ViewGroup rootParent, - InteractionHandler handler) { + InteractionHandler handler, ColorResources colorResources) { // In the async implementation, update the view tree so that subsequent calls to // findViewById return the current view. root.createTree(); @@ -2019,8 +2048,8 @@ public class RemoteViews implements Parcelable, Filter { // Inflate nested views and perform all the async tasks for the child remoteView. final Context context = root.mRoot.getContext(); - final AsyncApplyTask task = mNestedViews.getAsyncApplyTask( - context, targetVg, null, handler); + final AsyncApplyTask task = mNestedViews.getAsyncApplyTask(context, targetVg, + null /* listener */, handler, null /* size */, colorResources); final ViewTree tree = task.doInBackground(); if (tree == null) { @@ -2033,8 +2062,8 @@ public class RemoteViews implements Parcelable, Filter { return new RuntimeAction() { @Override - public void apply(View root, ViewGroup rootParent, InteractionHandler handler) - throws ActionException { + public void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) throws ActionException { task.onPostExecute(tree); targetVg.addView(task.mResult, mIndex); } @@ -2095,7 +2124,8 @@ public class RemoteViews implements Parcelable, Filter { } @Override - public void apply(View root, ViewGroup rootParent, InteractionHandler handler) { + public void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) { final ViewGroup target = root.findViewById(viewId); if (target == null) { @@ -2112,7 +2142,7 @@ public class RemoteViews implements Parcelable, Filter { @Override public Action initActionAsync(ViewTree root, ViewGroup rootParent, - InteractionHandler handler) { + InteractionHandler handler, ColorResources colorResources) { // In the async implementation, update the view tree so that subsequent calls to // findViewById return the current view. root.createTree(); @@ -2136,8 +2166,8 @@ public class RemoteViews implements Parcelable, Filter { } return new RuntimeAction() { @Override - public void apply(View root, ViewGroup rootParent, InteractionHandler handler) - throws ActionException { + public void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) throws ActionException { if (mViewIdToKeep == REMOVE_ALL_VIEWS_ID) { targetVg.removeAllViews(); return; @@ -2192,7 +2222,8 @@ public class RemoteViews implements Parcelable, Filter { } @Override - public void apply(View root, ViewGroup rootParent, InteractionHandler handler) { + public void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) { final View target = root.findViewById(viewId); if (target == null || target == root) { @@ -2207,7 +2238,7 @@ public class RemoteViews implements Parcelable, Filter { @Override public Action initActionAsync(ViewTree root, ViewGroup rootParent, - InteractionHandler handler) { + InteractionHandler handler, ColorResources colorResources) { // In the async implementation, update the view tree so that subsequent calls to // findViewById return the correct view. root.createTree(); @@ -2226,8 +2257,8 @@ public class RemoteViews implements Parcelable, Filter { parent.mChildren.remove(target); return new RuntimeAction() { @Override - public void apply(View root, ViewGroup rootParent, InteractionHandler handler) - throws ActionException { + public void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) throws ActionException { parentVg.removeView(target.mRoot); } }; @@ -2306,7 +2337,8 @@ public class RemoteViews implements Parcelable, Filter { } @Override - public void apply(View root, ViewGroup rootParent, InteractionHandler handler) { + public void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) { final TextView target = root.findViewById(viewId); if (target == null) return; if (drawablesLoaded) { @@ -2337,7 +2369,7 @@ public class RemoteViews implements Parcelable, Filter { @Override public Action initActionAsync(ViewTree root, ViewGroup rootParent, - InteractionHandler handler) { + InteractionHandler handler, ColorResources colorResources) { final TextView target = root.findViewById(viewId); if (target == null) return ACTION_NOOP; @@ -2415,7 +2447,8 @@ public class RemoteViews implements Parcelable, Filter { } @Override - public void apply(View root, ViewGroup rootParent, InteractionHandler handler) { + public void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) { final TextView target = root.findViewById(viewId); if (target == null) return; target.setTextSize(units, size); @@ -2460,7 +2493,8 @@ public class RemoteViews implements Parcelable, Filter { } @Override - public void apply(View root, ViewGroup rootParent, InteractionHandler handler) { + public void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) { final View target = root.findViewById(viewId); if (target == null) return; target.setPadding(left, top, right, bottom); @@ -2533,7 +2567,8 @@ public class RemoteViews implements Parcelable, Filter { } @Override - public void apply(View root, ViewGroup rootParent, InteractionHandler handler) { + public void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) { final View target = root.findViewById(viewId); if (target == null) { return; @@ -2646,7 +2681,8 @@ public class RemoteViews implements Parcelable, Filter { } @Override - public void apply(View root, ViewGroup rootParent, InteractionHandler handler) { + public void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) { final View target = root.findViewById(viewId); if (target == null) return; @@ -2681,7 +2717,8 @@ public class RemoteViews implements Parcelable, Filter { } @Override - public void apply(View root, ViewGroup rootParent, InteractionHandler handler) { + public void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) { // Let's traverse the viewtree and override all textColors! Stack<View> viewsToProcess = new Stack<>(); viewsToProcess.add(root); @@ -2731,7 +2768,8 @@ public class RemoteViews implements Parcelable, Filter { } @Override - public void apply(View root, ViewGroup rootParent, InteractionHandler handler) { + public void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) { final View target = root.findViewById(mViewId); if (target == null) return; @@ -2765,7 +2803,8 @@ public class RemoteViews implements Parcelable, Filter { } @Override - public void apply(View root, ViewGroup rootParent, InteractionHandler handler) + public void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) throws ActionException { final View target = root.findViewById(viewId); if (target == null) return; @@ -2816,8 +2855,8 @@ public class RemoteViews implements Parcelable, Filter { } @Override - public void apply(View root, ViewGroup rootParent, InteractionHandler handler) - throws ActionException { + public void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) throws ActionException { final View target = root.findViewById(viewId); if (target == null) return; @@ -2893,8 +2932,8 @@ public class RemoteViews implements Parcelable, Filter { } @Override - public void apply(View root, ViewGroup rootParent, InteractionHandler handler) - throws ActionException { + public void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) throws ActionException { final View target = root.findViewById(viewId); if (target == null) return; @@ -4662,7 +4701,7 @@ public class RemoteViews implements Parcelable, Filter { RemoteViews rvToApply = getRemoteViewsToApply(context, size); View result = inflateView(context, rvToApply, parent); - rvToApply.performApply(result, parent, handler); + rvToApply.performApply(result, parent, handler, null); return result; } @@ -4674,27 +4713,39 @@ public class RemoteViews implements Parcelable, Filter { /** @hide */ public View applyWithTheme(@NonNull Context context, @NonNull ViewGroup parent, - @Nullable InteractionHandler handler, - @StyleRes int applyThemeResId, + @Nullable InteractionHandler handler, @StyleRes int applyThemeResId, @Nullable PointF size) { RemoteViews rvToApply = getRemoteViewsToApply(context, size); - View result = inflateView(context, rvToApply, parent, applyThemeResId); - rvToApply.performApply(result, parent, handler); + View result = inflateView(context, rvToApply, parent, applyThemeResId, null); + rvToApply.performApply(result, parent, handler, null); + return result; + } + + /** @hide */ + public View apply(Context context, ViewGroup parent, InteractionHandler handler, + @NonNull PointF size, @Nullable ColorResources colorResources) { + RemoteViews rvToApply = getRemoteViewsToApply(context, size); + + View result = inflateView(context, rvToApply, parent, 0, colorResources); + rvToApply.performApply(result, parent, handler, colorResources); return result; } private View inflateView(Context context, RemoteViews rv, ViewGroup parent) { - return inflateView(context, rv, parent, 0); + return inflateView(context, rv, parent, 0, null); } private View inflateView(Context context, RemoteViews rv, ViewGroup parent, - @StyleRes int applyThemeResId) { + @StyleRes int applyThemeResId, @Nullable ColorResources colorResources) { // RemoteViews may be built by an application installed in another // user. So build a context that loads resources from that user but // still returns the current users userId so settings like data / time formats // are loaded without requiring cross user persmissions. final Context contextForResources = getContextForResources(context); + if (colorResources != null) { + colorResources.apply(contextForResources); + } Context inflationContext = new RemoteViewsContextWrapper(context, contextForResources); // If mApplyThemeResId is not given, Theme.DeviceDefault will be used. @@ -4756,34 +4807,37 @@ public class RemoteViews implements Parcelable, Filter { */ public CancellationSignal applyAsync( Context context, ViewGroup parent, Executor executor, OnViewAppliedListener listener) { - return applyAsync(context, parent, executor, listener, null); + return applyAsync(context, parent, executor, listener, null /* handler */); } /** @hide */ public CancellationSignal applyAsync(Context context, ViewGroup parent, Executor executor, OnViewAppliedListener listener, InteractionHandler handler) { - return applyAsync(context, parent, executor, listener, handler, null); + return applyAsync(context, parent, executor, listener, handler, null /* size */); } /** @hide */ public CancellationSignal applyAsync(Context context, ViewGroup parent, Executor executor, OnViewAppliedListener listener, InteractionHandler handler, PointF size) { - return getAsyncApplyTask(context, parent, listener, handler, size).startTaskOnExecutor( - executor); + return getAsyncApplyTask(context, parent, listener, handler, size, null /* themeColors */) + .startTaskOnExecutor(executor); } - private AsyncApplyTask getAsyncApplyTask(Context context, ViewGroup parent, - OnViewAppliedListener listener, InteractionHandler handler) { - return getAsyncApplyTask(context, parent, listener, handler, null); + /** @hide */ + public CancellationSignal applyAsync(Context context, ViewGroup parent, Executor executor, + OnViewAppliedListener listener, InteractionHandler handler, PointF size, + ColorResources colorResources) { + return getAsyncApplyTask(context, parent, listener, handler, size, colorResources) + .startTaskOnExecutor(executor); } private AsyncApplyTask getAsyncApplyTask(Context context, ViewGroup parent, - OnViewAppliedListener listener, InteractionHandler handler, PointF size) { - return new AsyncApplyTask(getRemoteViewsToApply(context, size), parent, context, - listener, - handler, null); + OnViewAppliedListener listener, InteractionHandler handler, PointF size, + ColorResources colorResources) { + return new AsyncApplyTask(getRemoteViewsToApply(context, size), parent, context, listener, + handler, colorResources, null /* result */); } private class AsyncApplyTask extends AsyncTask<Void, Void, ViewTree> @@ -4794,6 +4848,7 @@ public class RemoteViews implements Parcelable, Filter { final Context mContext; final OnViewAppliedListener mListener; final InteractionHandler mHandler; + final ColorResources mColorResources; private View mResult; private ViewTree mTree; @@ -4802,11 +4857,12 @@ public class RemoteViews implements Parcelable, Filter { private AsyncApplyTask( RemoteViews rv, ViewGroup parent, Context context, OnViewAppliedListener listener, - InteractionHandler handler, View result) { + InteractionHandler handler, ColorResources colorResources, View result) { mRV = rv; mParent = parent; mContext = context; mListener = listener; + mColorResources = colorResources; mHandler = handler; mResult = result; @@ -4816,7 +4872,7 @@ public class RemoteViews implements Parcelable, Filter { protected ViewTree doInBackground(Void... params) { try { if (mResult == null) { - mResult = inflateView(mContext, mRV, mParent); + mResult = inflateView(mContext, mRV, mParent, 0, mColorResources); } mTree = new ViewTree(mResult); @@ -4825,7 +4881,8 @@ public class RemoteViews implements Parcelable, Filter { mActions = new Action[count]; for (int i = 0; i < count && !isCancelled(); i++) { // TODO: check if isCancelled in nested views. - mActions[i] = mRV.mActions.get(i).initActionAsync(mTree, mParent, mHandler); + mActions[i] = mRV.mActions.get(i).initActionAsync(mTree, mParent, mHandler, + mColorResources); } } else { mActions = null; @@ -4850,7 +4907,7 @@ public class RemoteViews implements Parcelable, Filter { InteractionHandler handler = mHandler == null ? DEFAULT_INTERACTION_HANDLER : mHandler; for (Action a : mActions) { - a.apply(viewTree.mRoot, mParent, handler); + a.apply(viewTree.mRoot, mParent, handler, mColorResources); } } } catch (Exception e) { @@ -4894,16 +4951,17 @@ public class RemoteViews implements Parcelable, Filter { * the {@link #apply(Context,ViewGroup)} call. */ public void reapply(Context context, View v) { - reapply(context, v, null, null); + reapply(context, v, null /* handler */); } /** @hide */ public void reapply(Context context, View v, InteractionHandler handler) { - reapply(context, v, handler, null); + reapply(context, v, handler, null /* size */, null /* colorResources */); } /** @hide */ - public void reapply(Context context, View v, InteractionHandler handler, PointF size) { + public void reapply(Context context, View v, InteractionHandler handler, PointF size, + ColorResources colorResources) { RemoteViews rvToApply = getRemoteViewsToApply(context, size); // In the case that a view has this RemoteViews applied in one orientation or size, is @@ -4917,7 +4975,7 @@ public class RemoteViews implements Parcelable, Filter { } } - rvToApply.performApply(v, (ViewGroup) v.getParent(), handler); + rvToApply.performApply(v, (ViewGroup) v.getParent(), handler, colorResources); } /** @@ -4933,20 +4991,21 @@ public class RemoteViews implements Parcelable, Filter { * @return CancellationSignal * @hide */ - public CancellationSignal reapplyAsync( - Context context, View v, Executor executor, OnViewAppliedListener listener) { - return reapplyAsync(context, v, executor, listener, null, null); + public CancellationSignal reapplyAsync(Context context, View v, Executor executor, + OnViewAppliedListener listener) { + return reapplyAsync(context, v, executor, listener, null); } /** @hide */ public CancellationSignal reapplyAsync(Context context, View v, Executor executor, OnViewAppliedListener listener, InteractionHandler handler) { - return reapplyAsync(context, v, executor, listener, handler, null); + return reapplyAsync(context, v, executor, listener, handler, null, null); } /** @hide */ public CancellationSignal reapplyAsync(Context context, View v, Executor executor, - OnViewAppliedListener listener, InteractionHandler handler, PointF size) { + OnViewAppliedListener listener, InteractionHandler handler, PointF size, + ColorResources colorResources) { RemoteViews rvToApply = getRemoteViewsToApply(context, size); // In the case that a view has this RemoteViews applied in one orientation, is persisted @@ -4960,16 +5019,18 @@ public class RemoteViews implements Parcelable, Filter { } return new AsyncApplyTask(rvToApply, (ViewGroup) v.getParent(), - context, listener, handler, v).startTaskOnExecutor(executor); + context, listener, handler, colorResources, v).startTaskOnExecutor( + executor); } - private void performApply(View v, ViewGroup parent, InteractionHandler handler) { + private void performApply(View v, ViewGroup parent, InteractionHandler handler, + ColorResources colorResources) { if (mActions != null) { handler = handler == null ? DEFAULT_INTERACTION_HANDLER : handler; final int count = mActions.size(); for (int i = 0; i < count; i++) { Action a = mActions.get(i); - a.apply(v, parent, handler); + a.apply(v, parent, handler, colorResources); } } } @@ -5010,6 +5071,122 @@ public class RemoteViews implements Parcelable, Filter { } /** + * Object allowing the modification of a context to overload the system's dynamic colors. + * + * Only colors from {@link android.R.color#system_primary_0} to + * {@link android.R.color#system_neutral_1000} can be overloaded. + * @hide + */ + public static final class ColorResources { + // Set of valid colors resources. + private static final int FIRST_RESOURCE_COLOR_ID = android.R.color.system_primary_0; + private static final int LAST_RESOURCE_COLOR_ID = android.R.color.system_neutral_1000; + // Size, in bytes, of an entry in the array of colors in an ARSC file. + private static final int ARSC_ENTRY_SIZE = 16; + + private ResourcesLoader mLoader; + + private ColorResources(ResourcesLoader loader) { + mLoader = loader; + } + + /** + * Apply the color resources to the given context. + * + * No resource resolution must have be done on the context given to that method. + */ + public void apply(Context context) { + context.getResources().addLoaders(mLoader); + } + + private static ByteArrayOutputStream readFileContent(InputStream input) throws IOException { + ByteArrayOutputStream content = new ByteArrayOutputStream(2048); + byte[] buffer = new byte[4096]; + while (input.available() > 0) { + int read = input.read(buffer); + content.write(buffer, 0, read); + } + return content; + } + + /** + * Creates the compiled resources content from the asset stored in the APK. + * + * The asset is a compiled resource with the correct resources name and correct ids, only + * the values are incorrect. The last value is at the very end of the file. The resources + * are in an array, the array's entries are 16 bytes each. We use this to work out the + * location of all the positions of the various resources. + */ + private static byte[] createCompiledResourcesContent(Context context, + SparseIntArray colorResources) throws IOException { + byte[] content; + try (InputStream input = context.getResources().openRawResource( + com.android.internal.R.raw.remote_views_color_resources)) { + ByteArrayOutputStream rawContent = readFileContent(input); + content = rawContent.toByteArray(); + } + int valuesOffset = + content.length - (LAST_RESOURCE_COLOR_ID & 0xffff) * ARSC_ENTRY_SIZE - 4; + if (valuesOffset < 0) { + Log.e(LOG_TAG, "ARSC file for theme colors is invalid."); + return null; + } + for (int colorRes = FIRST_RESOURCE_COLOR_ID; colorRes <= LAST_RESOURCE_COLOR_ID; + colorRes++) { + // The last 2 bytes are the index in the color array. + int index = colorRes & 0xffff; + int offset = valuesOffset + index * ARSC_ENTRY_SIZE; + int value = colorResources.get(colorRes, context.getColor(colorRes)); + // Write the 32 bit integer in little endian + for (int b = 0; b < 4; b++) { + content[offset + b] = (byte) (value & 0xff); + value >>= 8; + } + } + return content; + } + + /** + * Adds a resource loader for theme colors to the given context. + * + * @param context Context of the view hosting the widget. + * @param colorMapping Mapping of resources to color values. + * + * @hide + */ + public static ColorResources create(Context context, SparseIntArray colorMapping) { + try { + byte[] contentBytes = createCompiledResourcesContent(context, colorMapping); + if (contentBytes == null) { + return null; + } + FileDescriptor arscFile = null; + try { + arscFile = Os.memfd_create("remote_views_theme_colors.arsc", 0 /* flags */); + // Note: This must not be closed through the OutputStream. + try (OutputStream pipeWriter = new FileOutputStream(arscFile)) { + pipeWriter.write(contentBytes); + + try (ParcelFileDescriptor pfd = ParcelFileDescriptor.dup(arscFile)) { + ResourcesLoader colorsLoader = new ResourcesLoader(); + colorsLoader.addProvider(ResourcesProvider + .loadFromTable(pfd, null /* assetsProvider */)); + return new ColorResources(colorsLoader); + } + } + } finally { + if (arscFile != null) { + Os.close(arscFile); + } + } + } catch (Exception ex) { + Log.e(LOG_TAG, "Failed to setup the context for theme colors", ex); + } + return null; + } + } + + /** * Returns the number of actions in this RemoteViews. Can be used as a sequence number. * * @hide diff --git a/core/java/android/widget/RemoteViewsListAdapter.java b/core/java/android/widget/RemoteViewsListAdapter.java index b80fe4871616..827d03317d6a 100644 --- a/core/java/android/widget/RemoteViewsListAdapter.java +++ b/core/java/android/widget/RemoteViewsListAdapter.java @@ -31,12 +31,14 @@ public class RemoteViewsListAdapter extends BaseAdapter { private ArrayList<RemoteViews> mRemoteViewsList; private ArrayList<Integer> mViewTypes = new ArrayList<Integer>(); private int mViewTypeCount; + private RemoteViews.ColorResources mColorResources; public RemoteViewsListAdapter(Context context, ArrayList<RemoteViews> remoteViews, - int viewTypeCount) { + int viewTypeCount, RemoteViews.ColorResources colorResources) { mContext = context; mRemoteViewsList = remoteViews; mViewTypeCount = viewTypeCount; + mColorResources = colorResources; init(); } @@ -90,9 +92,10 @@ public class RemoteViewsListAdapter extends BaseAdapter { if (convertView != null && rv != null && convertView.getId() == rv.getLayoutId()) { v = convertView; - rv.reapply(mContext, v); + rv.reapply(mContext, v, null /* handler */, null /* size */, mColorResources); } else { - v = rv.apply(mContext, parent); + v = rv.apply(mContext, parent, null /* handler */, null /* size */, + mColorResources); } return v; } else { |
