summaryrefslogtreecommitdiff
path: root/core/java
diff options
context:
space:
mode:
authorPierre Barbier de Reuille <pbdr@google.com>2021-02-24 20:16:40 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2021-02-24 20:16:40 +0000
commit4e14c35736e115df735afefe0603e6d7d7ccf455 (patch)
treec9e342db5041c189aa2c254831d3cb2c32e88863 /core/java
parent5c9a0355f8abbbdb549e6eccbc454f98d5f03d94 (diff)
parent4219de7ded64971f100f9187b3c6a75e03073152 (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.java69
-rw-r--r--core/java/android/widget/RemoteViews.java347
-rw-r--r--core/java/android/widget/RemoteViewsListAdapter.java9
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 {