1 /*
2  * Copyright (C) 2007 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.widget;
18 
19 import android.annotation.AttrRes;
20 import android.annotation.ColorInt;
21 import android.annotation.ColorRes;
22 import android.annotation.DimenRes;
23 import android.annotation.DrawableRes;
24 import android.annotation.IdRes;
25 import android.annotation.IntDef;
26 import android.annotation.LayoutRes;
27 import android.annotation.NonNull;
28 import android.annotation.Nullable;
29 import android.annotation.Px;
30 import android.annotation.StringRes;
31 import android.annotation.StyleRes;
32 import android.annotation.SuppressLint;
33 import android.app.Activity;
34 import android.app.ActivityOptions;
35 import android.app.ActivityThread;
36 import android.app.Application;
37 import android.app.LoadedApk;
38 import android.app.PendingIntent;
39 import android.app.RemoteInput;
40 import android.appwidget.AppWidgetHostView;
41 import android.compat.annotation.UnsupportedAppUsage;
42 import android.content.Context;
43 import android.content.ContextWrapper;
44 import android.content.Intent;
45 import android.content.IntentSender;
46 import android.content.pm.ApplicationInfo;
47 import android.content.pm.PackageManager.NameNotFoundException;
48 import android.content.res.ColorStateList;
49 import android.content.res.Configuration;
50 import android.content.res.Resources;
51 import android.content.res.TypedArray;
52 import android.content.res.loader.ResourcesLoader;
53 import android.content.res.loader.ResourcesProvider;
54 import android.graphics.Bitmap;
55 import android.graphics.BlendMode;
56 import android.graphics.Outline;
57 import android.graphics.PorterDuff;
58 import android.graphics.Rect;
59 import android.graphics.drawable.Drawable;
60 import android.graphics.drawable.Icon;
61 import android.graphics.drawable.RippleDrawable;
62 import android.net.Uri;
63 import android.os.AsyncTask;
64 import android.os.Binder;
65 import android.os.Build;
66 import android.os.Bundle;
67 import android.os.CancellationSignal;
68 import android.os.Parcel;
69 import android.os.ParcelFileDescriptor;
70 import android.os.Parcelable;
71 import android.os.Process;
72 import android.os.StrictMode;
73 import android.os.UserHandle;
74 import android.system.Os;
75 import android.text.TextUtils;
76 import android.util.ArrayMap;
77 import android.util.DisplayMetrics;
78 import android.util.IntArray;
79 import android.util.Log;
80 import android.util.LongArray;
81 import android.util.Pair;
82 import android.util.SizeF;
83 import android.util.SparseIntArray;
84 import android.util.TypedValue;
85 import android.util.TypedValue.ComplexDimensionUnit;
86 import android.view.ContextThemeWrapper;
87 import android.view.LayoutInflater;
88 import android.view.LayoutInflater.Filter;
89 import android.view.RemotableViewMethod;
90 import android.view.View;
91 import android.view.ViewGroup;
92 import android.view.ViewGroup.MarginLayoutParams;
93 import android.view.ViewManager;
94 import android.view.ViewOutlineProvider;
95 import android.view.ViewParent;
96 import android.view.ViewStub;
97 import android.widget.AdapterView.OnItemClickListener;
98 import android.widget.CompoundButton.OnCheckedChangeListener;
99 
100 import com.android.internal.R;
101 import com.android.internal.util.ContrastColorUtil;
102 import com.android.internal.util.Preconditions;
103 
104 import java.io.ByteArrayOutputStream;
105 import java.io.FileDescriptor;
106 import java.io.FileOutputStream;
107 import java.io.IOException;
108 import java.io.InputStream;
109 import java.io.OutputStream;
110 import java.lang.annotation.ElementType;
111 import java.lang.annotation.Retention;
112 import java.lang.annotation.RetentionPolicy;
113 import java.lang.annotation.Target;
114 import java.lang.invoke.MethodHandle;
115 import java.lang.invoke.MethodHandles;
116 import java.lang.invoke.MethodType;
117 import java.lang.reflect.Method;
118 import java.util.ArrayDeque;
119 import java.util.ArrayList;
120 import java.util.Arrays;
121 import java.util.HashMap;
122 import java.util.Iterator;
123 import java.util.List;
124 import java.util.Map;
125 import java.util.Objects;
126 import java.util.Stack;
127 import java.util.concurrent.Executor;
128 import java.util.function.Consumer;
129 import java.util.function.Predicate;
130 
131 /**
132  * A class that describes a view hierarchy that can be displayed in
133  * another process. The hierarchy is inflated from a layout resource
134  * file, and this class provides some basic operations for modifying
135  * the content of the inflated hierarchy.
136  *
137  * <p>{@code RemoteViews} is limited to support for the following layouts:</p>
138  * <ul>
139  *   <li>{@link android.widget.AdapterViewFlipper}</li>
140  *   <li>{@link android.widget.FrameLayout}</li>
141  *   <li>{@link android.widget.GridLayout}</li>
142  *   <li>{@link android.widget.GridView}</li>
143  *   <li>{@link android.widget.LinearLayout}</li>
144  *   <li>{@link android.widget.ListView}</li>
145  *   <li>{@link android.widget.RelativeLayout}</li>
146  *   <li>{@link android.widget.StackView}</li>
147  *   <li>{@link android.widget.ViewFlipper}</li>
148  * </ul>
149  * <p>And the following widgets:</p>
150  * <ul>
151  *   <li>{@link android.widget.AnalogClock}</li>
152  *   <li>{@link android.widget.Button}</li>
153  *   <li>{@link android.widget.Chronometer}</li>
154  *   <li>{@link android.widget.ImageButton}</li>
155  *   <li>{@link android.widget.ImageView}</li>
156  *   <li>{@link android.widget.ProgressBar}</li>
157  *   <li>{@link android.widget.TextClock}</li>
158  *   <li>{@link android.widget.TextView}</li>
159  * </ul>
160  * <p>As of API 31, the following widgets and layouts may also be used:</p>
161  * <ul>
162  *     <li>{@link android.widget.CheckBox}</li>
163  *     <li>{@link android.widget.RadioButton}</li>
164  *     <li>{@link android.widget.RadioGroup}</li>
165  *     <li>{@link android.widget.Switch}</li>
166  * </ul>
167  * <p>Descendants of these classes are not supported.</p>
168  */
169 public class RemoteViews implements Parcelable, Filter {
170 
171     private static final String LOG_TAG = "RemoteViews";
172 
173     /** The intent extra for whether the view whose checked state changed is currently checked. */
174     public static final String EXTRA_CHECKED = "android.widget.extra.CHECKED";
175 
176     /**
177      * The intent extra that contains the appWidgetId.
178      * @hide
179      */
180     static final String EXTRA_REMOTEADAPTER_APPWIDGET_ID = "remoteAdapterAppWidgetId";
181 
182     /**
183      * The intent extra that contains {@code true} if inflating as dak text theme.
184      * @hide
185      */
186     static final String EXTRA_REMOTEADAPTER_ON_LIGHT_BACKGROUND = "remoteAdapterOnLightBackground";
187 
188     /**
189      * The intent extra that contains the bounds for all shared elements.
190      */
191     public static final String EXTRA_SHARED_ELEMENT_BOUNDS =
192             "android.widget.extra.SHARED_ELEMENT_BOUNDS";
193 
194     /**
195      * Maximum depth of nested views calls from {@link #addView(int, RemoteViews)} and
196      * {@link #RemoteViews(RemoteViews, RemoteViews)}.
197      */
198     private static final int MAX_NESTED_VIEWS = 10;
199 
200     /**
201      * Maximum number of RemoteViews that can be specified in constructor.
202      */
203     private static final int MAX_INIT_VIEW_COUNT = 16;
204 
205     // The unique identifiers for each custom {@link Action}.
206     private static final int SET_ON_CLICK_RESPONSE_TAG = 1;
207     private static final int REFLECTION_ACTION_TAG = 2;
208     private static final int SET_DRAWABLE_TINT_TAG = 3;
209     private static final int VIEW_GROUP_ACTION_ADD_TAG = 4;
210     private static final int VIEW_CONTENT_NAVIGATION_TAG = 5;
211     private static final int SET_EMPTY_VIEW_ACTION_TAG = 6;
212     private static final int VIEW_GROUP_ACTION_REMOVE_TAG = 7;
213     private static final int SET_PENDING_INTENT_TEMPLATE_TAG = 8;
214     private static final int SET_REMOTE_VIEW_ADAPTER_INTENT_TAG = 10;
215     private static final int TEXT_VIEW_DRAWABLE_ACTION_TAG = 11;
216     private static final int BITMAP_REFLECTION_ACTION_TAG = 12;
217     private static final int TEXT_VIEW_SIZE_ACTION_TAG = 13;
218     private static final int VIEW_PADDING_ACTION_TAG = 14;
219     private static final int SET_REMOTE_VIEW_ADAPTER_LIST_TAG = 15;
220     private static final int SET_REMOTE_INPUTS_ACTION_TAG = 18;
221     private static final int LAYOUT_PARAM_ACTION_TAG = 19;
222     private static final int OVERRIDE_TEXT_COLORS_TAG = 20;
223     private static final int SET_RIPPLE_DRAWABLE_COLOR_TAG = 21;
224     private static final int SET_INT_TAG_TAG = 22;
225     private static final int REMOVE_FROM_PARENT_ACTION_TAG = 23;
226     private static final int RESOURCE_REFLECTION_ACTION_TAG = 24;
227     private static final int COMPLEX_UNIT_DIMENSION_REFLECTION_ACTION_TAG = 25;
228     private static final int SET_COMPOUND_BUTTON_CHECKED_TAG = 26;
229     private static final int SET_RADIO_GROUP_CHECKED = 27;
230     private static final int SET_VIEW_OUTLINE_RADIUS_TAG = 28;
231     private static final int SET_ON_CHECKED_CHANGE_RESPONSE_TAG = 29;
232     private static final int NIGHT_MODE_REFLECTION_ACTION_TAG = 30;
233     private static final int SET_REMOTE_COLLECTION_ITEMS_ADAPTER_TAG = 31;
234     private static final int ATTRIBUTE_REFLECTION_ACTION_TAG = 32;
235 
236     /** @hide **/
237     @IntDef(prefix = "MARGIN_", value = {
238             MARGIN_LEFT,
239             MARGIN_TOP,
240             MARGIN_RIGHT,
241             MARGIN_BOTTOM,
242             MARGIN_START,
243             MARGIN_END
244     })
245     @Retention(RetentionPolicy.SOURCE)
246     public @interface MarginType {}
247     /** The value will apply to the marginLeft. */
248     public static final int MARGIN_LEFT = 0;
249     /** The value will apply to the marginTop. */
250     public static final int MARGIN_TOP = 1;
251     /** The value will apply to the marginRight. */
252     public static final int MARGIN_RIGHT = 2;
253     /** The value will apply to the marginBottom. */
254     public static final int MARGIN_BOTTOM = 3;
255     /** The value will apply to the marginStart. */
256     public static final int MARGIN_START = 4;
257     /** The value will apply to the marginEnd. */
258     public static final int MARGIN_END = 5;
259 
260     @IntDef(prefix = "VALUE_TYPE_", value = {
261             VALUE_TYPE_RAW,
262             VALUE_TYPE_COMPLEX_UNIT,
263             VALUE_TYPE_RESOURCE,
264             VALUE_TYPE_ATTRIBUTE
265     })
266     @Retention(RetentionPolicy.SOURCE)
267     @interface ValueType {}
268     static final int VALUE_TYPE_RAW = 1;
269     static final int VALUE_TYPE_COMPLEX_UNIT = 2;
270     static final int VALUE_TYPE_RESOURCE = 3;
271     static final int VALUE_TYPE_ATTRIBUTE = 4;
272 
273     /** @hide **/
274     @IntDef(flag = true, value = {
275             FLAG_REAPPLY_DISALLOWED,
276             FLAG_WIDGET_IS_COLLECTION_CHILD,
277             FLAG_USE_LIGHT_BACKGROUND_LAYOUT
278     })
279     @Retention(RetentionPolicy.SOURCE)
280     public @interface ApplyFlags {}
281     /**
282      * Whether reapply is disallowed on this remoteview. This maybe be true if some actions modify
283      * the layout in a way that isn't recoverable, since views are being removed.
284      * @hide
285      */
286     public static final int FLAG_REAPPLY_DISALLOWED = 1;
287     /**
288      * This flag indicates whether this RemoteViews object is being created from a
289      * RemoteViewsService for use as a child of a widget collection. This flag is used
290      * to determine whether or not certain features are available, in particular,
291      * setting on click extras and setting on click pending intents. The former is enabled,
292      * and the latter disabled when this flag is true.
293      * @hide
294      */
295     public static final int FLAG_WIDGET_IS_COLLECTION_CHILD = 2;
296     /**
297      * When this flag is set, the views is inflated with {@link #mLightBackgroundLayoutId} instead
298      * of {link #mLayoutId}
299      * @hide
300      */
301     public static final int FLAG_USE_LIGHT_BACKGROUND_LAYOUT = 4;
302 
303     /**
304      * A ReadWriteHelper which has the same behavior as ReadWriteHelper.DEFAULT, but which is
305      * intentionally a different instance in order to trick Bundle reader so that it doesn't allow
306      * lazy initialization.
307      */
308     private static final Parcel.ReadWriteHelper ALTERNATIVE_DEFAULT = new Parcel.ReadWriteHelper();
309 
310     /**
311      * Used to restrict the views which can be inflated
312      *
313      * @see android.view.LayoutInflater.Filter#onLoadClass(java.lang.Class)
314      */
315     private static final LayoutInflater.Filter INFLATER_FILTER =
316             (clazz) -> clazz.isAnnotationPresent(RemoteViews.RemoteView.class);
317 
318     /**
319      * Application that hosts the remote views.
320      *
321      * @hide
322      */
323     @UnsupportedAppUsage
324     public ApplicationInfo mApplication;
325 
326     /**
327      * The resource ID of the layout file. (Added to the parcel)
328      */
329     @UnsupportedAppUsage
330     private int mLayoutId;
331 
332     /**
333      * The resource ID of the layout file in dark text mode. (Added to the parcel)
334      */
335     private int mLightBackgroundLayoutId = 0;
336 
337     /**
338      * An array of actions to perform on the view tree once it has been
339      * inflated
340      */
341     @UnsupportedAppUsage
342     private ArrayList<Action> mActions;
343 
344     /**
345      * Maps bitmaps to unique indicies to avoid Bitmap duplication.
346      */
347     @UnsupportedAppUsage
348     private BitmapCache mBitmapCache = new BitmapCache();
349 
350     /** Cache of ApplicationInfos used by collection items. */
351     private ApplicationInfoCache mApplicationInfoCache = new ApplicationInfoCache();
352 
353     /**
354      * Indicates whether or not this RemoteViews object is contained as a child of any other
355      * RemoteViews.
356      */
357     private boolean mIsRoot = true;
358 
359     /**
360      * Constants to whether or not this RemoteViews is composed of a landscape and portrait
361      * RemoteViews.
362      */
363     private static final int MODE_NORMAL = 0;
364     private static final int MODE_HAS_LANDSCAPE_AND_PORTRAIT = 1;
365     private static final int MODE_HAS_SIZED_REMOTEVIEWS = 2;
366 
367     /**
368      * Used in conjunction with the special constructor
369      * {@link #RemoteViews(RemoteViews, RemoteViews)} to keep track of the landscape and portrait
370      * RemoteViews.
371      */
372     private RemoteViews mLandscape = null;
373     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
374     private RemoteViews mPortrait = null;
375     /**
376      * List of RemoteViews with their ideal size. There must be at least two if the map is not null.
377      *
378      * The smallest remote view is always the last element in the list.
379      */
380     private List<RemoteViews> mSizedRemoteViews = null;
381 
382     /**
383      * Ideal size for this RemoteViews.
384      *
385      * Only to be used on children views used in a {@link RemoteViews} with
386      * {@link RemoteViews#hasSizedRemoteViews()}.
387      */
388     private SizeF mIdealSize = null;
389 
390     @ApplyFlags
391     private int mApplyFlags = 0;
392 
393     /**
394      * Id to use to override the ID of the top-level view in this RemoteViews.
395      *
396      * Only used if this RemoteViews is defined from a XML layout value.
397      */
398     private int mViewId = View.NO_ID;
399 
400     /**
401      * Id used to uniquely identify a {@link RemoteViews} instance coming from a given provider.
402      */
403     private long mProviderInstanceId = -1;
404 
405     /** Class cookies of the Parcel this instance was read from. */
406     private Map<Class, Object> mClassCookies;
407 
408     private static final InteractionHandler DEFAULT_INTERACTION_HANDLER =
409             (view, pendingIntent, response) ->
410                     startPendingIntent(view, pendingIntent, response.getLaunchOptions(view));
411 
412     private static final ArrayMap<MethodKey, MethodArgs> sMethods = new ArrayMap<>();
413 
414     /**
415      * This key is used to perform lookups in sMethods without causing allocations.
416      */
417     private static final MethodKey sLookupKey = new MethodKey();
418 
419     /**
420      * @hide
421      */
setRemoteInputs(@dRes int viewId, RemoteInput[] remoteInputs)422     public void setRemoteInputs(@IdRes int viewId, RemoteInput[] remoteInputs) {
423         mActions.add(new SetRemoteInputsAction(viewId, remoteInputs));
424     }
425 
426     /**
427      * Reduces all images and ensures that they are all below the given sizes.
428      *
429      * @param maxWidth the maximum width allowed
430      * @param maxHeight the maximum height allowed
431      *
432      * @hide
433      */
reduceImageSizes(int maxWidth, int maxHeight)434     public void reduceImageSizes(int maxWidth, int maxHeight) {
435         ArrayList<Bitmap> cache = mBitmapCache.mBitmaps;
436         for (int i = 0; i < cache.size(); i++) {
437             Bitmap bitmap = cache.get(i);
438             cache.set(i, Icon.scaleDownIfNecessary(bitmap, maxWidth, maxHeight));
439         }
440     }
441 
442     /**
443      * Override all text colors in this layout and replace them by the given text color.
444      *
445      * @param textColor The color to use.
446      *
447      * @hide
448      */
overrideTextColors(int textColor)449     public void overrideTextColors(int textColor) {
450         addAction(new OverrideTextColorsAction(textColor));
451     }
452 
453     /**
454      * Sets an integer tag to the view.
455      *
456      * @hide
457      */
setIntTag(@dRes int viewId, @IdRes int key, int tag)458     public void setIntTag(@IdRes int viewId, @IdRes int key, int tag) {
459         addAction(new SetIntTagAction(viewId, key, tag));
460     }
461 
462     /**
463      * Set that it is disallowed to reapply another remoteview with the same layout as this view.
464      * This should be done if an action is destroying the view tree of the base layout.
465      *
466      * @hide
467      */
addFlags(@pplyFlags int flags)468     public void addFlags(@ApplyFlags int flags) {
469         mApplyFlags = mApplyFlags | flags;
470     }
471 
472     /**
473      * @hide
474      */
hasFlags(@pplyFlags int flag)475     public boolean hasFlags(@ApplyFlags int flag) {
476         return (mApplyFlags & flag) == flag;
477     }
478 
479     /**
480      * Stores information related to reflection method lookup.
481      */
482     static class MethodKey {
483         public Class targetClass;
484         public Class paramClass;
485         public String methodName;
486 
487         @Override
equals(@ullable Object o)488         public boolean equals(@Nullable Object o) {
489             if (!(o instanceof MethodKey)) {
490                 return false;
491             }
492             MethodKey p = (MethodKey) o;
493             return Objects.equals(p.targetClass, targetClass)
494                     && Objects.equals(p.paramClass, paramClass)
495                     && Objects.equals(p.methodName, methodName);
496         }
497 
498         @Override
hashCode()499         public int hashCode() {
500             return Objects.hashCode(targetClass) ^ Objects.hashCode(paramClass)
501                     ^ Objects.hashCode(methodName);
502         }
503 
set(Class targetClass, Class paramClass, String methodName)504         public void set(Class targetClass, Class paramClass, String methodName) {
505             this.targetClass = targetClass;
506             this.paramClass = paramClass;
507             this.methodName = methodName;
508         }
509     }
510 
511 
512     /**
513      * Stores information related to reflection method lookup result.
514      */
515     static class MethodArgs {
516         public MethodHandle syncMethod;
517         public MethodHandle asyncMethod;
518         public String asyncMethodName;
519     }
520 
521     /**
522      * This annotation indicates that a subclass of View is allowed to be used
523      * with the {@link RemoteViews} mechanism.
524      */
525     @Target({ ElementType.TYPE })
526     @Retention(RetentionPolicy.RUNTIME)
527     public @interface RemoteView {
528     }
529 
530     /**
531      * Exception to send when something goes wrong executing an action
532      *
533      */
534     public static class ActionException extends RuntimeException {
ActionException(Exception ex)535         public ActionException(Exception ex) {
536             super(ex);
537         }
ActionException(String message)538         public ActionException(String message) {
539             super(message);
540         }
541         /**
542          * @hide
543          */
ActionException(Throwable t)544         public ActionException(Throwable t) {
545             super(t);
546         }
547     }
548 
549     /**
550      * Handler for view interactions (such as clicks) within a RemoteViews.
551      *
552      * @hide
553      */
554     public interface InteractionHandler {
555         /**
556          * Invoked when the user performs an interaction on the View.
557          *
558          * @param view the View with which the user interacted
559          * @param pendingIntent the base PendingIntent associated with the view
560          * @param response the response to the interaction, which knows how to fill in the
561          *                 attached PendingIntent
562          *
563          * @hide
564          */
onInteraction( View view, PendingIntent pendingIntent, RemoteResponse response)565         boolean onInteraction(
566                 View view,
567                 PendingIntent pendingIntent,
568                 RemoteResponse response);
569     }
570 
571     /**
572      * Base class for all actions that can be performed on an
573      * inflated view.
574      *
575      *  SUBCLASSES MUST BE IMMUTABLE SO CLONE WORKS!!!!!
576      */
577     private abstract static class Action implements Parcelable {
apply(View root, ViewGroup rootParent, InteractionHandler handler, ColorResources colorResources)578         public abstract void apply(View root, ViewGroup rootParent, InteractionHandler handler,
579                 ColorResources colorResources) throws ActionException;
580 
581         public static final int MERGE_REPLACE = 0;
582         public static final int MERGE_APPEND = 1;
583         public static final int MERGE_IGNORE = 2;
584 
describeContents()585         public int describeContents() {
586             return 0;
587         }
588 
setHierarchyRootData(HierarchyRootData root)589         public void setHierarchyRootData(HierarchyRootData root) {
590             // Do nothing
591         }
592 
593         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
mergeBehavior()594         public int mergeBehavior() {
595             return MERGE_REPLACE;
596         }
597 
getActionTag()598         public abstract int getActionTag();
599 
getUniqueKey()600         public String getUniqueKey() {
601             return (getActionTag() + "_" + viewId);
602         }
603 
604         /**
605          * This is called on the background thread. It should perform any non-ui computations
606          * and return the final action which will run on the UI thread.
607          * Override this if some of the tasks can be performed async.
608          */
initActionAsync(ViewTree root, ViewGroup rootParent, InteractionHandler handler, ColorResources colorResources)609         public Action initActionAsync(ViewTree root, ViewGroup rootParent,
610                 InteractionHandler handler, ColorResources colorResources) {
611             return this;
612         }
613 
prefersAsyncApply()614         public boolean prefersAsyncApply() {
615             return false;
616         }
617 
visitUris(@onNull Consumer<Uri> visitor)618         public void visitUris(@NonNull Consumer<Uri> visitor) {
619             // Nothing to visit by default
620         }
621 
622         @IdRes
623         @UnsupportedAppUsage
624         int viewId;
625     }
626 
627     /**
628      * Action class used during async inflation of RemoteViews. Subclasses are not parcelable.
629      */
630     private static abstract class RuntimeAction extends Action {
631         @Override
getActionTag()632         public final int getActionTag() {
633             return 0;
634         }
635 
636         @Override
writeToParcel(Parcel dest, int flags)637         public final void writeToParcel(Parcel dest, int flags) {
638             throw new UnsupportedOperationException();
639         }
640     }
641 
642     // Constant used during async execution. It is not parcelable.
643     private static final Action ACTION_NOOP = new RuntimeAction() {
644         @Override
645         public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
646                 ColorResources colorResources) {
647         }
648     };
649 
650     /**
651      * Merges the passed RemoteViews actions with this RemoteViews actions according to
652      * action-specific merge rules.
653      *
654      * @param newRv
655      *
656      * @hide
657      */
658     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
mergeRemoteViews(RemoteViews newRv)659     public void mergeRemoteViews(RemoteViews newRv) {
660         if (newRv == null) return;
661         // We first copy the new RemoteViews, as the process of merging modifies the way the actions
662         // reference the bitmap cache. We don't want to modify the object as it may need to
663         // be merged and applied multiple times.
664         RemoteViews copy = new RemoteViews(newRv);
665 
666         HashMap<String, Action> map = new HashMap<String, Action>();
667         if (mActions == null) {
668             mActions = new ArrayList<Action>();
669         }
670 
671         int count = mActions.size();
672         for (int i = 0; i < count; i++) {
673             Action a = mActions.get(i);
674             map.put(a.getUniqueKey(), a);
675         }
676 
677         ArrayList<Action> newActions = copy.mActions;
678         if (newActions == null) return;
679         count = newActions.size();
680         for (int i = 0; i < count; i++) {
681             Action a = newActions.get(i);
682             String key = newActions.get(i).getUniqueKey();
683             int mergeBehavior = newActions.get(i).mergeBehavior();
684             if (map.containsKey(key) && mergeBehavior == Action.MERGE_REPLACE) {
685                 mActions.remove(map.get(key));
686                 map.remove(key);
687             }
688 
689             // If the merge behavior is ignore, we don't bother keeping the extra action
690             if (mergeBehavior == Action.MERGE_REPLACE || mergeBehavior == Action.MERGE_APPEND) {
691                 mActions.add(a);
692             }
693         }
694 
695         // Because pruning can remove the need for bitmaps, we reconstruct the caches.
696         reconstructCaches();
697     }
698 
699     /**
700      * Note all {@link Uri} that are referenced internally, with the expectation
701      * that Uri permission grants will need to be issued to ensure the recipient
702      * of this object is able to render its contents.
703      *
704      * @hide
705      */
visitUris(@onNull Consumer<Uri> visitor)706     public void visitUris(@NonNull Consumer<Uri> visitor) {
707         if (mActions != null) {
708             for (int i = 0; i < mActions.size(); i++) {
709                 mActions.get(i).visitUris(visitor);
710             }
711         }
712     }
713 
visitIconUri(Icon icon, @NonNull Consumer<Uri> visitor)714     private static void visitIconUri(Icon icon, @NonNull Consumer<Uri> visitor) {
715         if (icon != null && (icon.getType() == Icon.TYPE_URI
716                 || icon.getType() == Icon.TYPE_URI_ADAPTIVE_BITMAP)) {
717             visitor.accept(icon.getUri());
718         }
719     }
720 
721     private static class RemoteViewsContextWrapper extends ContextWrapper {
722         private final Context mContextForResources;
723 
RemoteViewsContextWrapper(Context context, Context contextForResources)724         RemoteViewsContextWrapper(Context context, Context contextForResources) {
725             super(context);
726             mContextForResources = contextForResources;
727         }
728 
729         @Override
getResources()730         public Resources getResources() {
731             return mContextForResources.getResources();
732         }
733 
734         @Override
getTheme()735         public Resources.Theme getTheme() {
736             return mContextForResources.getTheme();
737         }
738 
739         @Override
getPackageName()740         public String getPackageName() {
741             return mContextForResources.getPackageName();
742         }
743 
744         @Override
getUser()745         public UserHandle getUser() {
746             return mContextForResources.getUser();
747         }
748 
749         @Override
getUserId()750         public int getUserId() {
751             return mContextForResources.getUserId();
752         }
753 
754         @Override
isRestricted()755         public boolean isRestricted() {
756             // Override isRestricted and direct to resource's implementation. The isRestricted is
757             // used for determining the risky resources loading, e.g. fonts, thus direct to context
758             // for resource.
759             return mContextForResources.isRestricted();
760         }
761     }
762 
763     private class SetEmptyView extends Action {
764         int emptyViewId;
765 
SetEmptyView(@dRes int viewId, @IdRes int emptyViewId)766         SetEmptyView(@IdRes int viewId, @IdRes int emptyViewId) {
767             this.viewId = viewId;
768             this.emptyViewId = emptyViewId;
769         }
770 
SetEmptyView(Parcel in)771         SetEmptyView(Parcel in) {
772             this.viewId = in.readInt();
773             this.emptyViewId = in.readInt();
774         }
775 
writeToParcel(Parcel out, int flags)776         public void writeToParcel(Parcel out, int flags) {
777             out.writeInt(this.viewId);
778             out.writeInt(this.emptyViewId);
779         }
780 
781         @Override
apply(View root, ViewGroup rootParent, InteractionHandler handler, ColorResources colorResources)782         public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
783                 ColorResources colorResources) {
784             final View view = root.findViewById(viewId);
785             if (!(view instanceof AdapterView<?>)) return;
786 
787             AdapterView<?> adapterView = (AdapterView<?>) view;
788 
789             final View emptyView = root.findViewById(emptyViewId);
790             if (emptyView == null) return;
791 
792             adapterView.setEmptyView(emptyView);
793         }
794 
795         @Override
getActionTag()796         public int getActionTag() {
797             return SET_EMPTY_VIEW_ACTION_TAG;
798         }
799     }
800 
801     private class SetPendingIntentTemplate extends Action {
SetPendingIntentTemplate(@dRes int id, PendingIntent pendingIntentTemplate)802         public SetPendingIntentTemplate(@IdRes int id, PendingIntent pendingIntentTemplate) {
803             this.viewId = id;
804             this.pendingIntentTemplate = pendingIntentTemplate;
805         }
806 
SetPendingIntentTemplate(Parcel parcel)807         public SetPendingIntentTemplate(Parcel parcel) {
808             viewId = parcel.readInt();
809             pendingIntentTemplate = PendingIntent.readPendingIntentOrNullFromParcel(parcel);
810         }
811 
writeToParcel(Parcel dest, int flags)812         public void writeToParcel(Parcel dest, int flags) {
813             dest.writeInt(viewId);
814             PendingIntent.writePendingIntentOrNullToParcel(pendingIntentTemplate, dest);
815         }
816 
817         @Override
apply(View root, ViewGroup rootParent, final InteractionHandler handler, ColorResources colorResources)818         public void apply(View root, ViewGroup rootParent, final InteractionHandler handler,
819                 ColorResources colorResources) {
820             final View target = root.findViewById(viewId);
821             if (target == null) return;
822 
823             // If the view isn't an AdapterView, setting a PendingIntent template doesn't make sense
824             if (target instanceof AdapterView<?>) {
825                 AdapterView<?> av = (AdapterView<?>) target;
826                 // The PendingIntent template is stored in the view's tag.
827                 OnItemClickListener listener = (parent, view, position, id) -> {
828                     RemoteResponse response = findRemoteResponseTag(view);
829                     if (response != null) {
830                         response.handleViewInteraction(view, handler);
831                     }
832                 };
833                 av.setOnItemClickListener(listener);
834                 av.setTag(pendingIntentTemplate);
835             } else {
836                 Log.e(LOG_TAG, "Cannot setPendingIntentTemplate on a view which is not" +
837                         "an AdapterView (id: " + viewId + ")");
838                 return;
839             }
840         }
841 
842         @Nullable
findRemoteResponseTag(@ullable View rootView)843         private RemoteResponse findRemoteResponseTag(@Nullable View rootView) {
844             if (rootView == null) return null;
845 
846             ArrayDeque<View> viewsToCheck = new ArrayDeque<>();
847             viewsToCheck.addLast(rootView);
848 
849             while (!viewsToCheck.isEmpty()) {
850                 View view = viewsToCheck.removeFirst();
851                 Object tag = view.getTag(R.id.fillInIntent);
852                 if (tag instanceof RemoteResponse) return (RemoteResponse) tag;
853                 if (!(view instanceof ViewGroup)) continue;
854 
855                 ViewGroup viewGroup = (ViewGroup) view;
856                 for (int i = 0; i < viewGroup.getChildCount(); i++) {
857                     viewsToCheck.addLast(viewGroup.getChildAt(i));
858                 }
859             }
860 
861             return null;
862         }
863 
864         @Override
getActionTag()865         public int getActionTag() {
866             return SET_PENDING_INTENT_TEMPLATE_TAG;
867         }
868 
869         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
870         PendingIntent pendingIntentTemplate;
871     }
872 
873     private class SetRemoteViewsAdapterList extends Action {
SetRemoteViewsAdapterList(@dRes int id, ArrayList<RemoteViews> list, int viewTypeCount)874         public SetRemoteViewsAdapterList(@IdRes int id, ArrayList<RemoteViews> list,
875                 int viewTypeCount) {
876             this.viewId = id;
877             this.list = list;
878             this.viewTypeCount = viewTypeCount;
879         }
880 
SetRemoteViewsAdapterList(Parcel parcel)881         public SetRemoteViewsAdapterList(Parcel parcel) {
882             viewId = parcel.readInt();
883             viewTypeCount = parcel.readInt();
884             list = parcel.createTypedArrayList(RemoteViews.CREATOR);
885         }
886 
writeToParcel(Parcel dest, int flags)887         public void writeToParcel(Parcel dest, int flags) {
888             dest.writeInt(viewId);
889             dest.writeInt(viewTypeCount);
890             dest.writeTypedList(list, flags);
891         }
892 
893         @Override
apply(View root, ViewGroup rootParent, InteractionHandler handler, ColorResources colorResources)894         public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
895                 ColorResources colorResources) {
896             final View target = root.findViewById(viewId);
897             if (target == null) return;
898 
899             // Ensure that we are applying to an AppWidget root
900             if (!(rootParent instanceof AppWidgetHostView)) {
901                 Log.e(LOG_TAG, "SetRemoteViewsAdapterIntent action can only be used for " +
902                         "AppWidgets (root id: " + viewId + ")");
903                 return;
904             }
905             // Ensure that we are calling setRemoteAdapter on an AdapterView that supports it
906             if (!(target instanceof AbsListView) && !(target instanceof AdapterViewAnimator)) {
907                 Log.e(LOG_TAG, "Cannot setRemoteViewsAdapter on a view which is not " +
908                         "an AbsListView or AdapterViewAnimator (id: " + viewId + ")");
909                 return;
910             }
911 
912             if (target instanceof AbsListView) {
913                 AbsListView v = (AbsListView) target;
914                 Adapter a = v.getAdapter();
915                 if (a instanceof RemoteViewsListAdapter && viewTypeCount <= a.getViewTypeCount()) {
916                     ((RemoteViewsListAdapter) a).setViewsList(list);
917                 } else {
918                     v.setAdapter(new RemoteViewsListAdapter(v.getContext(), list, viewTypeCount,
919                             colorResources));
920                 }
921             } else if (target instanceof AdapterViewAnimator) {
922                 AdapterViewAnimator v = (AdapterViewAnimator) target;
923                 Adapter a = v.getAdapter();
924                 if (a instanceof RemoteViewsListAdapter && viewTypeCount <= a.getViewTypeCount()) {
925                     ((RemoteViewsListAdapter) a).setViewsList(list);
926                 } else {
927                     v.setAdapter(new RemoteViewsListAdapter(v.getContext(), list, viewTypeCount,
928                             colorResources));
929                 }
930             }
931         }
932 
933         @Override
getActionTag()934         public int getActionTag() {
935             return SET_REMOTE_VIEW_ADAPTER_LIST_TAG;
936         }
937 
938         int viewTypeCount;
939         ArrayList<RemoteViews> list;
940     }
941 
942     /**
943      * Cache of {@link ApplicationInfo}s that can be used to ensure that the same
944      * {@link ApplicationInfo} instance is used throughout the RemoteViews.
945      */
946     private static class ApplicationInfoCache {
947         private final Map<Pair<String, Integer>, ApplicationInfo> mPackageUserToApplicationInfo;
948 
ApplicationInfoCache()949         ApplicationInfoCache() {
950             mPackageUserToApplicationInfo = new ArrayMap<>();
951         }
952 
953         /**
954          * Adds the {@link ApplicationInfo} to the cache if it's not present. Returns either the
955          * provided {@code applicationInfo} or a previously added value with the same package name
956          * and uid.
957          */
958         @Nullable
getOrPut(@ullable ApplicationInfo applicationInfo)959         ApplicationInfo getOrPut(@Nullable ApplicationInfo applicationInfo) {
960             Pair<String, Integer> key = getPackageUserKey(applicationInfo);
961             if (key == null) return null;
962             return mPackageUserToApplicationInfo.computeIfAbsent(key, ignored -> applicationInfo);
963         }
964 
965         /** Puts the {@link ApplicationInfo} in the cache, replacing any previously stored value. */
put(@ullable ApplicationInfo applicationInfo)966         void put(@Nullable ApplicationInfo applicationInfo) {
967             Pair<String, Integer> key = getPackageUserKey(applicationInfo);
968             if (key == null) return;
969             mPackageUserToApplicationInfo.put(key, applicationInfo);
970         }
971 
972         /**
973          * Returns the currently stored {@link ApplicationInfo} from the cache matching
974          * {@code  applicationInfo}, or null if there wasn't any.
975          */
get(@ullable ApplicationInfo applicationInfo)976         @Nullable ApplicationInfo get(@Nullable ApplicationInfo applicationInfo) {
977             Pair<String, Integer> key = getPackageUserKey(applicationInfo);
978             if (key == null) return null;
979             return mPackageUserToApplicationInfo.get(key);
980         }
981     }
982 
983     private class SetRemoteCollectionItemListAdapterAction extends Action {
984         private final RemoteCollectionItems mItems;
985 
SetRemoteCollectionItemListAdapterAction(@dRes int id, RemoteCollectionItems items)986         SetRemoteCollectionItemListAdapterAction(@IdRes int id, RemoteCollectionItems items) {
987             viewId = id;
988             mItems = items;
989             mItems.setHierarchyRootData(getHierarchyRootData());
990         }
991 
SetRemoteCollectionItemListAdapterAction(Parcel parcel)992         SetRemoteCollectionItemListAdapterAction(Parcel parcel) {
993             viewId = parcel.readInt();
994             mItems = new RemoteCollectionItems(parcel, getHierarchyRootData());
995         }
996 
997         @Override
setHierarchyRootData(HierarchyRootData rootData)998         public void setHierarchyRootData(HierarchyRootData rootData) {
999             mItems.setHierarchyRootData(rootData);
1000         }
1001 
1002         @Override
writeToParcel(Parcel dest, int flags)1003         public void writeToParcel(Parcel dest, int flags) {
1004             dest.writeInt(viewId);
1005             mItems.writeToParcel(dest, flags, /* attached= */ true);
1006         }
1007 
1008         @Override
apply(View root, ViewGroup rootParent, InteractionHandler handler, ColorResources colorResources)1009         public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
1010                 ColorResources colorResources) throws ActionException {
1011             View target = root.findViewById(viewId);
1012             if (target == null) return;
1013 
1014             // Ensure that we are applying to an AppWidget root
1015             if (!(rootParent instanceof AppWidgetHostView)) {
1016                 Log.e(LOG_TAG, "setRemoteAdapter can only be used for "
1017                         + "AppWidgets (root id: " + viewId + ")");
1018                 return;
1019             }
1020 
1021             if (!(target instanceof AdapterView)) {
1022                 Log.e(LOG_TAG, "Cannot call setRemoteAdapter on a view which is not "
1023                         + "an AdapterView (id: " + viewId + ")");
1024                 return;
1025             }
1026 
1027             AdapterView adapterView = (AdapterView) target;
1028             Adapter adapter = adapterView.getAdapter();
1029             // We can reuse the adapter if it's a RemoteCollectionItemsAdapter and the view type
1030             // count hasn't increased. Note that AbsListView allocates a fixed size array for view
1031             // recycling in setAdapter, so we must call setAdapter again if the number of view types
1032             // increases.
1033             if (adapter instanceof RemoteCollectionItemsAdapter
1034                     && adapter.getViewTypeCount() >= mItems.getViewTypeCount()) {
1035                 try {
1036                     ((RemoteCollectionItemsAdapter) adapter).setData(
1037                             mItems, handler, colorResources);
1038                 } catch (Throwable throwable) {
1039                     // setData should never failed with the validation in the items builder, but if
1040                     // it does, catch and rethrow.
1041                     throw new ActionException(throwable);
1042                 }
1043                 return;
1044             }
1045 
1046             try {
1047                 adapterView.setAdapter(
1048                         new RemoteCollectionItemsAdapter(mItems, handler, colorResources));
1049             } catch (Throwable throwable) {
1050                 // This could throw if the AdapterView somehow doesn't accept BaseAdapter due to
1051                 // a type error.
1052                 throw new ActionException(throwable);
1053             }
1054         }
1055 
1056         @Override
getActionTag()1057         public int getActionTag() {
1058             return SET_REMOTE_COLLECTION_ITEMS_ADAPTER_TAG;
1059         }
1060     }
1061 
1062     private class SetRemoteViewsAdapterIntent extends Action {
SetRemoteViewsAdapterIntent(@dRes int id, Intent intent)1063         public SetRemoteViewsAdapterIntent(@IdRes int id, Intent intent) {
1064             this.viewId = id;
1065             this.intent = intent;
1066         }
1067 
SetRemoteViewsAdapterIntent(Parcel parcel)1068         public SetRemoteViewsAdapterIntent(Parcel parcel) {
1069             viewId = parcel.readInt();
1070             intent = parcel.readTypedObject(Intent.CREATOR);
1071         }
1072 
writeToParcel(Parcel dest, int flags)1073         public void writeToParcel(Parcel dest, int flags) {
1074             dest.writeInt(viewId);
1075             dest.writeTypedObject(intent, flags);
1076         }
1077 
1078         @Override
apply(View root, ViewGroup rootParent, InteractionHandler handler, ColorResources colorResources)1079         public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
1080                 ColorResources colorResources) {
1081             final View target = root.findViewById(viewId);
1082             if (target == null) return;
1083 
1084             // Ensure that we are applying to an AppWidget root
1085             if (!(rootParent instanceof AppWidgetHostView)) {
1086                 Log.e(LOG_TAG, "setRemoteAdapter can only be used for "
1087                         + "AppWidgets (root id: " + viewId + ")");
1088                 return;
1089             }
1090 
1091             // Ensure that we are calling setRemoteAdapter on an AdapterView that supports it
1092             if (!(target instanceof AbsListView) && !(target instanceof AdapterViewAnimator)) {
1093                 Log.e(LOG_TAG, "Cannot setRemoteAdapter on a view which is not "
1094                         + "an AbsListView or AdapterViewAnimator (id: " + viewId + ")");
1095                 return;
1096             }
1097 
1098             // Embed the AppWidget Id for use in RemoteViewsAdapter when connecting to the intent
1099             // RemoteViewsService
1100             AppWidgetHostView host = (AppWidgetHostView) rootParent;
1101             intent.putExtra(EXTRA_REMOTEADAPTER_APPWIDGET_ID, host.getAppWidgetId())
1102                     .putExtra(EXTRA_REMOTEADAPTER_ON_LIGHT_BACKGROUND,
1103                             hasFlags(FLAG_USE_LIGHT_BACKGROUND_LAYOUT));
1104 
1105             if (target instanceof AbsListView) {
1106                 AbsListView v = (AbsListView) target;
1107                 v.setRemoteViewsAdapter(intent, isAsync);
1108                 v.setRemoteViewsInteractionHandler(handler);
1109             } else if (target instanceof AdapterViewAnimator) {
1110                 AdapterViewAnimator v = (AdapterViewAnimator) target;
1111                 v.setRemoteViewsAdapter(intent, isAsync);
1112                 v.setRemoteViewsOnClickHandler(handler);
1113             }
1114         }
1115 
1116         @Override
initActionAsync(ViewTree root, ViewGroup rootParent, InteractionHandler handler, ColorResources colorResources)1117         public Action initActionAsync(ViewTree root, ViewGroup rootParent,
1118                 InteractionHandler handler, ColorResources colorResources) {
1119             SetRemoteViewsAdapterIntent copy = new SetRemoteViewsAdapterIntent(viewId, intent);
1120             copy.isAsync = true;
1121             return copy;
1122         }
1123 
1124         @Override
getActionTag()1125         public int getActionTag() {
1126             return SET_REMOTE_VIEW_ADAPTER_INTENT_TAG;
1127         }
1128 
1129         Intent intent;
1130         boolean isAsync = false;
1131     }
1132 
1133     /**
1134      * Equivalent to calling
1135      * {@link android.view.View#setOnClickListener(android.view.View.OnClickListener)}
1136      * to launch the provided {@link PendingIntent}.
1137      */
1138     private class SetOnClickResponse extends Action {
1139 
SetOnClickResponse(@dRes int id, RemoteResponse response)1140         SetOnClickResponse(@IdRes int id, RemoteResponse response) {
1141             this.viewId = id;
1142             this.mResponse = response;
1143         }
1144 
SetOnClickResponse(Parcel parcel)1145         SetOnClickResponse(Parcel parcel) {
1146             viewId = parcel.readInt();
1147             mResponse = new RemoteResponse();
1148             mResponse.readFromParcel(parcel);
1149         }
1150 
writeToParcel(Parcel dest, int flags)1151         public void writeToParcel(Parcel dest, int flags) {
1152             dest.writeInt(viewId);
1153             mResponse.writeToParcel(dest, flags);
1154         }
1155 
1156         @Override
apply(View root, ViewGroup rootParent, final InteractionHandler handler, ColorResources colorResources)1157         public void apply(View root, ViewGroup rootParent, final InteractionHandler handler,
1158                 ColorResources colorResources) {
1159             final View target = root.findViewById(viewId);
1160             if (target == null) return;
1161 
1162             if (mResponse.mPendingIntent != null) {
1163                 // If the view is an AdapterView, setting a PendingIntent on click doesn't make
1164                 // much sense, do they mean to set a PendingIntent template for the
1165                 // AdapterView's children?
1166                 if (hasFlags(FLAG_WIDGET_IS_COLLECTION_CHILD)) {
1167                     Log.w(LOG_TAG, "Cannot SetOnClickResponse for collection item "
1168                             + "(id: " + viewId + ")");
1169                     ApplicationInfo appInfo = root.getContext().getApplicationInfo();
1170 
1171                     // We let this slide for HC and ICS so as to not break compatibility. It should
1172                     // have been disabled from the outset, but was left open by accident.
1173                     if (appInfo != null
1174                             && appInfo.targetSdkVersion >= Build.VERSION_CODES.JELLY_BEAN) {
1175                         return;
1176                     }
1177                 }
1178                 target.setTagInternal(R.id.pending_intent_tag, mResponse.mPendingIntent);
1179             } else if (mResponse.mFillIntent != null) {
1180                 if (!hasFlags(FLAG_WIDGET_IS_COLLECTION_CHILD)) {
1181                     Log.e(LOG_TAG, "The method setOnClickFillInIntent is available "
1182                             + "only from RemoteViewsFactory (ie. on collection items).");
1183                     return;
1184                 }
1185                 if (target == root) {
1186                     // Target is a root node of an AdapterView child. Set the response in the tag.
1187                     // Actual click handling is done by OnItemClickListener in
1188                     // SetPendingIntentTemplate, which uses this tag information.
1189                     target.setTagInternal(com.android.internal.R.id.fillInIntent, mResponse);
1190                     return;
1191                 }
1192             } else {
1193                 // No intent to apply, clear the listener and any tags that were previously set.
1194                 target.setOnClickListener(null);
1195                 target.setTagInternal(R.id.pending_intent_tag, null);
1196                 target.setTagInternal(com.android.internal.R.id.fillInIntent, null);
1197                 return;
1198             }
1199             target.setOnClickListener(v -> mResponse.handleViewInteraction(v, handler));
1200         }
1201 
1202         @Override
getActionTag()1203         public int getActionTag() {
1204             return SET_ON_CLICK_RESPONSE_TAG;
1205         }
1206 
1207         final RemoteResponse mResponse;
1208     }
1209 
1210     /**
1211      * Equivalent to calling
1212      * {@link android.widget.CompoundButton#setOnCheckedChangeListener(
1213      * android.widget.CompoundButton.OnCheckedChangeListener)}
1214      * to launch the provided {@link PendingIntent}.
1215      */
1216     private class SetOnCheckedChangeResponse extends Action {
1217 
1218         private final RemoteResponse mResponse;
1219 
SetOnCheckedChangeResponse(@dRes int id, RemoteResponse response)1220         SetOnCheckedChangeResponse(@IdRes int id, RemoteResponse response) {
1221             this.viewId = id;
1222             this.mResponse = response;
1223         }
1224 
SetOnCheckedChangeResponse(Parcel parcel)1225         SetOnCheckedChangeResponse(Parcel parcel) {
1226             viewId = parcel.readInt();
1227             mResponse = new RemoteResponse();
1228             mResponse.readFromParcel(parcel);
1229         }
1230 
writeToParcel(Parcel dest, int flags)1231         public void writeToParcel(Parcel dest, int flags) {
1232             dest.writeInt(viewId);
1233             mResponse.writeToParcel(dest, flags);
1234         }
1235 
1236         @Override
apply(View root, ViewGroup rootParent, final InteractionHandler handler, ColorResources colorResources)1237         public void apply(View root, ViewGroup rootParent, final InteractionHandler handler,
1238                 ColorResources colorResources) {
1239             final View target = root.findViewById(viewId);
1240             if (target == null) return;
1241             if (!(target instanceof CompoundButton)) {
1242                 Log.w(LOG_TAG, "setOnCheckedChange methods cannot be used on "
1243                         + "non-CompoundButton child (id: " + viewId + ")");
1244                 return;
1245             }
1246             CompoundButton button = (CompoundButton) target;
1247 
1248             if (mResponse.mPendingIntent != null) {
1249                 // setOnCheckedChangePendingIntent cannot be used with collection children, which
1250                 // must use setOnCheckedChangeFillInIntent instead.
1251                 if (hasFlags(FLAG_WIDGET_IS_COLLECTION_CHILD)) {
1252                     Log.w(LOG_TAG, "Cannot setOnCheckedChangePendingIntent for collection item "
1253                             + "(id: " + viewId + ")");
1254                     return;
1255                 }
1256                 target.setTagInternal(R.id.pending_intent_tag, mResponse.mPendingIntent);
1257             } else if (mResponse.mFillIntent != null) {
1258                 if (!hasFlags(FLAG_WIDGET_IS_COLLECTION_CHILD)) {
1259                     Log.e(LOG_TAG, "The method setOnCheckedChangeFillInIntent is available "
1260                             + "only from RemoteViewsFactory (ie. on collection items).");
1261                     return;
1262                 }
1263             } else {
1264                 // No intent to apply, clear any existing listener or tag.
1265                 button.setOnCheckedChangeListener(null);
1266                 button.setTagInternal(R.id.remote_checked_change_listener_tag, null);
1267                 return;
1268             }
1269 
1270             OnCheckedChangeListener onCheckedChangeListener =
1271                     (v, isChecked) -> mResponse.handleViewInteraction(v, handler);
1272             button.setTagInternal(R.id.remote_checked_change_listener_tag, onCheckedChangeListener);
1273             button.setOnCheckedChangeListener(onCheckedChangeListener);
1274         }
1275 
1276         @Override
getActionTag()1277         public int getActionTag() {
1278             return SET_ON_CHECKED_CHANGE_RESPONSE_TAG;
1279         }
1280     }
1281 
1282     /** @hide **/
getSourceBounds(View v)1283     public static Rect getSourceBounds(View v) {
1284         final float appScale = v.getContext().getResources()
1285                 .getCompatibilityInfo().applicationScale;
1286         final int[] pos = new int[2];
1287         v.getLocationOnScreen(pos);
1288 
1289         final Rect rect = new Rect();
1290         rect.left = (int) (pos[0] * appScale + 0.5f);
1291         rect.top = (int) (pos[1] * appScale + 0.5f);
1292         rect.right = (int) ((pos[0] + v.getWidth()) * appScale + 0.5f);
1293         rect.bottom = (int) ((pos[1] + v.getHeight()) * appScale + 0.5f);
1294         return rect;
1295     }
1296 
1297     @Nullable
getParameterType(int type)1298     private static Class<?> getParameterType(int type) {
1299         switch (type) {
1300             case BaseReflectionAction.BOOLEAN:
1301                 return boolean.class;
1302             case BaseReflectionAction.BYTE:
1303                 return byte.class;
1304             case BaseReflectionAction.SHORT:
1305                 return short.class;
1306             case BaseReflectionAction.INT:
1307                 return int.class;
1308             case BaseReflectionAction.LONG:
1309                 return long.class;
1310             case BaseReflectionAction.FLOAT:
1311                 return float.class;
1312             case BaseReflectionAction.DOUBLE:
1313                 return double.class;
1314             case BaseReflectionAction.CHAR:
1315                 return char.class;
1316             case BaseReflectionAction.STRING:
1317                 return String.class;
1318             case BaseReflectionAction.CHAR_SEQUENCE:
1319                 return CharSequence.class;
1320             case BaseReflectionAction.URI:
1321                 return Uri.class;
1322             case BaseReflectionAction.BITMAP:
1323                 return Bitmap.class;
1324             case BaseReflectionAction.BUNDLE:
1325                 return Bundle.class;
1326             case BaseReflectionAction.INTENT:
1327                 return Intent.class;
1328             case BaseReflectionAction.COLOR_STATE_LIST:
1329                 return ColorStateList.class;
1330             case BaseReflectionAction.ICON:
1331                 return Icon.class;
1332             case BaseReflectionAction.BLEND_MODE:
1333                 return BlendMode.class;
1334             default:
1335                 return null;
1336         }
1337     }
1338 
1339     @Nullable
getMethod(View view, String methodName, Class<?> paramType, boolean async)1340     private MethodHandle getMethod(View view, String methodName, Class<?> paramType,
1341             boolean async) {
1342         MethodArgs result;
1343         Class<? extends View> klass = view.getClass();
1344 
1345         synchronized (sMethods) {
1346             // The key is defined by the view class, param class and method name.
1347             sLookupKey.set(klass, paramType, methodName);
1348             result = sMethods.get(sLookupKey);
1349 
1350             if (result == null) {
1351                 Method method;
1352                 try {
1353                     if (paramType == null) {
1354                         method = klass.getMethod(methodName);
1355                     } else {
1356                         method = klass.getMethod(methodName, paramType);
1357                     }
1358                     if (!method.isAnnotationPresent(RemotableViewMethod.class)) {
1359                         throw new ActionException("view: " + klass.getName()
1360                                 + " can't use method with RemoteViews: "
1361                                 + methodName + getParameters(paramType));
1362                     }
1363 
1364                     result = new MethodArgs();
1365                     result.syncMethod = MethodHandles.publicLookup().unreflect(method);
1366                     result.asyncMethodName =
1367                             method.getAnnotation(RemotableViewMethod.class).asyncImpl();
1368                 } catch (NoSuchMethodException | IllegalAccessException ex) {
1369                     throw new ActionException("view: " + klass.getName() + " doesn't have method: "
1370                             + methodName + getParameters(paramType));
1371                 }
1372 
1373                 MethodKey key = new MethodKey();
1374                 key.set(klass, paramType, methodName);
1375                 sMethods.put(key, result);
1376             }
1377 
1378             if (!async) {
1379                 return result.syncMethod;
1380             }
1381             // Check this so see if async method is implemented or not.
1382             if (result.asyncMethodName.isEmpty()) {
1383                 return null;
1384             }
1385             // Async method is lazily loaded. If it is not yet loaded, load now.
1386             if (result.asyncMethod == null) {
1387                 MethodType asyncType = result.syncMethod.type()
1388                         .dropParameterTypes(0, 1).changeReturnType(Runnable.class);
1389                 try {
1390                     result.asyncMethod = MethodHandles.publicLookup().findVirtual(
1391                             klass, result.asyncMethodName, asyncType);
1392                 } catch (NoSuchMethodException | IllegalAccessException ex) {
1393                     throw new ActionException("Async implementation declared as "
1394                             + result.asyncMethodName + " but not defined for " + methodName
1395                             + ": public Runnable " + result.asyncMethodName + " ("
1396                             + TextUtils.join(",", asyncType.parameterArray()) + ")");
1397                 }
1398             }
1399             return result.asyncMethod;
1400         }
1401     }
1402 
getParameters(Class<?> paramType)1403     private static String getParameters(Class<?> paramType) {
1404         if (paramType == null) return "()";
1405         return "(" + paramType + ")";
1406     }
1407 
1408     /**
1409      * Equivalent to calling
1410      * {@link Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)},
1411      * on the {@link Drawable} of a given view.
1412      * <p>
1413      * The operation will be performed on the {@link Drawable} returned by the
1414      * target {@link View#getBackground()} by default.  If targetBackground is false,
1415      * we assume the target is an {@link ImageView} and try applying the operations
1416      * to {@link ImageView#getDrawable()}.
1417      * <p>
1418      */
1419     private class SetDrawableTint extends Action {
SetDrawableTint(@dRes int id, boolean targetBackground, @ColorInt int colorFilter, @NonNull PorterDuff.Mode mode)1420         SetDrawableTint(@IdRes int id, boolean targetBackground,
1421                 @ColorInt int colorFilter, @NonNull PorterDuff.Mode mode) {
1422             this.viewId = id;
1423             this.targetBackground = targetBackground;
1424             this.colorFilter = colorFilter;
1425             this.filterMode = mode;
1426         }
1427 
SetDrawableTint(Parcel parcel)1428         SetDrawableTint(Parcel parcel) {
1429             viewId = parcel.readInt();
1430             targetBackground = parcel.readInt() != 0;
1431             colorFilter = parcel.readInt();
1432             filterMode = PorterDuff.intToMode(parcel.readInt());
1433         }
1434 
writeToParcel(Parcel dest, int flags)1435         public void writeToParcel(Parcel dest, int flags) {
1436             dest.writeInt(viewId);
1437             dest.writeInt(targetBackground ? 1 : 0);
1438             dest.writeInt(colorFilter);
1439             dest.writeInt(PorterDuff.modeToInt(filterMode));
1440         }
1441 
1442         @Override
apply(View root, ViewGroup rootParent, InteractionHandler handler, ColorResources colorResources)1443         public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
1444                 ColorResources colorResources) {
1445             final View target = root.findViewById(viewId);
1446             if (target == null) return;
1447 
1448             // Pick the correct drawable to modify for this view
1449             Drawable targetDrawable = null;
1450             if (targetBackground) {
1451                 targetDrawable = target.getBackground();
1452             } else if (target instanceof ImageView) {
1453                 ImageView imageView = (ImageView) target;
1454                 targetDrawable = imageView.getDrawable();
1455             }
1456 
1457             if (targetDrawable != null) {
1458                 targetDrawable.mutate().setColorFilter(colorFilter, filterMode);
1459             }
1460         }
1461 
1462         @Override
getActionTag()1463         public int getActionTag() {
1464             return SET_DRAWABLE_TINT_TAG;
1465         }
1466 
1467         boolean targetBackground;
1468         @ColorInt int colorFilter;
1469         PorterDuff.Mode filterMode;
1470     }
1471 
1472     /**
1473      * Equivalent to calling
1474      * {@link RippleDrawable#setColor(ColorStateList)},
1475      * on the {@link Drawable} of a given view.
1476      * <p>
1477      * The operation will be performed on the {@link Drawable} returned by the
1478      * target {@link View#getBackground()}.
1479      * <p>
1480      */
1481     private class SetRippleDrawableColor extends Action {
1482 
1483         ColorStateList mColorStateList;
1484 
SetRippleDrawableColor(@dRes int id, ColorStateList colorStateList)1485         SetRippleDrawableColor(@IdRes int id, ColorStateList colorStateList) {
1486             this.viewId = id;
1487             this.mColorStateList = colorStateList;
1488         }
1489 
SetRippleDrawableColor(Parcel parcel)1490         SetRippleDrawableColor(Parcel parcel) {
1491             viewId = parcel.readInt();
1492             mColorStateList = parcel.readParcelable(null);
1493         }
1494 
writeToParcel(Parcel dest, int flags)1495         public void writeToParcel(Parcel dest, int flags) {
1496             dest.writeInt(viewId);
1497             dest.writeParcelable(mColorStateList, 0);
1498         }
1499 
1500         @Override
apply(View root, ViewGroup rootParent, InteractionHandler handler, ColorResources colorResources)1501         public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
1502                 ColorResources colorResources) {
1503             final View target = root.findViewById(viewId);
1504             if (target == null) return;
1505 
1506             // Pick the correct drawable to modify for this view
1507             Drawable targetDrawable = target.getBackground();
1508 
1509             if (targetDrawable instanceof RippleDrawable) {
1510                 ((RippleDrawable) targetDrawable.mutate()).setColor(mColorStateList);
1511             }
1512         }
1513 
1514         @Override
getActionTag()1515         public int getActionTag() {
1516             return SET_RIPPLE_DRAWABLE_COLOR_TAG;
1517         }
1518     }
1519 
1520     private final class ViewContentNavigation extends Action {
1521         final boolean mNext;
1522 
ViewContentNavigation(@dRes int viewId, boolean next)1523         ViewContentNavigation(@IdRes int viewId, boolean next) {
1524             this.viewId = viewId;
1525             this.mNext = next;
1526         }
1527 
ViewContentNavigation(Parcel in)1528         ViewContentNavigation(Parcel in) {
1529             this.viewId = in.readInt();
1530             this.mNext = in.readBoolean();
1531         }
1532 
writeToParcel(Parcel out, int flags)1533         public void writeToParcel(Parcel out, int flags) {
1534             out.writeInt(this.viewId);
1535             out.writeBoolean(this.mNext);
1536         }
1537 
1538         @Override
apply(View root, ViewGroup rootParent, InteractionHandler handler, ColorResources colorResources)1539         public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
1540                 ColorResources colorResources) {
1541             final View view = root.findViewById(viewId);
1542             if (view == null) return;
1543 
1544             try {
1545                 getMethod(view,
1546                         mNext ? "showNext" : "showPrevious", null, false /* async */).invoke(view);
1547             } catch (Throwable ex) {
1548                 throw new ActionException(ex);
1549             }
1550         }
1551 
mergeBehavior()1552         public int mergeBehavior() {
1553             return MERGE_IGNORE;
1554         }
1555 
1556         @Override
getActionTag()1557         public int getActionTag() {
1558             return VIEW_CONTENT_NAVIGATION_TAG;
1559         }
1560     }
1561 
1562     private static class BitmapCache {
1563 
1564         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
1565         ArrayList<Bitmap> mBitmaps;
1566         int mBitmapMemory = -1;
1567 
BitmapCache()1568         public BitmapCache() {
1569             mBitmaps = new ArrayList<>();
1570         }
1571 
BitmapCache(Parcel source)1572         public BitmapCache(Parcel source) {
1573             mBitmaps = source.createTypedArrayList(Bitmap.CREATOR);
1574         }
1575 
getBitmapId(Bitmap b)1576         public int getBitmapId(Bitmap b) {
1577             if (b == null) {
1578                 return -1;
1579             } else {
1580                 if (mBitmaps.contains(b)) {
1581                     return mBitmaps.indexOf(b);
1582                 } else {
1583                     mBitmaps.add(b);
1584                     mBitmapMemory = -1;
1585                     return (mBitmaps.size() - 1);
1586                 }
1587             }
1588         }
1589 
1590         @Nullable
getBitmapForId(int id)1591         public Bitmap getBitmapForId(int id) {
1592             if (id == -1 || id >= mBitmaps.size()) {
1593                 return null;
1594             } else {
1595                 return mBitmaps.get(id);
1596             }
1597         }
1598 
writeBitmapsToParcel(Parcel dest, int flags)1599         public void writeBitmapsToParcel(Parcel dest, int flags) {
1600             dest.writeTypedList(mBitmaps, flags);
1601         }
1602 
getBitmapMemory()1603         public int getBitmapMemory() {
1604             if (mBitmapMemory < 0) {
1605                 mBitmapMemory = 0;
1606                 int count = mBitmaps.size();
1607                 for (int i = 0; i < count; i++) {
1608                     mBitmapMemory += mBitmaps.get(i).getAllocationByteCount();
1609                 }
1610             }
1611             return mBitmapMemory;
1612         }
1613     }
1614 
1615     private class BitmapReflectionAction extends Action {
1616         int bitmapId;
1617         @UnsupportedAppUsage
1618         Bitmap bitmap;
1619         @UnsupportedAppUsage
1620         String methodName;
1621 
BitmapReflectionAction(@dRes int viewId, String methodName, Bitmap bitmap)1622         BitmapReflectionAction(@IdRes int viewId, String methodName, Bitmap bitmap) {
1623             this.bitmap = bitmap;
1624             this.viewId = viewId;
1625             this.methodName = methodName;
1626             bitmapId = mBitmapCache.getBitmapId(bitmap);
1627         }
1628 
BitmapReflectionAction(Parcel in)1629         BitmapReflectionAction(Parcel in) {
1630             viewId = in.readInt();
1631             methodName = in.readString8();
1632             bitmapId = in.readInt();
1633             bitmap = mBitmapCache.getBitmapForId(bitmapId);
1634         }
1635 
1636         @Override
writeToParcel(Parcel dest, int flags)1637         public void writeToParcel(Parcel dest, int flags) {
1638             dest.writeInt(viewId);
1639             dest.writeString8(methodName);
1640             dest.writeInt(bitmapId);
1641         }
1642 
1643         @Override
apply(View root, ViewGroup rootParent, InteractionHandler handler, ColorResources colorResources)1644         public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
1645                 ColorResources colorResources) throws ActionException {
1646             ReflectionAction ra = new ReflectionAction(viewId, methodName,
1647                     BaseReflectionAction.BITMAP,
1648                     bitmap);
1649             ra.apply(root, rootParent, handler, colorResources);
1650         }
1651 
1652         @Override
setHierarchyRootData(HierarchyRootData rootData)1653         public void setHierarchyRootData(HierarchyRootData rootData) {
1654             bitmapId = rootData.mBitmapCache.getBitmapId(bitmap);
1655         }
1656 
1657         @Override
getActionTag()1658         public int getActionTag() {
1659             return BITMAP_REFLECTION_ACTION_TAG;
1660         }
1661     }
1662 
1663     /**
1664      * Base class for the reflection actions.
1665      */
1666     private abstract class BaseReflectionAction extends Action {
1667         static final int BOOLEAN = 1;
1668         static final int BYTE = 2;
1669         static final int SHORT = 3;
1670         static final int INT = 4;
1671         static final int LONG = 5;
1672         static final int FLOAT = 6;
1673         static final int DOUBLE = 7;
1674         static final int CHAR = 8;
1675         static final int STRING = 9;
1676         static final int CHAR_SEQUENCE = 10;
1677         static final int URI = 11;
1678         // BITMAP actions are never stored in the list of actions. They are only used locally
1679         // to implement BitmapReflectionAction, which eliminates duplicates using BitmapCache.
1680         static final int BITMAP = 12;
1681         static final int BUNDLE = 13;
1682         static final int INTENT = 14;
1683         static final int COLOR_STATE_LIST = 15;
1684         static final int ICON = 16;
1685         static final int BLEND_MODE = 17;
1686 
1687         @UnsupportedAppUsage
1688         String methodName;
1689         int type;
1690 
BaseReflectionAction(@dRes int viewId, String methodName, int type)1691         BaseReflectionAction(@IdRes int viewId, String methodName, int type) {
1692             this.viewId = viewId;
1693             this.methodName = methodName;
1694             this.type = type;
1695         }
1696 
BaseReflectionAction(Parcel in)1697         BaseReflectionAction(Parcel in) {
1698             this.viewId = in.readInt();
1699             this.methodName = in.readString8();
1700             this.type = in.readInt();
1701             //noinspection ConstantIfStatement
1702             if (false) {
1703                 Log.d(LOG_TAG, "read viewId=0x" + Integer.toHexString(this.viewId)
1704                         + " methodName=" + this.methodName + " type=" + this.type);
1705             }
1706         }
1707 
writeToParcel(Parcel out, int flags)1708         public void writeToParcel(Parcel out, int flags) {
1709             out.writeInt(this.viewId);
1710             out.writeString8(this.methodName);
1711             out.writeInt(this.type);
1712         }
1713 
1714         /**
1715          * Returns the value to use as parameter for the method.
1716          *
1717          * The view might be passed as {@code null} if the parameter value is requested outside of
1718          * inflation. If the parameter cannot be determined at that time, the method should return
1719          * {@code null} but not raise any exception.
1720          */
1721         @Nullable
getParameterValue(@ullable View view)1722         protected abstract Object getParameterValue(@Nullable View view) throws ActionException;
1723 
1724         @Override
apply(View root, ViewGroup rootParent, InteractionHandler handler, ColorResources colorResources)1725         public final void apply(View root, ViewGroup rootParent, InteractionHandler handler,
1726                 ColorResources colorResources) {
1727             final View view = root.findViewById(viewId);
1728             if (view == null) return;
1729 
1730             Class<?> param = getParameterType(this.type);
1731             if (param == null) {
1732                 throw new ActionException("bad type: " + this.type);
1733             }
1734             Object value = getParameterValue(view);
1735             try {
1736                 getMethod(view, this.methodName, param, false /* async */).invoke(view, value);
1737             } catch (Throwable ex) {
1738                 throw new ActionException(ex);
1739             }
1740         }
1741 
1742         @Override
initActionAsync(ViewTree root, ViewGroup rootParent, InteractionHandler handler, ColorResources colorResources)1743         public final Action initActionAsync(ViewTree root, ViewGroup rootParent,
1744                 InteractionHandler handler, ColorResources colorResources) {
1745             final View view = root.findViewById(viewId);
1746             if (view == null) return ACTION_NOOP;
1747 
1748             Class<?> param = getParameterType(this.type);
1749             if (param == null) {
1750                 throw new ActionException("bad type: " + this.type);
1751             }
1752 
1753             Object value = getParameterValue(view);
1754             try {
1755                 MethodHandle method = getMethod(view, this.methodName, param, true /* async */);
1756 
1757                 if (method != null) {
1758                     Runnable endAction = (Runnable) method.invoke(view, value);
1759                     if (endAction == null) {
1760                         return ACTION_NOOP;
1761                     }
1762                     // Special case view stub
1763                     if (endAction instanceof ViewStub.ViewReplaceRunnable) {
1764                         root.createTree();
1765                         // Replace child tree
1766                         root.findViewTreeById(viewId).replaceView(
1767                                 ((ViewStub.ViewReplaceRunnable) endAction).view);
1768                     }
1769                     return new RunnableAction(endAction);
1770                 }
1771             } catch (Throwable ex) {
1772                 throw new ActionException(ex);
1773             }
1774 
1775             return this;
1776         }
1777 
mergeBehavior()1778         public final int mergeBehavior() {
1779             // smoothScrollBy is cumulative, everything else overwites.
1780             if (methodName.equals("smoothScrollBy")) {
1781                 return MERGE_APPEND;
1782             } else {
1783                 return MERGE_REPLACE;
1784             }
1785         }
1786 
1787         @Override
getUniqueKey()1788         public final String getUniqueKey() {
1789             // Each type of reflection action corresponds to a setter, so each should be seen as
1790             // unique from the standpoint of merging.
1791             return super.getUniqueKey() + this.methodName + this.type;
1792         }
1793 
1794         @Override
prefersAsyncApply()1795         public final boolean prefersAsyncApply() {
1796             return this.type == URI || this.type == ICON;
1797         }
1798 
1799         @Override
visitUris(@onNull Consumer<Uri> visitor)1800         public final void visitUris(@NonNull Consumer<Uri> visitor) {
1801             switch (this.type) {
1802                 case URI:
1803                     final Uri uri = (Uri) getParameterValue(null);
1804                     if (uri != null) visitor.accept(uri);
1805                     break;
1806                 case ICON:
1807                     final Icon icon = (Icon) getParameterValue(null);
1808                     if (icon != null) visitIconUri(icon, visitor);
1809                     break;
1810             }
1811         }
1812     }
1813 
1814     /** Class for the reflection actions. */
1815     private final class ReflectionAction extends BaseReflectionAction {
1816         @UnsupportedAppUsage
1817         Object value;
1818 
ReflectionAction(@dRes int viewId, String methodName, int type, Object value)1819         ReflectionAction(@IdRes int viewId, String methodName, int type, Object value) {
1820             super(viewId, methodName, type);
1821             this.value = value;
1822         }
1823 
ReflectionAction(Parcel in)1824         ReflectionAction(Parcel in) {
1825             super(in);
1826             // For some values that may have been null, we first check a flag to see if they were
1827             // written to the parcel.
1828             switch (this.type) {
1829                 case BOOLEAN:
1830                     this.value = in.readBoolean();
1831                     break;
1832                 case BYTE:
1833                     this.value = in.readByte();
1834                     break;
1835                 case SHORT:
1836                     this.value = (short) in.readInt();
1837                     break;
1838                 case INT:
1839                     this.value = in.readInt();
1840                     break;
1841                 case LONG:
1842                     this.value = in.readLong();
1843                     break;
1844                 case FLOAT:
1845                     this.value = in.readFloat();
1846                     break;
1847                 case DOUBLE:
1848                     this.value = in.readDouble();
1849                     break;
1850                 case CHAR:
1851                     this.value = (char) in.readInt();
1852                     break;
1853                 case STRING:
1854                     this.value = in.readString8();
1855                     break;
1856                 case CHAR_SEQUENCE:
1857                     this.value = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
1858                     break;
1859                 case URI:
1860                     this.value = in.readTypedObject(Uri.CREATOR);
1861                     break;
1862                 case BITMAP:
1863                     this.value = in.readTypedObject(Bitmap.CREATOR);
1864                     break;
1865                 case BUNDLE:
1866                     // Because we use Parcel.allowSquashing() when writing, and that affects
1867                     //  how the contents of Bundles are written, we need to ensure the bundle is
1868                     //  unparceled immediately, not lazily.  Setting a custom ReadWriteHelper
1869                     //  just happens to have that effect on Bundle.readFromParcel().
1870                     // TODO(b/212731590): build this state tracking into Bundle
1871                     if (in.hasReadWriteHelper()) {
1872                         this.value = in.readBundle();
1873                     } else {
1874                         in.setReadWriteHelper(ALTERNATIVE_DEFAULT);
1875                         this.value = in.readBundle();
1876                         in.setReadWriteHelper(null);
1877                     }
1878                     break;
1879                 case INTENT:
1880                     this.value = in.readTypedObject(Intent.CREATOR);
1881                     break;
1882                 case COLOR_STATE_LIST:
1883                     this.value = in.readTypedObject(ColorStateList.CREATOR);
1884                     break;
1885                 case ICON:
1886                     this.value = in.readTypedObject(Icon.CREATOR);
1887                     break;
1888                 case BLEND_MODE:
1889                     this.value = BlendMode.fromValue(in.readInt());
1890                     break;
1891                 default:
1892                     break;
1893             }
1894         }
1895 
writeToParcel(Parcel out, int flags)1896         public void writeToParcel(Parcel out, int flags) {
1897             super.writeToParcel(out, flags);
1898             // For some values which are null, we record an integer flag to indicate whether
1899             // we have written a valid value to the parcel.
1900             switch (this.type) {
1901                 case BOOLEAN:
1902                     out.writeBoolean((Boolean) this.value);
1903                     break;
1904                 case BYTE:
1905                     out.writeByte((Byte) this.value);
1906                     break;
1907                 case SHORT:
1908                     out.writeInt((Short) this.value);
1909                     break;
1910                 case INT:
1911                     out.writeInt((Integer) this.value);
1912                     break;
1913                 case LONG:
1914                     out.writeLong((Long) this.value);
1915                     break;
1916                 case FLOAT:
1917                     out.writeFloat((Float) this.value);
1918                     break;
1919                 case DOUBLE:
1920                     out.writeDouble((Double) this.value);
1921                     break;
1922                 case CHAR:
1923                     out.writeInt((int) ((Character) this.value).charValue());
1924                     break;
1925                 case STRING:
1926                     out.writeString8((String) this.value);
1927                     break;
1928                 case CHAR_SEQUENCE:
1929                     TextUtils.writeToParcel((CharSequence) this.value, out, flags);
1930                     break;
1931                 case BUNDLE:
1932                     out.writeBundle((Bundle) this.value);
1933                     break;
1934                 case BLEND_MODE:
1935                     out.writeInt(BlendMode.toValue((BlendMode) this.value));
1936                     break;
1937                 case URI:
1938                 case BITMAP:
1939                 case INTENT:
1940                 case COLOR_STATE_LIST:
1941                 case ICON:
1942                     out.writeTypedObject((Parcelable) this.value, flags);
1943                     break;
1944                 default:
1945                     break;
1946             }
1947         }
1948 
1949         @Nullable
1950         @Override
getParameterValue(@ullable View view)1951         protected Object getParameterValue(@Nullable View view) throws ActionException {
1952             return this.value;
1953         }
1954 
1955         @Override
getActionTag()1956         public int getActionTag() {
1957             return REFLECTION_ACTION_TAG;
1958         }
1959     }
1960 
1961     private final class ResourceReflectionAction extends BaseReflectionAction {
1962 
1963         static final int DIMEN_RESOURCE = 1;
1964         static final int COLOR_RESOURCE = 2;
1965         static final int STRING_RESOURCE = 3;
1966 
1967         private final int mResourceType;
1968         private final int mResId;
1969 
ResourceReflectionAction(@dRes int viewId, String methodName, int parameterType, int resourceType, int resId)1970         ResourceReflectionAction(@IdRes int viewId, String methodName, int parameterType,
1971                 int resourceType, int resId) {
1972             super(viewId, methodName, parameterType);
1973             this.mResourceType = resourceType;
1974             this.mResId = resId;
1975         }
1976 
ResourceReflectionAction(Parcel in)1977         ResourceReflectionAction(Parcel in) {
1978             super(in);
1979             this.mResourceType = in.readInt();
1980             this.mResId = in.readInt();
1981         }
1982 
1983         @Override
writeToParcel(Parcel dest, int flags)1984         public void writeToParcel(Parcel dest, int flags) {
1985             super.writeToParcel(dest, flags);
1986             dest.writeInt(this.mResourceType);
1987             dest.writeInt(this.mResId);
1988         }
1989 
1990         @Nullable
1991         @Override
getParameterValue(@ullable View view)1992         protected Object getParameterValue(@Nullable View view) throws ActionException {
1993             if (view == null) return null;
1994 
1995             Resources resources = view.getContext().getResources();
1996             try {
1997                 switch (this.mResourceType) {
1998                     case DIMEN_RESOURCE:
1999                         switch (this.type) {
2000                             case BaseReflectionAction.INT:
2001                                 return mResId == 0 ? 0 : resources.getDimensionPixelSize(mResId);
2002                             case BaseReflectionAction.FLOAT:
2003                                 return mResId == 0 ? 0f : resources.getDimension(mResId);
2004                             default:
2005                                 throw new ActionException(
2006                                         "dimen resources must be used as INT or FLOAT, "
2007                                                 + "not " + this.type);
2008                         }
2009                     case COLOR_RESOURCE:
2010                         switch (this.type) {
2011                             case BaseReflectionAction.INT:
2012                                 return mResId == 0 ? 0 : view.getContext().getColor(mResId);
2013                             case BaseReflectionAction.COLOR_STATE_LIST:
2014                                 return mResId == 0
2015                                         ? null : view.getContext().getColorStateList(mResId);
2016                             default:
2017                                 throw new ActionException(
2018                                         "color resources must be used as INT or COLOR_STATE_LIST,"
2019                                                 + " not " + this.type);
2020                         }
2021                     case STRING_RESOURCE:
2022                         switch (this.type) {
2023                             case BaseReflectionAction.CHAR_SEQUENCE:
2024                                 return mResId == 0 ? null : resources.getText(mResId);
2025                             case BaseReflectionAction.STRING:
2026                                 return mResId == 0 ? null : resources.getString(mResId);
2027                             default:
2028                                 throw new ActionException(
2029                                         "string resources must be used as STRING or CHAR_SEQUENCE,"
2030                                                 + " not " + this.type);
2031                         }
2032                     default:
2033                         throw new ActionException("unknown resource type: " + this.mResourceType);
2034                 }
2035             } catch (ActionException ex) {
2036                 throw ex;
2037             } catch (Throwable t) {
2038                 throw new ActionException(t);
2039             }
2040         }
2041 
2042         @Override
getActionTag()2043         public int getActionTag() {
2044             return RESOURCE_REFLECTION_ACTION_TAG;
2045         }
2046     }
2047 
2048     private final class AttributeReflectionAction extends BaseReflectionAction {
2049 
2050         static final int DIMEN_RESOURCE = 1;
2051         static final int COLOR_RESOURCE = 2;
2052         static final int STRING_RESOURCE = 3;
2053 
2054         private final int mResourceType;
2055         private final int mAttrId;
2056 
AttributeReflectionAction(@dRes int viewId, String methodName, int parameterType, int resourceType, int attrId)2057         AttributeReflectionAction(@IdRes int viewId, String methodName, int parameterType,
2058                 int resourceType, int attrId) {
2059             super(viewId, methodName, parameterType);
2060             this.mResourceType = resourceType;
2061             this.mAttrId = attrId;
2062         }
2063 
AttributeReflectionAction(Parcel in)2064         AttributeReflectionAction(Parcel in) {
2065             super(in);
2066             this.mResourceType = in.readInt();
2067             this.mAttrId = in.readInt();
2068         }
2069 
2070         @Override
writeToParcel(Parcel dest, int flags)2071         public void writeToParcel(Parcel dest, int flags) {
2072             super.writeToParcel(dest, flags);
2073             dest.writeInt(this.mResourceType);
2074             dest.writeInt(this.mAttrId);
2075         }
2076 
2077         @Override
getParameterValue(View view)2078         protected Object getParameterValue(View view) throws ActionException {
2079             TypedArray typedArray = view.getContext().obtainStyledAttributes(new int[]{mAttrId});
2080             try {
2081                 // When mAttrId == 0, we will depend on the default values below
2082                 if (mAttrId != 0 && typedArray.getType(0) == TypedValue.TYPE_NULL) {
2083                     throw new ActionException("Attribute 0x" + Integer.toHexString(this.mAttrId)
2084                             + " is not defined");
2085                 }
2086                 switch (this.mResourceType) {
2087                     case DIMEN_RESOURCE:
2088                         switch (this.type) {
2089                             case BaseReflectionAction.INT:
2090                                 return typedArray.getDimensionPixelSize(0, 0);
2091                             case BaseReflectionAction.FLOAT:
2092                                 return typedArray.getDimension(0, 0);
2093                             default:
2094                                 throw new ActionException(
2095                                         "dimen attribute 0x" + Integer.toHexString(this.mAttrId)
2096                                                 + " must be used as INT or FLOAT,"
2097                                                 + " not " + this.type);
2098                         }
2099                     case COLOR_RESOURCE:
2100                         switch (this.type) {
2101                             case BaseReflectionAction.INT:
2102                                 return typedArray.getColor(0, 0);
2103                             case BaseReflectionAction.COLOR_STATE_LIST:
2104                                 return typedArray.getColorStateList(0);
2105                             default:
2106                                 throw new ActionException(
2107                                         "color attribute 0x" + Integer.toHexString(this.mAttrId)
2108                                                 + " must be used as INT or COLOR_STATE_LIST,"
2109                                                 + " not " + this.type);
2110                         }
2111                     case STRING_RESOURCE:
2112                         switch (this.type) {
2113                             case BaseReflectionAction.CHAR_SEQUENCE:
2114                                 return typedArray.getText(0);
2115                             case BaseReflectionAction.STRING:
2116                                 return typedArray.getString(0);
2117                             default:
2118                                 throw new ActionException(
2119                                         "string attribute 0x" + Integer.toHexString(this.mAttrId)
2120                                                 + " must be used as STRING or CHAR_SEQUENCE,"
2121                                                 + " not " + this.type);
2122                         }
2123                     default:
2124                         // Note: This can only be an implementation error.
2125                         throw new ActionException(
2126                                 "Unknown resource type: " + this.mResourceType);
2127                 }
2128             } catch (ActionException ex) {
2129                 throw ex;
2130             } catch (Throwable t) {
2131                 throw new ActionException(t);
2132             } finally {
2133                 typedArray.recycle();
2134             }
2135         }
2136 
2137         @Override
getActionTag()2138         public int getActionTag() {
2139             return ATTRIBUTE_REFLECTION_ACTION_TAG;
2140         }
2141     }
2142     private final class ComplexUnitDimensionReflectionAction extends BaseReflectionAction {
2143 
2144         private final float mValue;
2145         @ComplexDimensionUnit
2146         private final int mUnit;
2147 
ComplexUnitDimensionReflectionAction(int viewId, String methodName, int parameterType, float value, @ComplexDimensionUnit int unit)2148         ComplexUnitDimensionReflectionAction(int viewId, String methodName, int parameterType,
2149                 float value, @ComplexDimensionUnit int unit) {
2150             super(viewId, methodName, parameterType);
2151             this.mValue = value;
2152             this.mUnit = unit;
2153         }
2154 
ComplexUnitDimensionReflectionAction(Parcel in)2155         ComplexUnitDimensionReflectionAction(Parcel in) {
2156             super(in);
2157             this.mValue = in.readFloat();
2158             this.mUnit = in.readInt();
2159         }
2160 
2161         @Override
writeToParcel(Parcel dest, int flags)2162         public void writeToParcel(Parcel dest, int flags) {
2163             super.writeToParcel(dest, flags);
2164             dest.writeFloat(this.mValue);
2165             dest.writeInt(this.mUnit);
2166         }
2167 
2168         @Nullable
2169         @Override
getParameterValue(@ullable View view)2170         protected Object getParameterValue(@Nullable View view) throws ActionException {
2171             if (view == null) return null;
2172 
2173             DisplayMetrics dm = view.getContext().getResources().getDisplayMetrics();
2174             try {
2175                 int data = TypedValue.createComplexDimension(this.mValue, this.mUnit);
2176                 switch (this.type) {
2177                     case ReflectionAction.INT:
2178                         return TypedValue.complexToDimensionPixelSize(data, dm);
2179                     case ReflectionAction.FLOAT:
2180                         return TypedValue.complexToDimension(data, dm);
2181                     default:
2182                         throw new ActionException(
2183                                 "parameter type must be INT or FLOAT, not " + this.type);
2184                 }
2185             } catch (ActionException ex) {
2186                 throw ex;
2187             } catch (Throwable t) {
2188                 throw new ActionException(t);
2189             }
2190         }
2191 
2192         @Override
getActionTag()2193         public int getActionTag() {
2194             return COMPLEX_UNIT_DIMENSION_REFLECTION_ACTION_TAG;
2195         }
2196     }
2197 
2198     private final class NightModeReflectionAction extends BaseReflectionAction {
2199 
2200         private final Object mLightValue;
2201         private final Object mDarkValue;
2202 
NightModeReflectionAction( @dRes int viewId, String methodName, int type, Object lightValue, Object darkValue)2203         NightModeReflectionAction(
2204                 @IdRes int viewId,
2205                 String methodName,
2206                 int type,
2207                 Object lightValue,
2208                 Object darkValue) {
2209             super(viewId, methodName, type);
2210             mLightValue = lightValue;
2211             mDarkValue = darkValue;
2212         }
2213 
NightModeReflectionAction(Parcel in)2214         NightModeReflectionAction(Parcel in) {
2215             super(in);
2216             switch (this.type) {
2217                 case ICON:
2218                     mLightValue = in.readTypedObject(Icon.CREATOR);
2219                     mDarkValue = in.readTypedObject(Icon.CREATOR);
2220                     break;
2221                 case COLOR_STATE_LIST:
2222                     mLightValue = in.readTypedObject(ColorStateList.CREATOR);
2223                     mDarkValue = in.readTypedObject(ColorStateList.CREATOR);
2224                     break;
2225                 case INT:
2226                     mLightValue = in.readInt();
2227                     mDarkValue = in.readInt();
2228                     break;
2229                 default:
2230                     throw new ActionException("Unexpected night mode action type: " + this.type);
2231             }
2232         }
2233 
2234         @Override
writeToParcel(Parcel out, int flags)2235         public void writeToParcel(Parcel out, int flags) {
2236             super.writeToParcel(out, flags);
2237             switch (this.type) {
2238                 case ICON:
2239                 case COLOR_STATE_LIST:
2240                     out.writeTypedObject((Parcelable) mLightValue, flags);
2241                     out.writeTypedObject((Parcelable) mDarkValue, flags);
2242                     break;
2243                 case INT:
2244                     out.writeInt((int) mLightValue);
2245                     out.writeInt((int) mDarkValue);
2246                     break;
2247             }
2248         }
2249 
2250         @Nullable
2251         @Override
getParameterValue(@ullable View view)2252         protected Object getParameterValue(@Nullable View view) throws ActionException {
2253             if (view == null) return null;
2254 
2255             Configuration configuration = view.getResources().getConfiguration();
2256             return configuration.isNightModeActive() ? mDarkValue : mLightValue;
2257         }
2258 
2259         @Override
getActionTag()2260         public int getActionTag() {
2261             return NIGHT_MODE_REFLECTION_ACTION_TAG;
2262         }
2263     }
2264 
2265     /**
2266      * This is only used for async execution of actions and it not parcelable.
2267      */
2268     private static final class RunnableAction extends RuntimeAction {
2269         private final Runnable mRunnable;
2270 
RunnableAction(Runnable r)2271         RunnableAction(Runnable r) {
2272             mRunnable = r;
2273         }
2274 
2275         @Override
apply(View root, ViewGroup rootParent, InteractionHandler handler, ColorResources colorResources)2276         public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
2277                 ColorResources colorResources) {
2278             mRunnable.run();
2279         }
2280     }
2281 
hasStableId(View view)2282     private static boolean hasStableId(View view) {
2283         Object tag = view.getTag(com.android.internal.R.id.remote_views_stable_id);
2284         return tag != null;
2285     }
2286 
getStableId(View view)2287     private static int getStableId(View view) {
2288         Integer id = (Integer) view.getTag(com.android.internal.R.id.remote_views_stable_id);
2289         return id == null ? ViewGroupActionAdd.NO_ID : id;
2290     }
2291 
setStableId(View view, int stableId)2292     private static void setStableId(View view, int stableId) {
2293         view.setTagInternal(com.android.internal.R.id.remote_views_stable_id, stableId);
2294     }
2295 
2296     // Returns the next recyclable child of the view group, or -1 if there are none.
getNextRecyclableChild(ViewGroup vg)2297     private static int getNextRecyclableChild(ViewGroup vg) {
2298         Integer tag = (Integer) vg.getTag(com.android.internal.R.id.remote_views_next_child);
2299         return tag == null ? -1 : tag;
2300     }
2301 
getViewLayoutId(View v)2302     private static int getViewLayoutId(View v) {
2303         return (Integer) v.getTag(R.id.widget_frame);
2304     }
2305 
setNextRecyclableChild(ViewGroup vg, int nextChild, int numChildren)2306     private static void setNextRecyclableChild(ViewGroup vg, int nextChild, int numChildren) {
2307         if (nextChild < 0 || nextChild >= numChildren) {
2308             vg.setTagInternal(com.android.internal.R.id.remote_views_next_child, -1);
2309         } else {
2310             vg.setTagInternal(com.android.internal.R.id.remote_views_next_child, nextChild);
2311         }
2312     }
2313 
finalizeViewRecycling(ViewGroup root)2314     private void finalizeViewRecycling(ViewGroup root) {
2315         // Remove any recyclable children that were not used. nextChild should either be -1 or point
2316         // to the next recyclable child that hasn't been recycled.
2317         int nextChild = getNextRecyclableChild(root);
2318         if (nextChild >= 0 && nextChild < root.getChildCount()) {
2319             root.removeViews(nextChild, root.getChildCount() - nextChild);
2320         }
2321         // Make sure on the next round, we don't try to recycle if removeAllViews is not called.
2322         setNextRecyclableChild(root, -1, 0);
2323         // Traverse the view tree.
2324         for (int i = 0; i < root.getChildCount(); i++) {
2325             View child = root.getChildAt(i);
2326             if (child instanceof ViewGroup && !child.isRootNamespace()) {
2327                 finalizeViewRecycling((ViewGroup) child);
2328             }
2329         }
2330     }
2331 
2332     /**
2333      * ViewGroup methods that are related to adding Views.
2334      */
2335     private class ViewGroupActionAdd extends Action {
2336         static final int NO_ID = -1;
2337         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
2338         private RemoteViews mNestedViews;
2339         private int mIndex;
2340         private int mStableId;
2341 
ViewGroupActionAdd(@dRes int viewId, RemoteViews nestedViews)2342         ViewGroupActionAdd(@IdRes int viewId, RemoteViews nestedViews) {
2343             this(viewId, nestedViews, -1 /* index */, NO_ID /* nestedViewId */);
2344         }
2345 
ViewGroupActionAdd(@dRes int viewId, RemoteViews nestedViews, int index)2346         ViewGroupActionAdd(@IdRes int viewId, RemoteViews nestedViews, int index) {
2347             this(viewId, nestedViews, index, NO_ID /* nestedViewId */);
2348         }
2349 
ViewGroupActionAdd(@dRes int viewId, RemoteViews nestedViews, int index, int stableId)2350         ViewGroupActionAdd(@IdRes int viewId, RemoteViews nestedViews, int index, int stableId) {
2351             this.viewId = viewId;
2352             mNestedViews = nestedViews;
2353             mIndex = index;
2354             mStableId = stableId;
2355             nestedViews.configureAsChild(getHierarchyRootData());
2356         }
2357 
ViewGroupActionAdd(Parcel parcel, ApplicationInfo info, int depth)2358         ViewGroupActionAdd(Parcel parcel, ApplicationInfo info, int depth) {
2359             viewId = parcel.readInt();
2360             mIndex = parcel.readInt();
2361             mStableId = parcel.readInt();
2362             mNestedViews = new RemoteViews(parcel, getHierarchyRootData(), info, depth);
2363             mNestedViews.addFlags(mApplyFlags);
2364         }
2365 
writeToParcel(Parcel dest, int flags)2366         public void writeToParcel(Parcel dest, int flags) {
2367             dest.writeInt(viewId);
2368             dest.writeInt(mIndex);
2369             dest.writeInt(mStableId);
2370             mNestedViews.writeToParcel(dest, flags);
2371         }
2372 
2373         @Override
setHierarchyRootData(HierarchyRootData root)2374         public void setHierarchyRootData(HierarchyRootData root) {
2375             mNestedViews.configureAsChild(root);
2376         }
2377 
findViewIndexToRecycle(ViewGroup target, RemoteViews newContent)2378         private int findViewIndexToRecycle(ViewGroup target, RemoteViews newContent) {
2379             for (int nextChild = getNextRecyclableChild(target); nextChild < target.getChildCount();
2380                     nextChild++) {
2381                 View child = target.getChildAt(nextChild);
2382                 if (getStableId(child) == mStableId) {
2383                     return nextChild;
2384                 }
2385             }
2386             return -1;
2387         }
2388 
2389         @Override
apply(View root, ViewGroup rootParent, InteractionHandler handler, ColorResources colorResources)2390         public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
2391                 ColorResources colorResources) {
2392             final Context context = root.getContext();
2393             final ViewGroup target = root.findViewById(viewId);
2394 
2395             if (target == null) {
2396                 return;
2397             }
2398 
2399             // If removeAllViews was called, this returns the next potential recycled view.
2400             // If there are no more views to recycle (or removeAllViews was not called), this
2401             // will return -1.
2402             final int nextChild = getNextRecyclableChild(target);
2403             RemoteViews rvToApply = mNestedViews.getRemoteViewsToApply(context);
2404             if (nextChild >= 0 && mStableId != NO_ID) {
2405                 // At that point, the views starting at index nextChild are the ones recyclable but
2406                 // not yet recycled. All views added on that round of application are placed before.
2407                 // Find the next view with the same stable id, or -1.
2408                 int recycledViewIndex = findViewIndexToRecycle(target, rvToApply);
2409                 if (recycledViewIndex >= 0) {
2410                     View child = target.getChildAt(recycledViewIndex);
2411                     if (rvToApply.canRecycleView(child)) {
2412                         if (nextChild < recycledViewIndex) {
2413                             target.removeViews(nextChild, recycledViewIndex - nextChild);
2414                         }
2415                         setNextRecyclableChild(target, nextChild + 1, target.getChildCount());
2416                         rvToApply.reapply(context, child, handler, null /* size */, colorResources,
2417                                 false /* topLevel */);
2418                         return;
2419                     }
2420                     // If we cannot recycle the views, we still remove all views in between to
2421                     // avoid weird behaviors and insert the new view in place of the old one.
2422                     target.removeViews(nextChild, recycledViewIndex - nextChild + 1);
2423                 }
2424             }
2425             // If we cannot recycle, insert the new view before the next recyclable child.
2426 
2427             // Inflate nested views and add as children
2428             View nestedView = rvToApply.apply(context, target, handler, null /* size */,
2429                     colorResources);
2430             if (mStableId != NO_ID) {
2431                 setStableId(nestedView, mStableId);
2432             }
2433             target.addView(nestedView, mIndex >= 0 ? mIndex : nextChild);
2434             if (nextChild >= 0) {
2435                 // If we are at the end, there is no reason to try to recycle anymore
2436                 setNextRecyclableChild(target, nextChild + 1, target.getChildCount());
2437             }
2438         }
2439 
2440         @Override
initActionAsync(ViewTree root, ViewGroup rootParent, InteractionHandler handler, ColorResources colorResources)2441         public Action initActionAsync(ViewTree root, ViewGroup rootParent,
2442                 InteractionHandler handler, ColorResources colorResources) {
2443             // In the async implementation, update the view tree so that subsequent calls to
2444             // findViewById return the current view.
2445             root.createTree();
2446             ViewTree target = root.findViewTreeById(viewId);
2447             if ((target == null) || !(target.mRoot instanceof ViewGroup)) {
2448                 return ACTION_NOOP;
2449             }
2450             final ViewGroup targetVg = (ViewGroup) target.mRoot;
2451 
2452             // Inflate nested views and perform all the async tasks for the child remoteView.
2453             final Context context = root.mRoot.getContext();
2454 
2455             // If removeAllViews was called, this returns the next potential recycled view.
2456             // If there are no more views to recycle (or removeAllViews was not called), this
2457             // will return -1.
2458             final int nextChild = getNextRecyclableChild(targetVg);
2459             if (nextChild >= 0 && mStableId != NO_ID) {
2460                 RemoteViews rvToApply = mNestedViews.getRemoteViewsToApply(context);
2461                 final int recycledViewIndex = target.findChildIndex(nextChild,
2462                         view -> getStableId(view) == mStableId);
2463                 if (recycledViewIndex >= 0) {
2464                     // At that point, the views starting at index nextChild are the ones
2465                     // recyclable but not yet recycled. All views added on that round of
2466                     // application are placed before.
2467                     ViewTree recycled = target.mChildren.get(recycledViewIndex);
2468                     // We can only recycle the view if the layout id is the same.
2469                     if (rvToApply.canRecycleView(recycled.mRoot)) {
2470                         if (recycledViewIndex > nextChild) {
2471                             target.removeChildren(nextChild, recycledViewIndex - nextChild);
2472                         }
2473                         setNextRecyclableChild(targetVg, nextChild + 1, target.mChildren.size());
2474                         final AsyncApplyTask reapplyTask = rvToApply.getInternalAsyncApplyTask(
2475                                 context,
2476                                 targetVg, null /* listener */, handler, null /* size */,
2477                                 colorResources,
2478                                 recycled.mRoot);
2479                         final ViewTree tree = reapplyTask.doInBackground();
2480                         if (tree == null) {
2481                             throw new ActionException(reapplyTask.mError);
2482                         }
2483                         return new RuntimeAction() {
2484                             @Override
2485                             public void apply(View root, ViewGroup rootParent,
2486                                     InteractionHandler handler, ColorResources colorResources)
2487                                     throws ActionException {
2488                                 reapplyTask.onPostExecute(tree);
2489                                 if (recycledViewIndex > nextChild) {
2490                                     targetVg.removeViews(nextChild, recycledViewIndex - nextChild);
2491                                 }
2492                             }
2493                         };
2494                     }
2495                     // If the layout id is different, still remove the children as if we recycled
2496                     // the view, to insert at the same place.
2497                     target.removeChildren(nextChild, recycledViewIndex - nextChild + 1);
2498                     return insertNewView(context, target, handler, colorResources,
2499                             () -> targetVg.removeViews(nextChild,
2500                                     recycledViewIndex - nextChild + 1));
2501 
2502                 }
2503             }
2504             // If we cannot recycle, simply add the view at the same available slot.
2505             return insertNewView(context, target, handler, colorResources, () -> {});
2506         }
2507 
insertNewView(Context context, ViewTree target, InteractionHandler handler, ColorResources colorResources, Runnable finalizeAction)2508         private Action insertNewView(Context context, ViewTree target, InteractionHandler handler,
2509                 ColorResources colorResources, Runnable finalizeAction) {
2510             ViewGroup targetVg = (ViewGroup) target.mRoot;
2511             int nextChild = getNextRecyclableChild(targetVg);
2512             final AsyncApplyTask task = mNestedViews.getInternalAsyncApplyTask(context, targetVg,
2513                     null /* listener */, handler, null /* size */, colorResources,
2514                     null /* result */);
2515             final ViewTree tree = task.doInBackground();
2516 
2517             if (tree == null) {
2518                 throw new ActionException(task.mError);
2519             }
2520             if (mStableId != NO_ID) {
2521                 setStableId(task.mResult, mStableId);
2522             }
2523 
2524             // Update the global view tree, so that next call to findViewTreeById
2525             // goes through the subtree as well.
2526             final int insertIndex = mIndex >= 0 ? mIndex : nextChild;
2527             target.addChild(tree, insertIndex);
2528             if (nextChild >= 0) {
2529                 setNextRecyclableChild(targetVg, nextChild + 1, target.mChildren.size());
2530             }
2531 
2532             return new RuntimeAction() {
2533                 @Override
2534                 public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
2535                         ColorResources colorResources) throws ActionException {
2536                     task.onPostExecute(tree);
2537                     finalizeAction.run();
2538                     targetVg.addView(task.mResult, insertIndex);
2539                 }
2540             };
2541         }
2542 
2543         @Override
2544         public int mergeBehavior() {
2545             return MERGE_APPEND;
2546         }
2547 
2548         @Override
2549         public boolean prefersAsyncApply() {
2550             return mNestedViews.prefersAsyncApply();
2551         }
2552 
2553         @Override
2554         public int getActionTag() {
2555             return VIEW_GROUP_ACTION_ADD_TAG;
2556         }
2557     }
2558 
2559     /**
2560      * ViewGroup methods related to removing child views.
2561      */
2562     private class ViewGroupActionRemove extends Action {
2563         /**
2564          * Id that indicates that all child views of the affected ViewGroup should be removed.
2565          *
2566          * <p>Using -2 because the default id is -1. This avoids accidentally matching that.
2567          */
2568         private static final int REMOVE_ALL_VIEWS_ID = -2;
2569 
2570         private int mViewIdToKeep;
2571 
2572         ViewGroupActionRemove(@IdRes int viewId) {
2573             this(viewId, REMOVE_ALL_VIEWS_ID);
2574         }
2575 
2576         ViewGroupActionRemove(@IdRes int viewId, @IdRes int viewIdToKeep) {
2577             this.viewId = viewId;
2578             mViewIdToKeep = viewIdToKeep;
2579         }
2580 
2581         ViewGroupActionRemove(Parcel parcel) {
2582             viewId = parcel.readInt();
2583             mViewIdToKeep = parcel.readInt();
2584         }
2585 
2586         public void writeToParcel(Parcel dest, int flags) {
2587             dest.writeInt(viewId);
2588             dest.writeInt(mViewIdToKeep);
2589         }
2590 
2591         @Override
2592         public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
2593                 ColorResources colorResources) {
2594             final ViewGroup target = root.findViewById(viewId);
2595 
2596             if (target == null) {
2597                 return;
2598             }
2599 
2600             if (mViewIdToKeep == REMOVE_ALL_VIEWS_ID) {
2601                 // Remote any view without a stable id
2602                 for (int i = target.getChildCount() - 1; i >= 0; i--) {
2603                     if (!hasStableId(target.getChildAt(i))) {
2604                         target.removeViewAt(i);
2605                     }
2606                 }
2607                 // In the end, only children with a stable id (i.e. recyclable) are left.
2608                 setNextRecyclableChild(target, 0, target.getChildCount());
2609                 return;
2610             }
2611 
2612             removeAllViewsExceptIdToKeep(target);
2613         }
2614 
2615         @Override
2616         public Action initActionAsync(ViewTree root, ViewGroup rootParent,
2617                 InteractionHandler handler, ColorResources colorResources) {
2618             // In the async implementation, update the view tree so that subsequent calls to
2619             // findViewById return the current view.
2620             root.createTree();
2621             ViewTree target = root.findViewTreeById(viewId);
2622 
2623             if ((target == null) || !(target.mRoot instanceof ViewGroup)) {
2624                 return ACTION_NOOP;
2625             }
2626 
2627             final ViewGroup targetVg = (ViewGroup) target.mRoot;
2628 
2629             if (mViewIdToKeep == REMOVE_ALL_VIEWS_ID) {
2630                 target.mChildren.removeIf(childTree -> !hasStableId(childTree.mRoot));
2631                 setNextRecyclableChild(targetVg, 0, target.mChildren.size());
2632             } else {
2633                 // Remove just the children which don't match the excepted view
2634                 target.mChildren.removeIf(childTree -> childTree.mRoot.getId() != mViewIdToKeep);
2635                 if (target.mChildren.isEmpty()) {
2636                     target.mChildren = null;
2637                 }
2638             }
2639             return new RuntimeAction() {
2640                 @Override
2641                 public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
2642                         ColorResources colorResources) throws ActionException {
2643                     if (mViewIdToKeep == REMOVE_ALL_VIEWS_ID) {
2644                         for (int i = targetVg.getChildCount() - 1; i >= 0; i--) {
2645                             if (!hasStableId(targetVg.getChildAt(i))) {
2646                                 targetVg.removeViewAt(i);
2647                             }
2648                         }
2649                         return;
2650                     }
2651 
2652                     removeAllViewsExceptIdToKeep(targetVg);
2653                 }
2654             };
2655         }
2656 
2657         /**
2658          * Iterates through the children in the given ViewGroup and removes all the views that
2659          * do not have an id of {@link #mViewIdToKeep}.
2660          */
2661         private void removeAllViewsExceptIdToKeep(ViewGroup viewGroup) {
2662             // Otherwise, remove all the views that do not match the id to keep.
2663             int index = viewGroup.getChildCount() - 1;
2664             while (index >= 0) {
2665                 if (viewGroup.getChildAt(index).getId() != mViewIdToKeep) {
2666                     viewGroup.removeViewAt(index);
2667                 }
2668                 index--;
2669             }
2670         }
2671 
2672         @Override
2673         public int getActionTag() {
2674             return VIEW_GROUP_ACTION_REMOVE_TAG;
2675         }
2676 
2677         @Override
2678         public int mergeBehavior() {
2679             return MERGE_APPEND;
2680         }
2681     }
2682 
2683     /**
2684      * Action to remove a view from its parent.
2685      */
2686     private class RemoveFromParentAction extends Action {
2687 
2688         RemoveFromParentAction(@IdRes int viewId) {
2689             this.viewId = viewId;
2690         }
2691 
2692         RemoveFromParentAction(Parcel parcel) {
2693             viewId = parcel.readInt();
2694         }
2695 
2696         public void writeToParcel(Parcel dest, int flags) {
2697             dest.writeInt(viewId);
2698         }
2699 
2700         @Override
2701         public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
2702                 ColorResources colorResources) {
2703             final View target = root.findViewById(viewId);
2704 
2705             if (target == null || target == root) {
2706                 return;
2707             }
2708 
2709             ViewParent parent = target.getParent();
2710             if (parent instanceof ViewManager) {
2711                 ((ViewManager) parent).removeView(target);
2712             }
2713         }
2714 
2715         @Override
2716         public Action initActionAsync(ViewTree root, ViewGroup rootParent,
2717                 InteractionHandler handler, ColorResources colorResources) {
2718             // In the async implementation, update the view tree so that subsequent calls to
2719             // findViewById return the correct view.
2720             root.createTree();
2721             ViewTree target = root.findViewTreeById(viewId);
2722 
2723             if (target == null || target == root) {
2724                 return ACTION_NOOP;
2725             }
2726 
2727             ViewTree parent = root.findViewTreeParentOf(target);
2728             if (parent == null || !(parent.mRoot instanceof ViewManager)) {
2729                 return ACTION_NOOP;
2730             }
2731             final ViewManager parentVg = (ViewManager) parent.mRoot;
2732 
2733             parent.mChildren.remove(target);
2734             return new RuntimeAction() {
2735                 @Override
2736                 public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
2737                         ColorResources colorResources) throws ActionException {
2738                     parentVg.removeView(target.mRoot);
2739                 }
2740             };
2741         }
2742 
2743         @Override
2744         public int getActionTag() {
2745             return REMOVE_FROM_PARENT_ACTION_TAG;
2746         }
2747 
2748         @Override
2749         public int mergeBehavior() {
2750             return MERGE_APPEND;
2751         }
2752     }
2753 
2754     /**
2755      * Helper action to set compound drawables on a TextView. Supports relative
2756      * (s/t/e/b) or cardinal (l/t/r/b) arrangement.
2757      */
2758     private class TextViewDrawableAction extends Action {
2759         public TextViewDrawableAction(@IdRes int viewId, boolean isRelative, @DrawableRes int d1,
2760                 @DrawableRes int d2, @DrawableRes int d3, @DrawableRes int d4) {
2761             this.viewId = viewId;
2762             this.isRelative = isRelative;
2763             this.useIcons = false;
2764             this.d1 = d1;
2765             this.d2 = d2;
2766             this.d3 = d3;
2767             this.d4 = d4;
2768         }
2769 
2770         public TextViewDrawableAction(@IdRes int viewId, boolean isRelative,
2771                 Icon i1, Icon i2, Icon i3, Icon i4) {
2772             this.viewId = viewId;
2773             this.isRelative = isRelative;
2774             this.useIcons = true;
2775             this.i1 = i1;
2776             this.i2 = i2;
2777             this.i3 = i3;
2778             this.i4 = i4;
2779         }
2780 
2781         public TextViewDrawableAction(Parcel parcel) {
2782             viewId = parcel.readInt();
2783             isRelative = (parcel.readInt() != 0);
2784             useIcons = (parcel.readInt() != 0);
2785             if (useIcons) {
2786                 i1 = parcel.readTypedObject(Icon.CREATOR);
2787                 i2 = parcel.readTypedObject(Icon.CREATOR);
2788                 i3 = parcel.readTypedObject(Icon.CREATOR);
2789                 i4 = parcel.readTypedObject(Icon.CREATOR);
2790             } else {
2791                 d1 = parcel.readInt();
2792                 d2 = parcel.readInt();
2793                 d3 = parcel.readInt();
2794                 d4 = parcel.readInt();
2795             }
2796         }
2797 
2798         public void writeToParcel(Parcel dest, int flags) {
2799             dest.writeInt(viewId);
2800             dest.writeInt(isRelative ? 1 : 0);
2801             dest.writeInt(useIcons ? 1 : 0);
2802             if (useIcons) {
2803                 dest.writeTypedObject(i1, 0);
2804                 dest.writeTypedObject(i2, 0);
2805                 dest.writeTypedObject(i3, 0);
2806                 dest.writeTypedObject(i4, 0);
2807             } else {
2808                 dest.writeInt(d1);
2809                 dest.writeInt(d2);
2810                 dest.writeInt(d3);
2811                 dest.writeInt(d4);
2812             }
2813         }
2814 
2815         @Override
2816         public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
2817                 ColorResources colorResources) {
2818             final TextView target = root.findViewById(viewId);
2819             if (target == null) return;
2820             if (drawablesLoaded) {
2821                 if (isRelative) {
2822                     target.setCompoundDrawablesRelativeWithIntrinsicBounds(id1, id2, id3, id4);
2823                 } else {
2824                     target.setCompoundDrawablesWithIntrinsicBounds(id1, id2, id3, id4);
2825                 }
2826             } else if (useIcons) {
2827                 final Context ctx = target.getContext();
2828                 final Drawable id1 = i1 == null ? null : i1.loadDrawable(ctx);
2829                 final Drawable id2 = i2 == null ? null : i2.loadDrawable(ctx);
2830                 final Drawable id3 = i3 == null ? null : i3.loadDrawable(ctx);
2831                 final Drawable id4 = i4 == null ? null : i4.loadDrawable(ctx);
2832                 if (isRelative) {
2833                     target.setCompoundDrawablesRelativeWithIntrinsicBounds(id1, id2, id3, id4);
2834                 } else {
2835                     target.setCompoundDrawablesWithIntrinsicBounds(id1, id2, id3, id4);
2836                 }
2837             } else {
2838                 if (isRelative) {
2839                     target.setCompoundDrawablesRelativeWithIntrinsicBounds(d1, d2, d3, d4);
2840                 } else {
2841                     target.setCompoundDrawablesWithIntrinsicBounds(d1, d2, d3, d4);
2842                 }
2843             }
2844         }
2845 
2846         @Override
2847         public Action initActionAsync(ViewTree root, ViewGroup rootParent,
2848                 InteractionHandler handler, ColorResources colorResources) {
2849             final TextView target = root.findViewById(viewId);
2850             if (target == null) return ACTION_NOOP;
2851 
2852             TextViewDrawableAction copy = useIcons ?
2853                     new TextViewDrawableAction(viewId, isRelative, i1, i2, i3, i4) :
2854                     new TextViewDrawableAction(viewId, isRelative, d1, d2, d3, d4);
2855 
2856             // Load the drawables on the background thread.
2857             copy.drawablesLoaded = true;
2858             final Context ctx = target.getContext();
2859 
2860             if (useIcons) {
2861                 copy.id1 = i1 == null ? null : i1.loadDrawable(ctx);
2862                 copy.id2 = i2 == null ? null : i2.loadDrawable(ctx);
2863                 copy.id3 = i3 == null ? null : i3.loadDrawable(ctx);
2864                 copy.id4 = i4 == null ? null : i4.loadDrawable(ctx);
2865             } else {
2866                 copy.id1 = d1 == 0 ? null : ctx.getDrawable(d1);
2867                 copy.id2 = d2 == 0 ? null : ctx.getDrawable(d2);
2868                 copy.id3 = d3 == 0 ? null : ctx.getDrawable(d3);
2869                 copy.id4 = d4 == 0 ? null : ctx.getDrawable(d4);
2870             }
2871             return copy;
2872         }
2873 
2874         @Override
2875         public boolean prefersAsyncApply() {
2876             return useIcons;
2877         }
2878 
2879         @Override
2880         public int getActionTag() {
2881             return TEXT_VIEW_DRAWABLE_ACTION_TAG;
2882         }
2883 
2884         @Override
2885         public void visitUris(@NonNull Consumer<Uri> visitor) {
2886             if (useIcons) {
2887                 visitIconUri(i1, visitor);
2888                 visitIconUri(i2, visitor);
2889                 visitIconUri(i3, visitor);
2890                 visitIconUri(i4, visitor);
2891             }
2892         }
2893 
2894         boolean isRelative = false;
2895         boolean useIcons = false;
2896         int d1, d2, d3, d4;
2897         Icon i1, i2, i3, i4;
2898 
2899         boolean drawablesLoaded = false;
2900         Drawable id1, id2, id3, id4;
2901     }
2902 
2903     /**
2904      * Helper action to set text size on a TextView in any supported units.
2905      */
2906     private class TextViewSizeAction extends Action {
2907         TextViewSizeAction(@IdRes int viewId, @ComplexDimensionUnit int units, float size) {
2908             this.viewId = viewId;
2909             this.units = units;
2910             this.size = size;
2911         }
2912 
2913         TextViewSizeAction(Parcel parcel) {
2914             viewId = parcel.readInt();
2915             units = parcel.readInt();
2916             size  = parcel.readFloat();
2917         }
2918 
2919         public void writeToParcel(Parcel dest, int flags) {
2920             dest.writeInt(viewId);
2921             dest.writeInt(units);
2922             dest.writeFloat(size);
2923         }
2924 
2925         @Override
2926         public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
2927                 ColorResources colorResources) {
2928             final TextView target = root.findViewById(viewId);
2929             if (target == null) return;
2930             target.setTextSize(units, size);
2931         }
2932 
2933         @Override
2934         public int getActionTag() {
2935             return TEXT_VIEW_SIZE_ACTION_TAG;
2936         }
2937 
2938         int units;
2939         float size;
2940     }
2941 
2942     /**
2943      * Helper action to set padding on a View.
2944      */
2945     private class ViewPaddingAction extends Action {
2946         public ViewPaddingAction(@IdRes int viewId, @Px int left, @Px int top,
2947                 @Px int right, @Px int bottom) {
2948             this.viewId = viewId;
2949             this.left = left;
2950             this.top = top;
2951             this.right = right;
2952             this.bottom = bottom;
2953         }
2954 
2955         public ViewPaddingAction(Parcel parcel) {
2956             viewId = parcel.readInt();
2957             left = parcel.readInt();
2958             top = parcel.readInt();
2959             right = parcel.readInt();
2960             bottom = parcel.readInt();
2961         }
2962 
2963         public void writeToParcel(Parcel dest, int flags) {
2964             dest.writeInt(viewId);
2965             dest.writeInt(left);
2966             dest.writeInt(top);
2967             dest.writeInt(right);
2968             dest.writeInt(bottom);
2969         }
2970 
2971         @Override
2972         public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
2973                 ColorResources colorResources) {
2974             final View target = root.findViewById(viewId);
2975             if (target == null) return;
2976             target.setPadding(left, top, right, bottom);
2977         }
2978 
2979         @Override
2980         public int getActionTag() {
2981             return VIEW_PADDING_ACTION_TAG;
2982         }
2983 
2984         @Px int left, top, right, bottom;
2985     }
2986 
2987     /**
2988      * Helper action to set layout params on a View.
2989      */
2990     private static class LayoutParamAction extends Action {
2991 
2992         static final int LAYOUT_MARGIN_LEFT = MARGIN_LEFT;
2993         static final int LAYOUT_MARGIN_TOP = MARGIN_TOP;
2994         static final int LAYOUT_MARGIN_RIGHT = MARGIN_RIGHT;
2995         static final int LAYOUT_MARGIN_BOTTOM = MARGIN_BOTTOM;
2996         static final int LAYOUT_MARGIN_START = MARGIN_START;
2997         static final int LAYOUT_MARGIN_END = MARGIN_END;
2998         static final int LAYOUT_WIDTH = 8;
2999         static final int LAYOUT_HEIGHT = 9;
3000 
3001         final int mProperty;
3002         final int mValueType;
3003         final int mValue;
3004 
3005         /**
3006          * @param viewId ID of the view alter
3007          * @param property which layout parameter to alter
3008          * @param value new value of the layout parameter
3009          * @param units the units of the given value
3010          */
3011         LayoutParamAction(@IdRes int viewId, int property, float value,
3012                 @ComplexDimensionUnit int units) {
3013             this.viewId = viewId;
3014             this.mProperty = property;
3015             this.mValueType = VALUE_TYPE_COMPLEX_UNIT;
3016             this.mValue = TypedValue.createComplexDimension(value, units);
3017         }
3018 
3019         /**
3020          * @param viewId ID of the view alter
3021          * @param property which layout parameter to alter
3022          * @param value value to set.
3023          * @param valueType must be one of {@link #VALUE_TYPE_COMPLEX_UNIT},
3024          *   {@link #VALUE_TYPE_RESOURCE}, {@link #VALUE_TYPE_ATTRIBUTE} or
3025          *   {@link #VALUE_TYPE_RAW}.
3026          */
3027         LayoutParamAction(@IdRes int viewId, int property, int value, @ValueType int valueType) {
3028             this.viewId = viewId;
3029             this.mProperty = property;
3030             this.mValueType = valueType;
3031             this.mValue = value;
3032         }
3033 
3034         public LayoutParamAction(Parcel parcel) {
3035             viewId = parcel.readInt();
3036             mProperty = parcel.readInt();
3037             mValueType = parcel.readInt();
3038             mValue = parcel.readInt();
3039         }
3040 
3041         public void writeToParcel(Parcel dest, int flags) {
3042             dest.writeInt(viewId);
3043             dest.writeInt(mProperty);
3044             dest.writeInt(mValueType);
3045             dest.writeInt(mValue);
3046         }
3047 
3048         @Override
3049         public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
3050                 ColorResources colorResources) {
3051             final View target = root.findViewById(viewId);
3052             if (target == null) {
3053                 return;
3054             }
3055             ViewGroup.LayoutParams layoutParams = target.getLayoutParams();
3056             if (layoutParams == null) {
3057                 return;
3058             }
3059             switch (mProperty) {
3060                 case LAYOUT_MARGIN_LEFT:
3061                     if (layoutParams instanceof MarginLayoutParams) {
3062                         ((MarginLayoutParams) layoutParams).leftMargin = getPixelOffset(target);
3063                         target.setLayoutParams(layoutParams);
3064                     }
3065                     break;
3066                 case LAYOUT_MARGIN_TOP:
3067                     if (layoutParams instanceof MarginLayoutParams) {
3068                         ((MarginLayoutParams) layoutParams).topMargin = getPixelOffset(target);
3069                         target.setLayoutParams(layoutParams);
3070                     }
3071                     break;
3072                 case LAYOUT_MARGIN_RIGHT:
3073                     if (layoutParams instanceof MarginLayoutParams) {
3074                         ((MarginLayoutParams) layoutParams).rightMargin = getPixelOffset(target);
3075                         target.setLayoutParams(layoutParams);
3076                     }
3077                     break;
3078                 case LAYOUT_MARGIN_BOTTOM:
3079                     if (layoutParams instanceof MarginLayoutParams) {
3080                         ((MarginLayoutParams) layoutParams).bottomMargin = getPixelOffset(target);
3081                         target.setLayoutParams(layoutParams);
3082                     }
3083                     break;
3084                 case LAYOUT_MARGIN_START:
3085                     if (layoutParams instanceof MarginLayoutParams) {
3086                         ((MarginLayoutParams) layoutParams).setMarginStart(getPixelOffset(target));
3087                         target.setLayoutParams(layoutParams);
3088                     }
3089                     break;
3090                 case LAYOUT_MARGIN_END:
3091                     if (layoutParams instanceof MarginLayoutParams) {
3092                         ((MarginLayoutParams) layoutParams).setMarginEnd(getPixelOffset(target));
3093                         target.setLayoutParams(layoutParams);
3094                     }
3095                     break;
3096                 case LAYOUT_WIDTH:
3097                     layoutParams.width = getPixelSize(target);
3098                     target.setLayoutParams(layoutParams);
3099                     break;
3100                 case LAYOUT_HEIGHT:
3101                     layoutParams.height = getPixelSize(target);
3102                     target.setLayoutParams(layoutParams);
3103                     break;
3104                 default:
3105                     throw new IllegalArgumentException("Unknown property " + mProperty);
3106             }
3107         }
3108 
3109         private int getPixelOffset(View target) {
3110             try {
3111                 switch (mValueType) {
3112                     case VALUE_TYPE_ATTRIBUTE:
3113                         TypedArray typedArray = target.getContext().obtainStyledAttributes(
3114                                 new int[]{this.mValue});
3115                         try {
3116                             return typedArray.getDimensionPixelOffset(0, 0);
3117                         } finally {
3118                             typedArray.recycle();
3119                         }
3120                     case VALUE_TYPE_RESOURCE:
3121                         if (mValue == 0) {
3122                             return 0;
3123                         }
3124                         return target.getResources().getDimensionPixelOffset(mValue);
3125                     case VALUE_TYPE_COMPLEX_UNIT:
3126                         return TypedValue.complexToDimensionPixelOffset(mValue,
3127                                 target.getResources().getDisplayMetrics());
3128                     default:
3129                         return mValue;
3130                 }
3131             } catch (Throwable t) {
3132                 throw new ActionException(t);
3133             }
3134         }
3135 
3136         private int getPixelSize(View target) {
3137             try {
3138                 switch (mValueType) {
3139                     case VALUE_TYPE_ATTRIBUTE:
3140                         TypedArray typedArray = target.getContext().obtainStyledAttributes(
3141                                 new int[]{this.mValue});
3142                         try {
3143                             return typedArray.getDimensionPixelSize(0, 0);
3144                         } finally {
3145                             typedArray.recycle();
3146                         }
3147                     case VALUE_TYPE_RESOURCE:
3148                         if (mValue == 0) {
3149                             return 0;
3150                         }
3151                         return target.getResources().getDimensionPixelSize(mValue);
3152                     case VALUE_TYPE_COMPLEX_UNIT:
3153                         return TypedValue.complexToDimensionPixelSize(mValue,
3154                                 target.getResources().getDisplayMetrics());
3155                     default:
3156                         return mValue;
3157                 }
3158             } catch (Throwable t) {
3159                 throw new ActionException(t);
3160             }
3161         }
3162 
3163         @Override
3164         public int getActionTag() {
3165             return LAYOUT_PARAM_ACTION_TAG;
3166         }
3167 
3168         @Override
3169         public String getUniqueKey() {
3170             return super.getUniqueKey() + mProperty;
3171         }
3172     }
3173 
3174     /**
3175      * Helper action to add a view tag with RemoteInputs.
3176      */
3177     private class SetRemoteInputsAction extends Action {
3178 
3179         public SetRemoteInputsAction(@IdRes int viewId, RemoteInput[] remoteInputs) {
3180             this.viewId = viewId;
3181             this.remoteInputs = remoteInputs;
3182         }
3183 
3184         public SetRemoteInputsAction(Parcel parcel) {
3185             viewId = parcel.readInt();
3186             remoteInputs = parcel.createTypedArray(RemoteInput.CREATOR);
3187         }
3188 
3189         public void writeToParcel(Parcel dest, int flags) {
3190             dest.writeInt(viewId);
3191             dest.writeTypedArray(remoteInputs, flags);
3192         }
3193 
3194         @Override
3195         public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
3196                 ColorResources colorResources) {
3197             final View target = root.findViewById(viewId);
3198             if (target == null) return;
3199 
3200             target.setTagInternal(R.id.remote_input_tag, remoteInputs);
3201         }
3202 
3203         @Override
3204         public int getActionTag() {
3205             return SET_REMOTE_INPUTS_ACTION_TAG;
3206         }
3207 
3208         final Parcelable[] remoteInputs;
3209     }
3210 
3211     /**
3212      * Helper action to override all textViewColors
3213      */
3214     private class OverrideTextColorsAction extends Action {
3215 
3216         private final int textColor;
3217 
3218         public OverrideTextColorsAction(int textColor) {
3219             this.textColor = textColor;
3220         }
3221 
3222         public OverrideTextColorsAction(Parcel parcel) {
3223             textColor = parcel.readInt();
3224         }
3225 
3226         public void writeToParcel(Parcel dest, int flags) {
3227             dest.writeInt(textColor);
3228         }
3229 
3230         @Override
3231         public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
3232                 ColorResources colorResources) {
3233             // Let's traverse the viewtree and override all textColors!
3234             Stack<View> viewsToProcess = new Stack<>();
3235             viewsToProcess.add(root);
3236             while (!viewsToProcess.isEmpty()) {
3237                 View v = viewsToProcess.pop();
3238                 if (v instanceof TextView) {
3239                     TextView textView = (TextView) v;
3240                     textView.setText(ContrastColorUtil.clearColorSpans(textView.getText()));
3241                     textView.setTextColor(textColor);
3242                 }
3243                 if (v instanceof ViewGroup) {
3244                     ViewGroup viewGroup = (ViewGroup) v;
3245                     for (int i = 0; i < viewGroup.getChildCount(); i++) {
3246                         viewsToProcess.push(viewGroup.getChildAt(i));
3247                     }
3248                 }
3249             }
3250         }
3251 
3252         @Override
3253         public int getActionTag() {
3254             return OVERRIDE_TEXT_COLORS_TAG;
3255         }
3256     }
3257 
3258     private class SetIntTagAction extends Action {
3259         @IdRes private final int mViewId;
3260         @IdRes private final int mKey;
3261         private final int mTag;
3262 
3263         SetIntTagAction(@IdRes int viewId, @IdRes int key, int tag) {
3264             mViewId = viewId;
3265             mKey = key;
3266             mTag = tag;
3267         }
3268 
3269         SetIntTagAction(Parcel parcel) {
3270             mViewId = parcel.readInt();
3271             mKey = parcel.readInt();
3272             mTag = parcel.readInt();
3273         }
3274 
3275         public void writeToParcel(Parcel dest, int flags) {
3276             dest.writeInt(mViewId);
3277             dest.writeInt(mKey);
3278             dest.writeInt(mTag);
3279         }
3280 
3281         @Override
3282         public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
3283                 ColorResources colorResources) {
3284             final View target = root.findViewById(mViewId);
3285             if (target == null) return;
3286 
3287             target.setTagInternal(mKey, mTag);
3288         }
3289 
3290         @Override
3291         public int getActionTag() {
3292             return SET_INT_TAG_TAG;
3293         }
3294     }
3295 
3296     private static class SetCompoundButtonCheckedAction extends Action {
3297 
3298         private final boolean mChecked;
3299 
3300         SetCompoundButtonCheckedAction(@IdRes int viewId, boolean checked) {
3301             this.viewId = viewId;
3302             mChecked = checked;
3303         }
3304 
3305         SetCompoundButtonCheckedAction(Parcel in) {
3306             viewId = in.readInt();
3307             mChecked = in.readBoolean();
3308         }
3309 
3310         @Override
3311         public void writeToParcel(Parcel dest, int flags) {
3312             dest.writeInt(viewId);
3313             dest.writeBoolean(mChecked);
3314         }
3315 
3316         @Override
3317         public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
3318                 ColorResources colorResources)
3319                 throws ActionException {
3320             final View target = root.findViewById(viewId);
3321             if (target == null) return;
3322 
3323             if (!(target instanceof CompoundButton)) {
3324                 Log.w(LOG_TAG, "Cannot set checked to view "
3325                         + viewId + " because it is not a CompoundButton");
3326                 return;
3327             }
3328 
3329             CompoundButton button = (CompoundButton) target;
3330             Object tag = button.getTag(R.id.remote_checked_change_listener_tag);
3331             // Temporarily unset the checked change listener so calling setChecked doesn't launch
3332             // the intent.
3333             if (tag instanceof OnCheckedChangeListener) {
3334                 button.setOnCheckedChangeListener(null);
3335                 button.setChecked(mChecked);
3336                 button.setOnCheckedChangeListener((OnCheckedChangeListener) tag);
3337             } else {
3338                 button.setChecked(mChecked);
3339             }
3340         }
3341 
3342         @Override
3343         public int getActionTag() {
3344             return SET_COMPOUND_BUTTON_CHECKED_TAG;
3345         }
3346     }
3347 
3348     private static class SetRadioGroupCheckedAction extends Action {
3349 
3350         @IdRes private final int mCheckedId;
3351 
3352         SetRadioGroupCheckedAction(@IdRes int viewId, @IdRes int checkedId) {
3353             this.viewId = viewId;
3354             mCheckedId = checkedId;
3355         }
3356 
3357         SetRadioGroupCheckedAction(Parcel in) {
3358             viewId = in.readInt();
3359             mCheckedId = in.readInt();
3360         }
3361 
3362         @Override
3363         public void writeToParcel(Parcel dest, int flags) {
3364             dest.writeInt(viewId);
3365             dest.writeInt(mCheckedId);
3366         }
3367 
3368         @Override
3369         public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
3370                 ColorResources colorResources) throws ActionException {
3371             final View target = root.findViewById(viewId);
3372             if (target == null) return;
3373 
3374             if (!(target instanceof RadioGroup)) {
3375                 Log.w(LOG_TAG, "Cannot check " + viewId + " because it's not a RadioGroup");
3376                 return;
3377             }
3378 
3379             RadioGroup group = (RadioGroup) target;
3380 
3381             // Temporarily unset all the checked change listeners while we check the group.
3382             for (int i = 0; i < group.getChildCount(); i++) {
3383                 View child = group.getChildAt(i);
3384                 if (!(child instanceof CompoundButton)) continue;
3385 
3386                 Object tag = child.getTag(R.id.remote_checked_change_listener_tag);
3387                 if (!(tag instanceof OnCheckedChangeListener)) continue;
3388 
3389                 // Clear the checked change listener, we'll restore it after the check.
3390                 ((CompoundButton) child).setOnCheckedChangeListener(null);
3391             }
3392 
3393             group.check(mCheckedId);
3394 
3395             // Loop through the children again and restore the checked change listeners.
3396             for (int i = 0; i < group.getChildCount(); i++) {
3397                 View child = group.getChildAt(i);
3398                 if (!(child instanceof CompoundButton)) continue;
3399 
3400                 Object tag = child.getTag(R.id.remote_checked_change_listener_tag);
3401                 if (!(tag instanceof OnCheckedChangeListener)) continue;
3402 
3403                 ((CompoundButton) child).setOnCheckedChangeListener((OnCheckedChangeListener) tag);
3404             }
3405         }
3406 
3407         @Override
3408         public int getActionTag() {
3409             return SET_RADIO_GROUP_CHECKED;
3410         }
3411     }
3412 
3413     private static class SetViewOutlinePreferredRadiusAction extends Action {
3414 
3415         @ValueType
3416         private final int mValueType;
3417         private final int mValue;
3418 
3419         SetViewOutlinePreferredRadiusAction(@IdRes int viewId, int value,
3420                 @ValueType int valueType) {
3421             this.viewId = viewId;
3422             this.mValueType = valueType;
3423             this.mValue = value;
3424         }
3425 
3426         SetViewOutlinePreferredRadiusAction(
3427                 @IdRes int viewId, float radius, @ComplexDimensionUnit int units) {
3428             this.viewId = viewId;
3429             this.mValueType = VALUE_TYPE_COMPLEX_UNIT;
3430             this.mValue = TypedValue.createComplexDimension(radius, units);
3431 
3432         }
3433 
3434         SetViewOutlinePreferredRadiusAction(Parcel in) {
3435             viewId = in.readInt();
3436             mValueType = in.readInt();
3437             mValue = in.readInt();
3438         }
3439 
3440         @Override
3441         public void writeToParcel(Parcel dest, int flags) {
3442             dest.writeInt(viewId);
3443             dest.writeInt(mValueType);
3444             dest.writeInt(mValue);
3445         }
3446 
3447         @Override
3448         public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
3449                 ColorResources colorResources) throws ActionException {
3450             final View target = root.findViewById(viewId);
3451             if (target == null) return;
3452 
3453             try {
3454                 float radius;
3455                 switch (mValueType) {
3456                     case VALUE_TYPE_ATTRIBUTE:
3457                         TypedArray typedArray = target.getContext().obtainStyledAttributes(
3458                                 new int[]{mValue});
3459                         try {
3460                             radius = typedArray.getDimension(0, 0);
3461                         } finally {
3462                             typedArray.recycle();
3463                         }
3464                         break;
3465                     case VALUE_TYPE_RESOURCE:
3466                         radius = mValue == 0 ? 0 : target.getResources().getDimension(mValue);
3467                         break;
3468                     case VALUE_TYPE_COMPLEX_UNIT:
3469                         radius = TypedValue.complexToDimension(mValue,
3470                                 target.getResources().getDisplayMetrics());
3471                         break;
3472                     default:
3473                         radius = mValue;
3474                 }
3475                 target.setOutlineProvider(new RemoteViewOutlineProvider(radius));
3476             } catch (Throwable t) {
3477                 throw new ActionException(t);
3478             }
3479         }
3480 
3481         @Override
3482         public int getActionTag() {
3483             return SET_VIEW_OUTLINE_RADIUS_TAG;
3484         }
3485     }
3486 
3487     /**
3488      * OutlineProvider for a view with a radius set by
3489      * {@link #setViewOutlinePreferredRadius(int, float, int)}.
3490      */
3491     public static final class RemoteViewOutlineProvider extends ViewOutlineProvider {
3492 
3493         private final float mRadius;
3494 
3495         public RemoteViewOutlineProvider(float radius) {
3496             mRadius = radius;
3497         }
3498 
3499         /** Returns the corner radius used when providing the view outline. */
3500         public float getRadius() {
3501             return mRadius;
3502         }
3503 
3504         @Override
3505         public void getOutline(@NonNull View view, @NonNull Outline outline) {
3506             outline.setRoundRect(
3507                     0 /*left*/,
3508                     0 /* top */,
3509                     view.getWidth() /* right */,
3510                     view.getHeight() /* bottom */,
3511                     mRadius);
3512         }
3513     }
3514 
3515     /**
3516      * Create a new RemoteViews object that will display the views contained
3517      * in the specified layout file.
3518      *
3519      * @param packageName Name of the package that contains the layout resource
3520      * @param layoutId The id of the layout resource
3521      */
3522     public RemoteViews(String packageName, int layoutId) {
3523         this(getApplicationInfo(packageName, UserHandle.myUserId()), layoutId);
3524     }
3525 
3526     /**
3527      * Create a new RemoteViews object that will display the views contained
3528      * in the specified layout file and change the id of the root view to the specified one.
3529      *
3530      * @param packageName Name of the package that contains the layout resource
3531      * @param layoutId The id of the layout resource
3532      */
3533     public RemoteViews(@NonNull String packageName, @LayoutRes int layoutId, @IdRes int viewId) {
3534         this(packageName, layoutId);
3535         this.mViewId = viewId;
3536     }
3537 
3538     /**
3539      * Create a new RemoteViews object that will display the views contained
3540      * in the specified layout file.
3541      *
3542      * @param application The application whose content is shown by the views.
3543      * @param layoutId The id of the layout resource.
3544      *
3545      * @hide
3546      */
3547     protected RemoteViews(ApplicationInfo application, @LayoutRes int layoutId) {
3548         mApplication = application;
3549         mLayoutId = layoutId;
3550         mApplicationInfoCache.put(application);
3551     }
3552 
3553     private boolean hasMultipleLayouts() {
3554         return hasLandscapeAndPortraitLayouts() || hasSizedRemoteViews();
3555     }
3556 
3557     private boolean hasLandscapeAndPortraitLayouts() {
3558         return (mLandscape != null) && (mPortrait != null);
3559     }
3560 
3561     private boolean hasSizedRemoteViews() {
3562         return mSizedRemoteViews != null;
3563     }
3564 
3565     private @Nullable SizeF getIdealSize() {
3566         return mIdealSize;
3567     }
3568 
3569     private void setIdealSize(@Nullable SizeF size) {
3570         mIdealSize = size;
3571     }
3572 
3573     /**
3574      * Finds the smallest view in {@code mSizedRemoteViews}.
3575      * This method must not be called if {@code mSizedRemoteViews} is null.
3576      */
3577     private RemoteViews findSmallestRemoteView() {
3578         return mSizedRemoteViews.get(mSizedRemoteViews.size() - 1);
3579     }
3580 
3581     /**
3582      * Create a new RemoteViews object that will inflate as the specified
3583      * landspace or portrait RemoteViews, depending on the current configuration.
3584      *
3585      * @param landscape The RemoteViews to inflate in landscape configuration
3586      * @param portrait The RemoteViews to inflate in portrait configuration
3587      * @throws IllegalArgumentException if either landscape or portrait are null or if they are
3588      *   not from the same application
3589      */
3590     public RemoteViews(RemoteViews landscape, RemoteViews portrait) {
3591         if (landscape == null || portrait == null) {
3592             throw new IllegalArgumentException("Both RemoteViews must be non-null");
3593         }
3594         if (!landscape.hasSameAppInfo(portrait.mApplication)) {
3595             throw new IllegalArgumentException(
3596                     "Both RemoteViews must share the same package and user");
3597         }
3598         mApplication = portrait.mApplication;
3599         mLayoutId = portrait.mLayoutId;
3600         mViewId = portrait.mViewId;
3601         mLightBackgroundLayoutId = portrait.mLightBackgroundLayoutId;
3602 
3603         mLandscape = landscape;
3604         mPortrait = portrait;
3605 
3606         mClassCookies = (portrait.mClassCookies != null)
3607                 ? portrait.mClassCookies : landscape.mClassCookies;
3608 
3609         configureDescendantsAsChildren();
3610     }
3611 
3612     /**
3613      * Create a new RemoteViews object that will inflate the layout with the closest size
3614      * specification.
3615      *
3616      * The default remote views in that case is always the one with the smallest area.
3617      *
3618      * If the {@link RemoteViews} host provides the size of the view, the layout with the largest
3619      * area that fits entirely in the provided size will be used (i.e. the width and height of
3620      * the layout must be less than the size of the view, with a 1dp margin to account for
3621      * rounding). If no layout fits in the view, the layout with the smallest area will be used.
3622      *
3623      * @param remoteViews Mapping of size to layout.
3624      * @throws IllegalArgumentException if the map is empty, there are more than
3625      *   MAX_INIT_VIEW_COUNT layouts or the remote views are not all from the same application.
3626      */
3627     public RemoteViews(@NonNull Map<SizeF, RemoteViews> remoteViews) {
3628         if (remoteViews.isEmpty()) {
3629             throw new IllegalArgumentException("The set of RemoteViews cannot be empty");
3630         }
3631         if (remoteViews.size() > MAX_INIT_VIEW_COUNT) {
3632             throw new IllegalArgumentException("Too many RemoteViews in constructor");
3633         }
3634         if (remoteViews.size() == 1) {
3635             // If the map only contains a single mapping, treat this as if that RemoteViews was
3636             // passed as the top-level RemoteViews.
3637             RemoteViews single = remoteViews.values().iterator().next();
3638             initializeFrom(single, /* hierarchyRoot= */ single);
3639             return;
3640         }
3641         mClassCookies = initializeSizedRemoteViews(
3642                 remoteViews.entrySet().stream().map(
3643                         entry -> {
3644                             entry.getValue().setIdealSize(entry.getKey());
3645                             return entry.getValue();
3646                         }
3647                 ).iterator()
3648         );
3649 
3650         RemoteViews smallestView = findSmallestRemoteView();
3651         mApplication = smallestView.mApplication;
3652         mLayoutId = smallestView.mLayoutId;
3653         mViewId = smallestView.mViewId;
3654         mLightBackgroundLayoutId = smallestView.mLightBackgroundLayoutId;
3655 
3656         configureDescendantsAsChildren();
3657     }
3658 
3659     // Initialize mSizedRemoteViews and return the class cookies.
3660     private Map<Class, Object> initializeSizedRemoteViews(Iterator<RemoteViews> remoteViews) {
3661         List<RemoteViews> sizedRemoteViews = new ArrayList<>();
3662         Map<Class, Object> classCookies = null;
3663         float viewArea = Float.MAX_VALUE;
3664         RemoteViews smallestView = null;
3665         while (remoteViews.hasNext()) {
3666             RemoteViews view = remoteViews.next();
3667             SizeF size = view.getIdealSize();
3668             if (size == null) {
3669                 throw new IllegalStateException("Expected RemoteViews to have ideal size");
3670             }
3671             float newViewArea = size.getWidth() * size.getHeight();
3672             if (smallestView != null && !view.hasSameAppInfo(smallestView.mApplication)) {
3673                 throw new IllegalArgumentException(
3674                         "All RemoteViews must share the same package and user");
3675             }
3676             if (smallestView == null || newViewArea < viewArea) {
3677                 if (smallestView != null) {
3678                     sizedRemoteViews.add(smallestView);
3679                 }
3680                 viewArea = newViewArea;
3681                 smallestView = view;
3682             } else {
3683                 sizedRemoteViews.add(view);
3684             }
3685             view.setIdealSize(size);
3686             if (classCookies == null) {
3687                 classCookies = view.mClassCookies;
3688             }
3689         }
3690         sizedRemoteViews.add(smallestView);
3691         mSizedRemoteViews = sizedRemoteViews;
3692         return classCookies;
3693     }
3694 
3695     /**
3696      * Creates a copy of another RemoteViews.
3697      */
3698     public RemoteViews(RemoteViews src) {
3699         initializeFrom(src, /* hierarchyRoot= */ null);
3700     }
3701 
3702     /**
3703      * No-arg constructor for use with {@link #initializeFrom(RemoteViews, RemoteViews)}. A
3704      * constructor taking two RemoteViews parameters would clash with the landscape/portrait
3705      * constructor.
3706      */
3707     private RemoteViews() {}
3708 
3709     private static RemoteViews createInitializedFrom(@NonNull RemoteViews src,
3710             @Nullable RemoteViews hierarchyRoot) {
3711         RemoteViews child = new RemoteViews();
3712         child.initializeFrom(src, hierarchyRoot);
3713         return child;
3714     }
3715 
3716     private void initializeFrom(@NonNull RemoteViews src, @Nullable RemoteViews hierarchyRoot) {
3717         if (hierarchyRoot == null) {
3718             mBitmapCache = src.mBitmapCache;
3719             mApplicationInfoCache = src.mApplicationInfoCache;
3720         } else {
3721             mBitmapCache = hierarchyRoot.mBitmapCache;
3722             mApplicationInfoCache = hierarchyRoot.mApplicationInfoCache;
3723         }
3724         if (hierarchyRoot == null || src.mIsRoot) {
3725             // If there's no provided root, or if src was itself a root, then this RemoteViews is
3726             // the root of the new hierarchy.
3727             mIsRoot = true;
3728             hierarchyRoot = this;
3729         } else {
3730             // Otherwise, we're a descendant in the hierarchy.
3731             mIsRoot = false;
3732         }
3733         mApplication = src.mApplication;
3734         mLayoutId = src.mLayoutId;
3735         mLightBackgroundLayoutId = src.mLightBackgroundLayoutId;
3736         mApplyFlags = src.mApplyFlags;
3737         mClassCookies = src.mClassCookies;
3738         mIdealSize = src.mIdealSize;
3739         mProviderInstanceId = src.mProviderInstanceId;
3740 
3741         if (src.hasLandscapeAndPortraitLayouts()) {
3742             mLandscape = createInitializedFrom(src.mLandscape, hierarchyRoot);
3743             mPortrait = createInitializedFrom(src.mPortrait, hierarchyRoot);
3744         }
3745 
3746         if (src.hasSizedRemoteViews()) {
3747             mSizedRemoteViews = new ArrayList<>(src.mSizedRemoteViews.size());
3748             for (RemoteViews srcView : src.mSizedRemoteViews) {
3749                 mSizedRemoteViews.add(createInitializedFrom(srcView, hierarchyRoot));
3750             }
3751         }
3752 
3753         if (src.mActions != null) {
3754             Parcel p = Parcel.obtain();
3755             p.putClassCookies(mClassCookies);
3756             src.writeActionsToParcel(p, /* flags= */ 0);
3757             p.setDataPosition(0);
3758             // Since src is already in memory, we do not care about stack overflow as it has
3759             // already been read once.
3760             readActionsFromParcel(p, 0);
3761             p.recycle();
3762         }
3763 
3764         // Now that everything is initialized and duplicated, create new caches for this
3765         // RemoteViews and recursively set up all descendants.
3766         if (mIsRoot) {
3767             reconstructCaches();
3768         }
3769     }
3770 
3771     /**
3772      * Reads a RemoteViews object from a parcel.
3773      *
3774      * @param parcel
3775      */
3776     public RemoteViews(Parcel parcel) {
3777         this(parcel, /* rootParent= */ null, /* info= */ null, /* depth= */ 0);
3778     }
3779 
3780     private RemoteViews(@NonNull Parcel parcel, @Nullable HierarchyRootData rootData,
3781             @Nullable ApplicationInfo info, int depth) {
3782         if (depth > MAX_NESTED_VIEWS
3783                 && (UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID)) {
3784             throw new IllegalArgumentException("Too many nested views.");
3785         }
3786         depth++;
3787 
3788         int mode = parcel.readInt();
3789 
3790         if (rootData == null) {
3791             // We only store a bitmap cache in the root of the RemoteViews.
3792             mBitmapCache = new BitmapCache(parcel);
3793             // Store the class cookies such that they are available when we clone this RemoteView.
3794             mClassCookies = parcel.copyClassCookies();
3795         } else {
3796             configureAsChild(rootData);
3797         }
3798 
3799         if (mode == MODE_NORMAL) {
3800             mApplication = ApplicationInfo.CREATOR.createFromParcel(parcel);
3801             mIdealSize = parcel.readInt() == 0 ? null : SizeF.CREATOR.createFromParcel(parcel);
3802             mLayoutId = parcel.readInt();
3803             mViewId = parcel.readInt();
3804             mLightBackgroundLayoutId = parcel.readInt();
3805 
3806             readActionsFromParcel(parcel, depth);
3807         } else if (mode == MODE_HAS_SIZED_REMOTEVIEWS) {
3808             int numViews = parcel.readInt();
3809             if (numViews > MAX_INIT_VIEW_COUNT) {
3810                 throw new IllegalArgumentException(
3811                         "Too many views in mapping from size to RemoteViews.");
3812             }
3813             List<RemoteViews> remoteViews = new ArrayList<>(numViews);
3814             for (int i = 0; i < numViews; i++) {
3815                 RemoteViews view = new RemoteViews(parcel, getHierarchyRootData(), info, depth);
3816                 info = view.mApplication;
3817                 remoteViews.add(view);
3818             }
3819             initializeSizedRemoteViews(remoteViews.iterator());
3820             RemoteViews smallestView = findSmallestRemoteView();
3821             mApplication = smallestView.mApplication;
3822             mLayoutId = smallestView.mLayoutId;
3823             mViewId = smallestView.mViewId;
3824             mLightBackgroundLayoutId = smallestView.mLightBackgroundLayoutId;
3825         } else {
3826             // MODE_HAS_LANDSCAPE_AND_PORTRAIT
3827             mLandscape = new RemoteViews(parcel, getHierarchyRootData(), info, depth);
3828             mPortrait =
3829                     new RemoteViews(parcel, getHierarchyRootData(), mLandscape.mApplication, depth);
3830             mApplication = mPortrait.mApplication;
3831             mLayoutId = mPortrait.mLayoutId;
3832             mViewId = mPortrait.mViewId;
3833             mLightBackgroundLayoutId = mPortrait.mLightBackgroundLayoutId;
3834         }
3835         mApplyFlags = parcel.readInt();
3836         mProviderInstanceId = parcel.readLong();
3837 
3838         // Ensure that all descendants have their caches set up recursively.
3839         if (mIsRoot) {
3840             configureDescendantsAsChildren();
3841         }
3842     }
3843 
3844     private void readActionsFromParcel(Parcel parcel, int depth) {
3845         int count = parcel.readInt();
3846         if (count > 0) {
3847             mActions = new ArrayList<>(count);
3848             for (int i = 0; i < count; i++) {
3849                 mActions.add(getActionFromParcel(parcel, depth));
3850             }
3851         }
3852     }
3853 
3854     private Action getActionFromParcel(Parcel parcel, int depth) {
3855         int tag = parcel.readInt();
3856         switch (tag) {
3857             case SET_ON_CLICK_RESPONSE_TAG:
3858                 return new SetOnClickResponse(parcel);
3859             case SET_DRAWABLE_TINT_TAG:
3860                 return new SetDrawableTint(parcel);
3861             case REFLECTION_ACTION_TAG:
3862                 return new ReflectionAction(parcel);
3863             case VIEW_GROUP_ACTION_ADD_TAG:
3864                 return new ViewGroupActionAdd(parcel, mApplication, depth);
3865             case VIEW_GROUP_ACTION_REMOVE_TAG:
3866                 return new ViewGroupActionRemove(parcel);
3867             case VIEW_CONTENT_NAVIGATION_TAG:
3868                 return new ViewContentNavigation(parcel);
3869             case SET_EMPTY_VIEW_ACTION_TAG:
3870                 return new SetEmptyView(parcel);
3871             case SET_PENDING_INTENT_TEMPLATE_TAG:
3872                 return new SetPendingIntentTemplate(parcel);
3873             case SET_REMOTE_VIEW_ADAPTER_INTENT_TAG:
3874                 return new SetRemoteViewsAdapterIntent(parcel);
3875             case TEXT_VIEW_DRAWABLE_ACTION_TAG:
3876                 return new TextViewDrawableAction(parcel);
3877             case TEXT_VIEW_SIZE_ACTION_TAG:
3878                 return new TextViewSizeAction(parcel);
3879             case VIEW_PADDING_ACTION_TAG:
3880                 return new ViewPaddingAction(parcel);
3881             case BITMAP_REFLECTION_ACTION_TAG:
3882                 return new BitmapReflectionAction(parcel);
3883             case SET_REMOTE_VIEW_ADAPTER_LIST_TAG:
3884                 return new SetRemoteViewsAdapterList(parcel);
3885             case SET_REMOTE_INPUTS_ACTION_TAG:
3886                 return new SetRemoteInputsAction(parcel);
3887             case LAYOUT_PARAM_ACTION_TAG:
3888                 return new LayoutParamAction(parcel);
3889             case OVERRIDE_TEXT_COLORS_TAG:
3890                 return new OverrideTextColorsAction(parcel);
3891             case SET_RIPPLE_DRAWABLE_COLOR_TAG:
3892                 return new SetRippleDrawableColor(parcel);
3893             case SET_INT_TAG_TAG:
3894                 return new SetIntTagAction(parcel);
3895             case REMOVE_FROM_PARENT_ACTION_TAG:
3896                 return new RemoveFromParentAction(parcel);
3897             case RESOURCE_REFLECTION_ACTION_TAG:
3898                 return new ResourceReflectionAction(parcel);
3899             case COMPLEX_UNIT_DIMENSION_REFLECTION_ACTION_TAG:
3900                 return new ComplexUnitDimensionReflectionAction(parcel);
3901             case SET_COMPOUND_BUTTON_CHECKED_TAG:
3902                 return new SetCompoundButtonCheckedAction(parcel);
3903             case SET_RADIO_GROUP_CHECKED:
3904                 return new SetRadioGroupCheckedAction(parcel);
3905             case SET_VIEW_OUTLINE_RADIUS_TAG:
3906                 return new SetViewOutlinePreferredRadiusAction(parcel);
3907             case SET_ON_CHECKED_CHANGE_RESPONSE_TAG:
3908                 return new SetOnCheckedChangeResponse(parcel);
3909             case NIGHT_MODE_REFLECTION_ACTION_TAG:
3910                 return new NightModeReflectionAction(parcel);
3911             case SET_REMOTE_COLLECTION_ITEMS_ADAPTER_TAG:
3912                 return new SetRemoteCollectionItemListAdapterAction(parcel);
3913             case ATTRIBUTE_REFLECTION_ACTION_TAG:
3914                 return new AttributeReflectionAction(parcel);
3915             default:
3916                 throw new ActionException("Tag " + tag + " not found");
3917         }
3918     };
3919 
3920     /**
3921      * Returns a deep copy of the RemoteViews object. The RemoteView may not be
3922      * attached to another RemoteView -- it must be the root of a hierarchy.
3923      *
3924      * @deprecated use {@link #RemoteViews(RemoteViews)} instead.
3925      * @throws IllegalStateException if this is not the root of a RemoteView
3926      *         hierarchy
3927      */
3928     @Override
3929     @Deprecated
3930     public RemoteViews clone() {
3931         Preconditions.checkState(mIsRoot, "RemoteView has been attached to another RemoteView. "
3932                 + "May only clone the root of a RemoteView hierarchy.");
3933 
3934         return new RemoteViews(this);
3935     }
3936 
3937     public String getPackage() {
3938         return (mApplication != null) ? mApplication.packageName : null;
3939     }
3940 
3941     /**
3942      * Returns the layout id of the root layout associated with this RemoteViews. In the case
3943      * that the RemoteViews has both a landscape and portrait root, this will return the layout
3944      * id associated with the portrait layout.
3945      *
3946      * @return the layout id.
3947      */
3948     public int getLayoutId() {
3949         return hasFlags(FLAG_USE_LIGHT_BACKGROUND_LAYOUT) && (mLightBackgroundLayoutId != 0)
3950                 ? mLightBackgroundLayoutId : mLayoutId;
3951     }
3952 
3953     /**
3954      * Sets the root of the hierarchy and then recursively traverses the tree to update the root
3955      * and populate caches for all descendants.
3956      */
3957     private void configureAsChild(@NonNull HierarchyRootData rootData) {
3958         mIsRoot = false;
3959         mBitmapCache = rootData.mBitmapCache;
3960         mApplicationInfoCache = rootData.mApplicationInfoCache;
3961         mClassCookies = rootData.mClassCookies;
3962         configureDescendantsAsChildren();
3963     }
3964 
3965     /**
3966      * Recursively traverses the tree to update the root and populate caches for all descendants.
3967      */
3968     private void configureDescendantsAsChildren() {
3969         // Before propagating down the tree, replace our application from the root application info
3970         // cache, to ensure the same instance is present throughout the hierarchy to allow for
3971         // squashing.
3972         mApplication = mApplicationInfoCache.getOrPut(mApplication);
3973 
3974         HierarchyRootData rootData = getHierarchyRootData();
3975         if (hasSizedRemoteViews()) {
3976             for (RemoteViews remoteView : mSizedRemoteViews) {
3977                 remoteView.configureAsChild(rootData);
3978             }
3979         } else if (hasLandscapeAndPortraitLayouts()) {
3980             mLandscape.configureAsChild(rootData);
3981             mPortrait.configureAsChild(rootData);
3982         } else {
3983             if (mActions != null) {
3984                 for (Action action : mActions) {
3985                     action.setHierarchyRootData(rootData);
3986                 }
3987             }
3988         }
3989     }
3990 
3991     /**
3992      * Recreates caches at the root level of the hierarchy, then recursively populates the caches
3993      * down the hierarchy.
3994      */
3995     private void reconstructCaches() {
3996         if (!mIsRoot) return;
3997         mBitmapCache = new BitmapCache();
3998         mApplicationInfoCache = new ApplicationInfoCache();
3999         mApplication = mApplicationInfoCache.getOrPut(mApplication);
4000         configureDescendantsAsChildren();
4001     }
4002 
4003     /**
4004      * Returns an estimate of the bitmap heap memory usage for this RemoteViews.
4005      */
4006     /** @hide */
4007     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
4008     public int estimateMemoryUsage() {
4009         return mBitmapCache.getBitmapMemory();
4010     }
4011 
4012     /**
4013      * Add an action to be executed on the remote side when apply is called.
4014      *
4015      * @param a The action to add
4016      */
4017     private void addAction(Action a) {
4018         if (hasMultipleLayouts()) {
4019             throw new RuntimeException("RemoteViews specifying separate layouts for orientation"
4020                     + " or size cannot be modified. Instead, fully configure each layouts"
4021                     + " individually before constructing the combined layout.");
4022         }
4023         if (mActions == null) {
4024             mActions = new ArrayList<>();
4025         }
4026         mActions.add(a);
4027     }
4028 
4029     /**
4030      * Equivalent to calling {@link ViewGroup#addView(View)} after inflating the
4031      * given {@link RemoteViews}. This allows users to build "nested"
4032      * {@link RemoteViews}. In cases where consumers of {@link RemoteViews} may
4033      * recycle layouts, use {@link #removeAllViews(int)} to clear any existing
4034      * children.
4035      *
4036      * @param viewId The id of the parent {@link ViewGroup} to add child into.
4037      * @param nestedView {@link RemoteViews} that describes the child.
4038      */
4039     public void addView(@IdRes int viewId, RemoteViews nestedView) {
4040         // Clear all children when nested views omitted
4041         addAction(nestedView == null
4042                 ? new ViewGroupActionRemove(viewId)
4043                 : new ViewGroupActionAdd(viewId, nestedView));
4044     }
4045 
4046     /**
4047      * Equivalent to calling {@link ViewGroup#addView(View)} after inflating the given
4048      * {@link RemoteViews}. If the {@link RemoteViews} may be re-inflated or updated,
4049      * {@link #removeAllViews(int)} must be called on the same {@code viewId
4050      * } before the first call to this method for the behavior of this method to be predictable.
4051      *
4052      * The {@code stableId} will be used to identify a potential view to recycled when the remote
4053      * view is inflated. Views can be re-used if inserted in the same order, potentially with
4054      * some views appearing / disappearing. To be recycled the view must not change the layout
4055      * used to inflate it or its view id (see {@link RemoteViews#RemoteViews(String, int, int)}).
4056      *
4057      * Note: if a view is re-used, all the actions will be re-applied on it. However, its properties
4058      * are not reset, so what was applied in previous round will have an effect. As a view may be
4059      * re-created at any time by the host, the RemoteViews should not rely on keeping information
4060      * from previous applications and always re-set all the properties they need.
4061      *
4062      * @param viewId The id of the parent {@link ViewGroup} to add child into.
4063      * @param nestedView {@link RemoteViews} that describes the child.
4064      * @param stableId An id that is stable across different versions of RemoteViews.
4065      */
4066     public void addStableView(@IdRes int viewId, @NonNull RemoteViews nestedView, int stableId) {
4067         addAction(new ViewGroupActionAdd(viewId, nestedView, -1 /* index */, stableId));
4068     }
4069 
4070     /**
4071      * Equivalent to calling {@link ViewGroup#addView(View, int)} after inflating the
4072      * given {@link RemoteViews}.
4073      *
4074      * @param viewId The id of the parent {@link ViewGroup} to add the child into.
4075      * @param nestedView {@link RemoteViews} of the child to add.
4076      * @param index The position at which to add the child.
4077      *
4078      * @hide
4079      */
4080     @UnsupportedAppUsage
4081     public void addView(@IdRes int viewId, RemoteViews nestedView, int index) {
4082         addAction(new ViewGroupActionAdd(viewId, nestedView, index));
4083     }
4084 
4085     /**
4086      * Equivalent to calling {@link ViewGroup#removeAllViews()}.
4087      *
4088      * @param viewId The id of the parent {@link ViewGroup} to remove all
4089      *            children from.
4090      */
4091     public void removeAllViews(@IdRes int viewId) {
4092         addAction(new ViewGroupActionRemove(viewId));
4093     }
4094 
4095     /**
4096      * Removes all views in the {@link ViewGroup} specified by the {@code viewId} except for any
4097      * child that has the {@code viewIdToKeep} as its id.
4098      *
4099      * @param viewId The id of the parent {@link ViewGroup} to remove children from.
4100      * @param viewIdToKeep The id of a child that should not be removed.
4101      *
4102      * @hide
4103      */
4104     public void removeAllViewsExceptId(@IdRes int viewId, @IdRes int viewIdToKeep) {
4105         addAction(new ViewGroupActionRemove(viewId, viewIdToKeep));
4106     }
4107 
4108     /**
4109      * Removes the {@link View} specified by the {@code viewId} from its parent {@link ViewManager}.
4110      * This will do nothing if the viewId specifies the root view of this RemoteViews.
4111      *
4112      * @param viewId The id of the {@link View} to remove from its parent.
4113      *
4114      * @hide
4115      */
4116     public void removeFromParent(@IdRes int viewId) {
4117         addAction(new RemoveFromParentAction(viewId));
4118     }
4119 
4120     /**
4121      * Equivalent to calling {@link AdapterViewAnimator#showNext()}
4122      *
4123      * @param viewId The id of the view on which to call {@link AdapterViewAnimator#showNext()}
4124      */
4125     public void showNext(@IdRes int viewId) {
4126         addAction(new ViewContentNavigation(viewId, true /* next */));
4127     }
4128 
4129     /**
4130      * Equivalent to calling {@link AdapterViewAnimator#showPrevious()}
4131      *
4132      * @param viewId The id of the view on which to call {@link AdapterViewAnimator#showPrevious()}
4133      */
4134     public void showPrevious(@IdRes int viewId) {
4135         addAction(new ViewContentNavigation(viewId, false /* next */));
4136     }
4137 
4138     /**
4139      * Equivalent to calling {@link AdapterViewAnimator#setDisplayedChild(int)}
4140      *
4141      * @param viewId The id of the view on which to call
4142      *               {@link AdapterViewAnimator#setDisplayedChild(int)}
4143      */
4144     public void setDisplayedChild(@IdRes int viewId, int childIndex) {
4145         setInt(viewId, "setDisplayedChild", childIndex);
4146     }
4147 
4148     /**
4149      * Equivalent to calling {@link View#setVisibility(int)}
4150      *
4151      * @param viewId The id of the view whose visibility should change
4152      * @param visibility The new visibility for the view
4153      */
4154     public void setViewVisibility(@IdRes int viewId, @View.Visibility int visibility) {
4155         setInt(viewId, "setVisibility", visibility);
4156     }
4157 
4158     /**
4159      * Equivalent to calling {@link TextView#setText(CharSequence)}
4160      *
4161      * @param viewId The id of the view whose text should change
4162      * @param text The new text for the view
4163      */
4164     public void setTextViewText(@IdRes int viewId, CharSequence text) {
4165         setCharSequence(viewId, "setText", text);
4166     }
4167 
4168     /**
4169      * Equivalent to calling {@link TextView#setTextSize(int, float)}
4170      *
4171      * @param viewId The id of the view whose text size should change
4172      * @param units The units of size (e.g. COMPLEX_UNIT_SP)
4173      * @param size The size of the text
4174      */
4175     public void setTextViewTextSize(@IdRes int viewId, int units, float size) {
4176         addAction(new TextViewSizeAction(viewId, units, size));
4177     }
4178 
4179     /**
4180      * Equivalent to calling
4181      * {@link TextView#setCompoundDrawablesWithIntrinsicBounds(int, int, int, int)}.
4182      *
4183      * @param viewId The id of the view whose text should change
4184      * @param left The id of a drawable to place to the left of the text, or 0
4185      * @param top The id of a drawable to place above the text, or 0
4186      * @param right The id of a drawable to place to the right of the text, or 0
4187      * @param bottom The id of a drawable to place below the text, or 0
4188      */
4189     public void setTextViewCompoundDrawables(@IdRes int viewId, @DrawableRes int left,
4190             @DrawableRes int top, @DrawableRes int right, @DrawableRes int bottom) {
4191         addAction(new TextViewDrawableAction(viewId, false, left, top, right, bottom));
4192     }
4193 
4194     /**
4195      * Equivalent to calling {@link
4196      * TextView#setCompoundDrawablesRelativeWithIntrinsicBounds(int, int, int, int)}.
4197      *
4198      * @param viewId The id of the view whose text should change
4199      * @param start The id of a drawable to place before the text (relative to the
4200      * layout direction), or 0
4201      * @param top The id of a drawable to place above the text, or 0
4202      * @param end The id of a drawable to place after the text, or 0
4203      * @param bottom The id of a drawable to place below the text, or 0
4204      */
4205     public void setTextViewCompoundDrawablesRelative(@IdRes int viewId, @DrawableRes int start,
4206             @DrawableRes int top, @DrawableRes int end, @DrawableRes int bottom) {
4207         addAction(new TextViewDrawableAction(viewId, true, start, top, end, bottom));
4208     }
4209 
4210     /**
4211      * Equivalent to calling {@link
4212      * TextView#setCompoundDrawablesWithIntrinsicBounds(Drawable, Drawable, Drawable, Drawable)}
4213      * using the drawables yielded by {@link Icon#loadDrawable(Context)}.
4214      *
4215      * @param viewId The id of the view whose text should change
4216      * @param left an Icon to place to the left of the text, or 0
4217      * @param top an Icon to place above the text, or 0
4218      * @param right an Icon to place to the right of the text, or 0
4219      * @param bottom an Icon to place below the text, or 0
4220      *
4221      * @hide
4222      */
4223     public void setTextViewCompoundDrawables(@IdRes int viewId,
4224             Icon left, Icon top, Icon right, Icon bottom) {
4225         addAction(new TextViewDrawableAction(viewId, false, left, top, right, bottom));
4226     }
4227 
4228     /**
4229      * Equivalent to calling {@link
4230      * TextView#setCompoundDrawablesRelativeWithIntrinsicBounds(Drawable, Drawable, Drawable, Drawable)}
4231      * using the drawables yielded by {@link Icon#loadDrawable(Context)}.
4232      *
4233      * @param viewId The id of the view whose text should change
4234      * @param start an Icon to place before the text (relative to the
4235      * layout direction), or 0
4236      * @param top an Icon to place above the text, or 0
4237      * @param end an Icon to place after the text, or 0
4238      * @param bottom an Icon to place below the text, or 0
4239      *
4240      * @hide
4241      */
4242     public void setTextViewCompoundDrawablesRelative(@IdRes int viewId,
4243             Icon start, Icon top, Icon end, Icon bottom) {
4244         addAction(new TextViewDrawableAction(viewId, true, start, top, end, bottom));
4245     }
4246 
4247     /**
4248      * Equivalent to calling {@link ImageView#setImageResource(int)}
4249      *
4250      * @param viewId The id of the view whose drawable should change
4251      * @param srcId The new resource id for the drawable
4252      */
4253     public void setImageViewResource(@IdRes int viewId, @DrawableRes int srcId) {
4254         setInt(viewId, "setImageResource", srcId);
4255     }
4256 
4257     /**
4258      * Equivalent to calling {@link ImageView#setImageURI(Uri)}
4259      *
4260      * @param viewId The id of the view whose drawable should change
4261      * @param uri The Uri for the image
4262      */
4263     public void setImageViewUri(@IdRes int viewId, Uri uri) {
4264         setUri(viewId, "setImageURI", uri);
4265     }
4266 
4267     /**
4268      * Equivalent to calling {@link ImageView#setImageBitmap(Bitmap)}
4269      *
4270      * @param viewId The id of the view whose bitmap should change
4271      * @param bitmap The new Bitmap for the drawable
4272      */
4273     public void setImageViewBitmap(@IdRes int viewId, Bitmap bitmap) {
4274         setBitmap(viewId, "setImageBitmap", bitmap);
4275     }
4276 
4277     /**
4278      * Equivalent to calling {@link ImageView#setImageIcon(Icon)}
4279      *
4280      * @param viewId The id of the view whose bitmap should change
4281      * @param icon The new Icon for the ImageView
4282      */
4283     public void setImageViewIcon(@IdRes int viewId, Icon icon) {
4284         setIcon(viewId, "setImageIcon", icon);
4285     }
4286 
4287     /**
4288      * Equivalent to calling {@link AdapterView#setEmptyView(View)}
4289      *
4290      * @param viewId The id of the view on which to set the empty view
4291      * @param emptyViewId The view id of the empty view
4292      */
4293     public void setEmptyView(@IdRes int viewId, @IdRes int emptyViewId) {
4294         addAction(new SetEmptyView(viewId, emptyViewId));
4295     }
4296 
4297     /**
4298      * Equivalent to calling {@link Chronometer#setBase Chronometer.setBase},
4299      * {@link Chronometer#setFormat Chronometer.setFormat},
4300      * and {@link Chronometer#start Chronometer.start()} or
4301      * {@link Chronometer#stop Chronometer.stop()}.
4302      *
4303      * @param viewId The id of the {@link Chronometer} to change
4304      * @param base The time at which the timer would have read 0:00.  This
4305      *             time should be based off of
4306      *             {@link android.os.SystemClock#elapsedRealtime SystemClock.elapsedRealtime()}.
4307      * @param format The Chronometer format string, or null to
4308      *               simply display the timer value.
4309      * @param started True if you want the clock to be started, false if not.
4310      *
4311      * @see #setChronometerCountDown(int, boolean)
4312      */
4313     public void setChronometer(@IdRes int viewId, long base, String format, boolean started) {
4314         setLong(viewId, "setBase", base);
4315         setString(viewId, "setFormat", format);
4316         setBoolean(viewId, "setStarted", started);
4317     }
4318 
4319     /**
4320      * Equivalent to calling {@link Chronometer#setCountDown(boolean) Chronometer.setCountDown} on
4321      * the chronometer with the given viewId.
4322      *
4323      * @param viewId The id of the {@link Chronometer} to change
4324      * @param isCountDown True if you want the chronometer to count down to base instead of
4325      *                    counting up.
4326      */
4327     public void setChronometerCountDown(@IdRes int viewId, boolean isCountDown) {
4328         setBoolean(viewId, "setCountDown", isCountDown);
4329     }
4330 
4331     /**
4332      * Equivalent to calling {@link ProgressBar#setMax ProgressBar.setMax},
4333      * {@link ProgressBar#setProgress ProgressBar.setProgress}, and
4334      * {@link ProgressBar#setIndeterminate ProgressBar.setIndeterminate}
4335      *
4336      * If indeterminate is true, then the values for max and progress are ignored.
4337      *
4338      * @param viewId The id of the {@link ProgressBar} to change
4339      * @param max The 100% value for the progress bar
4340      * @param progress The current value of the progress bar.
4341      * @param indeterminate True if the progress bar is indeterminate,
4342      *                false if not.
4343      */
4344     public void setProgressBar(@IdRes int viewId, int max, int progress,
4345             boolean indeterminate) {
4346         setBoolean(viewId, "setIndeterminate", indeterminate);
4347         if (!indeterminate) {
4348             setInt(viewId, "setMax", max);
4349             setInt(viewId, "setProgress", progress);
4350         }
4351     }
4352 
4353     /**
4354      * Equivalent to calling
4355      * {@link android.view.View#setOnClickListener(android.view.View.OnClickListener)}
4356      * to launch the provided {@link PendingIntent}. The source bounds
4357      * ({@link Intent#getSourceBounds()}) of the intent will be set to the bounds of the clicked
4358      * view in screen space.
4359      * Note that any activity options associated with the mPendingIntent may get overridden
4360      * before starting the intent.
4361      *
4362      * When setting the on-click action of items within collections (eg. {@link ListView},
4363      * {@link StackView} etc.), this method will not work. Instead, use {@link
4364      * RemoteViews#setPendingIntentTemplate(int, PendingIntent)} in conjunction with
4365      * {@link RemoteViews#setOnClickFillInIntent(int, Intent)}.
4366      *
4367      * @param viewId The id of the view that will trigger the {@link PendingIntent} when clicked
4368      * @param pendingIntent The {@link PendingIntent} to send when user clicks
4369      */
4370     public void setOnClickPendingIntent(@IdRes int viewId, PendingIntent pendingIntent) {
4371         setOnClickResponse(viewId, RemoteResponse.fromPendingIntent(pendingIntent));
4372     }
4373 
4374     /**
4375      * Equivalent of calling
4376      * {@link android.view.View#setOnClickListener(android.view.View.OnClickListener)}
4377      * to launch the provided {@link RemoteResponse}.
4378      *
4379      * @param viewId The id of the view that will trigger the {@link RemoteResponse} when clicked
4380      * @param response The {@link RemoteResponse} to send when user clicks
4381      */
4382     public void setOnClickResponse(@IdRes int viewId, @NonNull RemoteResponse response) {
4383         addAction(new SetOnClickResponse(viewId, response));
4384     }
4385 
4386     /**
4387      * When using collections (eg. {@link ListView}, {@link StackView} etc.) in widgets, it is very
4388      * costly to set PendingIntents on the individual items, and is hence not recommended. Instead
4389      * this method should be used to set a single PendingIntent template on the collection, and
4390      * individual items can differentiate their on-click behavior using
4391      * {@link RemoteViews#setOnClickFillInIntent(int, Intent)}.
4392      *
4393      * @param viewId The id of the collection who's children will use this PendingIntent template
4394      *          when clicked
4395      * @param pendingIntentTemplate The {@link PendingIntent} to be combined with extras specified
4396      *          by a child of viewId and executed when that child is clicked
4397      */
4398     public void setPendingIntentTemplate(@IdRes int viewId, PendingIntent pendingIntentTemplate) {
4399         addAction(new SetPendingIntentTemplate(viewId, pendingIntentTemplate));
4400     }
4401 
4402     /**
4403      * When using collections (eg. {@link ListView}, {@link StackView} etc.) in widgets, it is very
4404      * costly to set PendingIntents on the individual items, and is hence not recommended. Instead
4405      * a single PendingIntent template can be set on the collection, see {@link
4406      * RemoteViews#setPendingIntentTemplate(int, PendingIntent)}, and the individual on-click
4407      * action of a given item can be distinguished by setting a fillInIntent on that item. The
4408      * fillInIntent is then combined with the PendingIntent template in order to determine the final
4409      * intent which will be executed when the item is clicked. This works as follows: any fields
4410      * which are left blank in the PendingIntent template, but are provided by the fillInIntent
4411      * will be overwritten, and the resulting PendingIntent will be used. The rest
4412      * of the PendingIntent template will then be filled in with the associated fields that are
4413      * set in fillInIntent. See {@link Intent#fillIn(Intent, int)} for more details.
4414      *
4415      * @param viewId The id of the view on which to set the fillInIntent
4416      * @param fillInIntent The intent which will be combined with the parent's PendingIntent
4417      *        in order to determine the on-click behavior of the view specified by viewId
4418      */
4419     public void setOnClickFillInIntent(@IdRes int viewId, Intent fillInIntent) {
4420         setOnClickResponse(viewId, RemoteResponse.fromFillInIntent(fillInIntent));
4421     }
4422 
4423     /**
4424      * Equivalent to calling
4425      * {@link android.widget.CompoundButton#setOnCheckedChangeListener(
4426      * android.widget.CompoundButton.OnCheckedChangeListener)}
4427      * to launch the provided {@link RemoteResponse}.
4428      *
4429      * The intent will be filled with the current checked state of the view at the key
4430      * {@link #EXTRA_CHECKED}.
4431      *
4432      * The {@link RemoteResponse} will not be launched in response to check changes arising from
4433      * {@link #setCompoundButtonChecked(int, boolean)} or {@link #setRadioGroupChecked(int, int)}
4434      * usages.
4435      *
4436      * The {@link RemoteResponse} must be created using
4437      * {@link RemoteResponse#fromFillInIntent(Intent)} in conjunction with
4438      * {@link RemoteViews#setPendingIntentTemplate(int, PendingIntent)} for items inside
4439      * collections (eg. {@link ListView}, {@link StackView} etc.).
4440      *
4441      * Otherwise, create the {@link RemoteResponse} using
4442      * {@link RemoteResponse#fromPendingIntent(PendingIntent)}.
4443      *
4444      * @param viewId The id of the view that will trigger the {@link PendingIntent} when checked
4445      *               state changes.
4446      * @param response The {@link RemoteResponse} to send when the checked state changes.
4447      */
4448     public void setOnCheckedChangeResponse(
4449             @IdRes int viewId,
4450             @NonNull RemoteResponse response) {
4451         addAction(
4452                 new SetOnCheckedChangeResponse(
4453                         viewId,
4454                         response.setInteractionType(
4455                                 RemoteResponse.INTERACTION_TYPE_CHECKED_CHANGE)));
4456     }
4457 
4458     /**
4459      * @hide
4460      * Equivalent to calling
4461      * {@link Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)},
4462      * on the {@link Drawable} of a given view.
4463      * <p>
4464      *
4465      * @param viewId The id of the view that contains the target
4466      *            {@link Drawable}
4467      * @param targetBackground If true, apply these parameters to the
4468      *            {@link Drawable} returned by
4469      *            {@link android.view.View#getBackground()}. Otherwise, assume
4470      *            the target view is an {@link ImageView} and apply them to
4471      *            {@link ImageView#getDrawable()}.
4472      * @param colorFilter Specify a color for a
4473      *            {@link android.graphics.ColorFilter} for this drawable. This will be ignored if
4474      *            {@code mode} is {@code null}.
4475      * @param mode Specify a PorterDuff mode for this drawable, or null to leave
4476      *            unchanged.
4477      */
4478     public void setDrawableTint(@IdRes int viewId, boolean targetBackground,
4479             @ColorInt int colorFilter, @NonNull PorterDuff.Mode mode) {
4480         addAction(new SetDrawableTint(viewId, targetBackground, colorFilter, mode));
4481     }
4482 
4483     /**
4484      * @hide
4485      * Equivalent to calling
4486      * {@link RippleDrawable#setColor(ColorStateList)} on the {@link Drawable} of a given view,
4487      * assuming it's a {@link RippleDrawable}.
4488      * <p>
4489      *
4490      * @param viewId The id of the view that contains the target
4491      *            {@link RippleDrawable}
4492      * @param colorStateList Specify a color for a
4493      *            {@link ColorStateList} for this drawable.
4494      */
4495     public void setRippleDrawableColor(@IdRes int viewId, ColorStateList colorStateList) {
4496         addAction(new SetRippleDrawableColor(viewId, colorStateList));
4497     }
4498 
4499     /**
4500      * @hide
4501      * Equivalent to calling {@link android.widget.ProgressBar#setProgressTintList}.
4502      *
4503      * @param viewId The id of the view whose tint should change
4504      * @param tint the tint to apply, may be {@code null} to clear tint
4505      */
4506     public void setProgressTintList(@IdRes int viewId, ColorStateList tint) {
4507         addAction(new ReflectionAction(viewId, "setProgressTintList",
4508                 BaseReflectionAction.COLOR_STATE_LIST, tint));
4509     }
4510 
4511     /**
4512      * @hide
4513      * Equivalent to calling {@link android.widget.ProgressBar#setProgressBackgroundTintList}.
4514      *
4515      * @param viewId The id of the view whose tint should change
4516      * @param tint the tint to apply, may be {@code null} to clear tint
4517      */
4518     public void setProgressBackgroundTintList(@IdRes int viewId, ColorStateList tint) {
4519         addAction(new ReflectionAction(viewId, "setProgressBackgroundTintList",
4520                 BaseReflectionAction.COLOR_STATE_LIST, tint));
4521     }
4522 
4523     /**
4524      * @hide
4525      * Equivalent to calling {@link android.widget.ProgressBar#setIndeterminateTintList}.
4526      *
4527      * @param viewId The id of the view whose tint should change
4528      * @param tint the tint to apply, may be {@code null} to clear tint
4529      */
4530     public void setProgressIndeterminateTintList(@IdRes int viewId, ColorStateList tint) {
4531         addAction(new ReflectionAction(viewId, "setIndeterminateTintList",
4532                 BaseReflectionAction.COLOR_STATE_LIST, tint));
4533     }
4534 
4535     /**
4536      * Equivalent to calling {@link android.widget.TextView#setTextColor(int)}.
4537      *
4538      * @param viewId The id of the view whose text color should change
4539      * @param color Sets the text color for all the states (normal, selected,
4540      *            focused) to be this color.
4541      */
4542     public void setTextColor(@IdRes int viewId, @ColorInt int color) {
4543         setInt(viewId, "setTextColor", color);
4544     }
4545 
4546     /**
4547      * @hide
4548      * Equivalent to calling {@link android.widget.TextView#setTextColor(ColorStateList)}.
4549      *
4550      * @param viewId The id of the view whose text color should change
4551      * @param colors the text colors to set
4552      */
4553     public void setTextColor(@IdRes int viewId, ColorStateList colors) {
4554         addAction(new ReflectionAction(viewId, "setTextColor",
4555                 BaseReflectionAction.COLOR_STATE_LIST, colors));
4556     }
4557 
4558     /**
4559      * Equivalent to calling {@link android.widget.AbsListView#setRemoteViewsAdapter(Intent)}.
4560      *
4561      * @param appWidgetId The id of the app widget which contains the specified view. (This
4562      *      parameter is ignored in this deprecated method)
4563      * @param viewId The id of the {@link AdapterView}
4564      * @param intent The intent of the service which will be
4565      *            providing data to the RemoteViewsAdapter
4566      * @deprecated This method has been deprecated. See
4567      *      {@link android.widget.RemoteViews#setRemoteAdapter(int, Intent)}
4568      */
4569     @Deprecated
4570     public void setRemoteAdapter(int appWidgetId, @IdRes int viewId, Intent intent) {
4571         setRemoteAdapter(viewId, intent);
4572     }
4573 
4574     /**
4575      * Equivalent to calling {@link android.widget.AbsListView#setRemoteViewsAdapter(Intent)}.
4576      * Can only be used for App Widgets.
4577      *
4578      * @param viewId The id of the {@link AdapterView}
4579      * @param intent The intent of the service which will be
4580      *            providing data to the RemoteViewsAdapter
4581      */
4582     public void setRemoteAdapter(@IdRes int viewId, Intent intent) {
4583         addAction(new SetRemoteViewsAdapterIntent(viewId, intent));
4584     }
4585 
4586     /**
4587      * Creates a simple Adapter for the viewId specified. The viewId must point to an AdapterView,
4588      * ie. {@link ListView}, {@link GridView}, {@link StackView} or {@link AdapterViewAnimator}.
4589      * This is a simpler but less flexible approach to populating collection widgets. Its use is
4590      * encouraged for most scenarios, as long as the total memory within the list of RemoteViews
4591      * is relatively small (ie. doesn't contain large or numerous Bitmaps, see {@link
4592      * RemoteViews#setImageViewBitmap}). In the case of numerous images, the use of API is still
4593      * possible by setting image URIs instead of Bitmaps, see {@link RemoteViews#setImageViewUri}.
4594      *
4595      * This API is supported in the compatibility library for previous API levels, see
4596      * RemoteViewsCompat.
4597      *
4598      * @param viewId The id of the {@link AdapterView}
4599      * @param list The list of RemoteViews which will populate the view specified by viewId.
4600      * @param viewTypeCount The maximum number of unique layout id's used to construct the list of
4601      *      RemoteViews. This count cannot change during the life-cycle of a given widget, so this
4602      *      parameter should account for the maximum possible number of types that may appear in the
4603      *      See {@link Adapter#getViewTypeCount()}.
4604      *
4605      * @hide
4606      * @deprecated this appears to have no users outside of UnsupportedAppUsage?
4607      */
4608     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
4609     @Deprecated
4610     public void setRemoteAdapter(@IdRes int viewId, ArrayList<RemoteViews> list,
4611             int viewTypeCount) {
4612         addAction(new SetRemoteViewsAdapterList(viewId, list, viewTypeCount));
4613     }
4614 
4615     /**
4616      * Creates a simple Adapter for the viewId specified. The viewId must point to an AdapterView,
4617      * ie. {@link ListView}, {@link GridView}, {@link StackView} or {@link AdapterViewAnimator}.
4618      * This is a simpler but less flexible approach to populating collection widgets. Its use is
4619      * encouraged for most scenarios, as long as the total memory within the list of RemoteViews
4620      * is relatively small (ie. doesn't contain large or numerous Bitmaps, see {@link
4621      * RemoteViews#setImageViewBitmap}). In the case of numerous images, the use of API is still
4622      * possible by setting image URIs instead of Bitmaps, see {@link RemoteViews#setImageViewUri}.
4623      *
4624      * This API is supported in the compatibility library for previous API levels, see
4625      * RemoteViewsCompat.
4626      *
4627      * @param viewId The id of the {@link AdapterView}.
4628      * @param items The items to display in the {@link AdapterView}.
4629      */
4630     public void setRemoteAdapter(@IdRes int viewId, @NonNull RemoteCollectionItems items) {
4631         addAction(new SetRemoteCollectionItemListAdapterAction(viewId, items));
4632     }
4633 
4634     /**
4635      * Equivalent to calling {@link ListView#smoothScrollToPosition(int)}.
4636      *
4637      * @param viewId The id of the view to change
4638      * @param position Scroll to this adapter position
4639      */
4640     public void setScrollPosition(@IdRes int viewId, int position) {
4641         setInt(viewId, "smoothScrollToPosition", position);
4642     }
4643 
4644     /**
4645      * Equivalent to calling {@link ListView#smoothScrollByOffset(int)}.
4646      *
4647      * @param viewId The id of the view to change
4648      * @param offset Scroll by this adapter position offset
4649      */
4650     public void setRelativeScrollPosition(@IdRes int viewId, int offset) {
4651         setInt(viewId, "smoothScrollByOffset", offset);
4652     }
4653 
4654     /**
4655      * Equivalent to calling {@link android.view.View#setPadding(int, int, int, int)}.
4656      *
4657      * @param viewId The id of the view to change
4658      * @param left the left padding in pixels
4659      * @param top the top padding in pixels
4660      * @param right the right padding in pixels
4661      * @param bottom the bottom padding in pixels
4662      */
4663     public void setViewPadding(@IdRes int viewId,
4664             @Px int left, @Px int top, @Px int right, @Px int bottom) {
4665         addAction(new ViewPaddingAction(viewId, left, top, right, bottom));
4666     }
4667 
4668     /**
4669      * Equivalent to calling {@link MarginLayoutParams#setMarginEnd}.
4670      * Only works if the {@link View#getLayoutParams()} supports margins.
4671      *
4672      * @param viewId The id of the view to change
4673      * @param type The margin being set e.g. {@link #MARGIN_END}
4674      * @param dimen a dimension resource to apply to the margin, or 0 to clear the margin.
4675      */
4676     public void setViewLayoutMarginDimen(@IdRes int viewId, @MarginType int type,
4677             @DimenRes int dimen) {
4678         addAction(new LayoutParamAction(viewId, type, dimen, VALUE_TYPE_RESOURCE));
4679     }
4680 
4681     /**
4682      * Equivalent to calling {@link MarginLayoutParams#setMarginEnd}.
4683      * Only works if the {@link View#getLayoutParams()} supports margins.
4684      *
4685      * @param viewId The id of the view to change
4686      * @param type The margin being set e.g. {@link #MARGIN_END}
4687      * @param attr a dimension attribute to apply to the margin, or 0 to clear the margin.
4688      */
4689     public void setViewLayoutMarginAttr(@IdRes int viewId, @MarginType int type,
4690             @AttrRes int attr) {
4691         addAction(new LayoutParamAction(viewId, type, attr, VALUE_TYPE_ATTRIBUTE));
4692     }
4693 
4694     /**
4695      * Equivalent to calling {@link MarginLayoutParams#setMarginEnd}.
4696      * Only works if the {@link View#getLayoutParams()} supports margins.
4697      *
4698      * <p>NOTE: It is recommended to use {@link TypedValue#COMPLEX_UNIT_PX} only for 0.
4699      * Setting margins in pixels will behave poorly when the RemoteViews object is used on a
4700      * display with a different density.
4701      *
4702      * @param viewId The id of the view to change
4703      * @param type The margin being set e.g. {@link #MARGIN_END}
4704      * @param value a value for the margin the given units.
4705      * @param units The unit type of the value e.g. {@link TypedValue#COMPLEX_UNIT_DIP}
4706      */
4707     public void setViewLayoutMargin(@IdRes int viewId, @MarginType int type, float value,
4708             @ComplexDimensionUnit int units) {
4709         addAction(new LayoutParamAction(viewId, type, value, units));
4710     }
4711 
4712     /**
4713      * Equivalent to setting {@link android.view.ViewGroup.LayoutParams#width} except that you may
4714      * provide the value in any dimension units.
4715      *
4716      * <p>NOTE: It is recommended to use {@link TypedValue#COMPLEX_UNIT_PX} only for 0,
4717      * {@link ViewGroup.LayoutParams#WRAP_CONTENT}, or {@link ViewGroup.LayoutParams#MATCH_PARENT}.
4718      * Setting actual sizes in pixels will behave poorly when the RemoteViews object is used on a
4719      * display with a different density.
4720      *
4721      * @param width Width of the view in the given units
4722      * @param units The unit type of the value e.g. {@link TypedValue#COMPLEX_UNIT_DIP}
4723      */
4724     public void setViewLayoutWidth(@IdRes int viewId, float width,
4725             @ComplexDimensionUnit int units) {
4726         addAction(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_WIDTH, width, units));
4727     }
4728 
4729     /**
4730      * Equivalent to setting {@link android.view.ViewGroup.LayoutParams#width} with
4731      * the result of {@link Resources#getDimensionPixelSize(int)}.
4732      *
4733      * @param widthDimen the dimension resource for the view's width
4734      */
4735     public void setViewLayoutWidthDimen(@IdRes int viewId, @DimenRes int widthDimen) {
4736         addAction(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_WIDTH, widthDimen,
4737                 VALUE_TYPE_RESOURCE));
4738     }
4739 
4740     /**
4741      * Equivalent to setting {@link android.view.ViewGroup.LayoutParams#width} with
4742      * the value of the given attribute in the current theme.
4743      *
4744      * @param widthAttr the dimension attribute for the view's width
4745      */
4746     public void setViewLayoutWidthAttr(@IdRes int viewId, @AttrRes int widthAttr) {
4747         addAction(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_WIDTH, widthAttr,
4748                 VALUE_TYPE_ATTRIBUTE));
4749     }
4750 
4751     /**
4752      * Equivalent to setting {@link android.view.ViewGroup.LayoutParams#height} except that you may
4753      * provide the value in any dimension units.
4754      *
4755      * <p>NOTE: It is recommended to use {@link TypedValue#COMPLEX_UNIT_PX} only for 0,
4756      * {@link ViewGroup.LayoutParams#WRAP_CONTENT}, or {@link ViewGroup.LayoutParams#MATCH_PARENT}.
4757      * Setting actual sizes in pixels will behave poorly when the RemoteViews object is used on a
4758      * display with a different density.
4759      *
4760      * @param height height of the view in the given units
4761      * @param units The unit type of the value e.g. {@link TypedValue#COMPLEX_UNIT_DIP}
4762      */
4763     public void setViewLayoutHeight(@IdRes int viewId, float height,
4764             @ComplexDimensionUnit int units) {
4765         addAction(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_HEIGHT, height, units));
4766     }
4767 
4768     /**
4769      * Equivalent to setting {@link android.view.ViewGroup.LayoutParams#height} with
4770      * the result of {@link Resources#getDimensionPixelSize(int)}.
4771      *
4772      * @param heightDimen a dimen resource to read the height from.
4773      */
4774     public void setViewLayoutHeightDimen(@IdRes int viewId, @DimenRes int heightDimen) {
4775         addAction(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_HEIGHT, heightDimen,
4776                 VALUE_TYPE_RESOURCE));
4777     }
4778 
4779     /**
4780      * Equivalent to setting {@link android.view.ViewGroup.LayoutParams#height} with
4781      * the value of the given attribute in the current theme.
4782      *
4783      * @param heightAttr a dimen attribute to read the height from.
4784      */
4785     public void setViewLayoutHeightAttr(@IdRes int viewId, @AttrRes int heightAttr) {
4786         addAction(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_HEIGHT, heightAttr,
4787                 VALUE_TYPE_ATTRIBUTE));
4788     }
4789 
4790     /**
4791      * Sets an OutlineProvider on the view whose corner radius is a dimension calculated using
4792      * {@link TypedValue#applyDimension(int, float, DisplayMetrics)}.
4793      *
4794      * <p>NOTE: It is recommended to use {@link TypedValue#COMPLEX_UNIT_PX} only for 0.
4795      * Setting margins in pixels will behave poorly when the RemoteViews object is used on a
4796      * display with a different density.
4797      */
4798     public void setViewOutlinePreferredRadius(
4799             @IdRes int viewId, float radius, @ComplexDimensionUnit int units) {
4800         addAction(new SetViewOutlinePreferredRadiusAction(viewId, radius, units));
4801     }
4802 
4803     /**
4804      * Sets an OutlineProvider on the view whose corner radius is a dimension resource with
4805      * {@code resId}.
4806      */
4807     public void setViewOutlinePreferredRadiusDimen(@IdRes int viewId, @DimenRes int resId) {
4808         addAction(new SetViewOutlinePreferredRadiusAction(viewId, resId, VALUE_TYPE_RESOURCE));
4809     }
4810 
4811     /**
4812      * Sets an OutlineProvider on the view whose corner radius is a dimension attribute with
4813      * {@code attrId}.
4814      */
4815     public void setViewOutlinePreferredRadiusAttr(@IdRes int viewId, @AttrRes int attrId) {
4816         addAction(new SetViewOutlinePreferredRadiusAction(viewId, attrId, VALUE_TYPE_ATTRIBUTE));
4817     }
4818 
4819     /**
4820      * Call a method taking one boolean on a view in the layout for this RemoteViews.
4821      *
4822      * @param viewId The id of the view on which to call the method.
4823      * @param methodName The name of the method to call.
4824      * @param value The value to pass to the method.
4825      */
4826     public void setBoolean(@IdRes int viewId, String methodName, boolean value) {
4827         addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.BOOLEAN, value));
4828     }
4829 
4830     /**
4831      * Call a method taking one byte on a view in the layout for this RemoteViews.
4832      *
4833      * @param viewId The id of the view on which to call the method.
4834      * @param methodName The name of the method to call.
4835      * @param value The value to pass to the method.
4836      */
4837     public void setByte(@IdRes int viewId, String methodName, byte value) {
4838         addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.BYTE, value));
4839     }
4840 
4841     /**
4842      * Call a method taking one short on a view in the layout for this RemoteViews.
4843      *
4844      * @param viewId The id of the view on which to call the method.
4845      * @param methodName The name of the method to call.
4846      * @param value The value to pass to the method.
4847      */
4848     public void setShort(@IdRes int viewId, String methodName, short value) {
4849         addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.SHORT, value));
4850     }
4851 
4852     /**
4853      * Call a method taking one int on a view in the layout for this RemoteViews.
4854      *
4855      * @param viewId The id of the view on which to call the method.
4856      * @param methodName The name of the method to call.
4857      * @param value The value to pass to the method.
4858      */
4859     public void setInt(@IdRes int viewId, String methodName, int value) {
4860         addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.INT, value));
4861     }
4862 
4863     /**
4864      * Call a method taking one int, a size in pixels, on a view in the layout for this
4865      * RemoteViews.
4866      *
4867      * The dimension will be resolved from the resources at the time the {@link RemoteViews} is
4868      * (re-)applied.
4869      *
4870      * Undefined resources will result in an exception, except 0 which will resolve to 0.
4871      *
4872      * @param viewId The id of the view on which to call the method.
4873      * @param methodName The name of the method to call.
4874      * @param dimenResource The resource to resolve and pass as argument to the method.
4875      */
4876     public void setIntDimen(@IdRes int viewId, @NonNull String methodName,
4877             @DimenRes int dimenResource) {
4878         addAction(new ResourceReflectionAction(viewId, methodName, BaseReflectionAction.INT,
4879                 ResourceReflectionAction.DIMEN_RESOURCE, dimenResource));
4880     }
4881 
4882     /**
4883      * Call a method taking one int, a size in pixels, on a view in the layout for this
4884      * RemoteViews.
4885      *
4886      * The dimension will be resolved from the specified dimension at the time of inflation.
4887      *
4888      * @param viewId The id of the view on which to call the method.
4889      * @param methodName The name of the method to call.
4890      * @param value The value of the dimension.
4891      * @param unit The unit in which the value is specified.
4892      */
4893     public void setIntDimen(@IdRes int viewId, @NonNull String methodName,
4894             float value, @ComplexDimensionUnit int unit) {
4895         addAction(new ComplexUnitDimensionReflectionAction(viewId, methodName, ReflectionAction.INT,
4896                 value, unit));
4897     }
4898 
4899     /**
4900      * Call a method taking one int, a size in pixels, on a view in the layout for this
4901      * RemoteViews.
4902      *
4903      * The dimension will be resolved from the theme attribute at the time the
4904      * {@link RemoteViews} is (re-)applied.
4905      *
4906      * Unresolvable attributes will result in an exception, except 0 which will resolve to 0.
4907      *
4908      * @param viewId The id of the view on which to call the method.
4909      * @param methodName The name of the method to call.
4910      * @param dimenAttr The attribute to resolve and pass as argument to the method.
4911      */
4912     public void setIntDimenAttr(@IdRes int viewId, @NonNull String methodName,
4913             @AttrRes int dimenAttr) {
4914         addAction(new AttributeReflectionAction(viewId, methodName, BaseReflectionAction.INT,
4915                 ResourceReflectionAction.DIMEN_RESOURCE, dimenAttr));
4916     }
4917 
4918     /**
4919      * Call a method taking one int, a color, on a view in the layout for this RemoteViews.
4920      *
4921      * The Color will be resolved from the resources at the time the {@link RemoteViews} is (re-)
4922      * applied.
4923      *
4924      * Undefined resources will result in an exception, except 0 which will resolve to 0.
4925      *
4926      * @param viewId The id of the view on which to call the method.
4927      * @param methodName The name of the method to call.
4928      * @param colorResource The resource to resolve and pass as argument to the method.
4929      */
4930     public void setColor(@IdRes int viewId, @NonNull String methodName,
4931             @ColorRes int colorResource) {
4932         addAction(new ResourceReflectionAction(viewId, methodName, BaseReflectionAction.INT,
4933                 ResourceReflectionAction.COLOR_RESOURCE, colorResource));
4934     }
4935 
4936     /**
4937      * Call a method taking one int, a color, on a view in the layout for this RemoteViews.
4938      *
4939      * The Color will be resolved from the theme attribute at the time the {@link RemoteViews} is
4940      * (re-)applied.
4941      *
4942      * Unresolvable attributes will result in an exception, except 0 which will resolve to 0.
4943      *
4944      * @param viewId The id of the view on which to call the method.
4945      * @param methodName The name of the method to call.
4946      * @param colorAttribute The theme attribute to resolve and pass as argument to the method.
4947      */
4948     public void setColorAttr(@IdRes int viewId, @NonNull String methodName,
4949             @AttrRes int colorAttribute) {
4950         addAction(new AttributeReflectionAction(viewId, methodName, BaseReflectionAction.INT,
4951                 AttributeReflectionAction.COLOR_RESOURCE, colorAttribute));
4952     }
4953 
4954     /**
4955      * Call a method taking one int, a color, on a view in the layout for this RemoteViews.
4956      *
4957      * @param viewId The id of the view on which to call the method.
4958      * @param methodName The name of the method to call.
4959      * @param notNight The value to pass to the method when the view's configuration is set to
4960      *                 {@link Configuration#UI_MODE_NIGHT_NO}
4961      * @param night The value to pass to the method when the view's configuration is set to
4962      *                 {@link Configuration#UI_MODE_NIGHT_YES}
4963      */
4964     public void setColorInt(
4965             @IdRes int viewId,
4966             @NonNull String methodName,
4967             @ColorInt int notNight,
4968             @ColorInt int night) {
4969         addAction(
4970                 new NightModeReflectionAction(
4971                         viewId,
4972                         methodName,
4973                         BaseReflectionAction.INT,
4974                         notNight,
4975                         night));
4976     }
4977 
4978 
4979     /**
4980      * Call a method taking one ColorStateList on a view in the layout for this RemoteViews.
4981      *
4982      * @param viewId The id of the view on which to call the method.
4983      * @param methodName The name of the method to call.
4984      * @param value The value to pass to the method.
4985      */
4986     public void setColorStateList(@IdRes int viewId, @NonNull String methodName,
4987             @Nullable ColorStateList value) {
4988         addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.COLOR_STATE_LIST,
4989                 value));
4990     }
4991 
4992     /**
4993      * Call a method taking one ColorStateList on a view in the layout for this RemoteViews.
4994      *
4995      * @param viewId The id of the view on which to call the method.
4996      * @param methodName The name of the method to call.
4997      * @param notNight The value to pass to the method when the view's configuration is set to
4998      *                 {@link Configuration#UI_MODE_NIGHT_NO}
4999      * @param night The value to pass to the method when the view's configuration is set to
5000      *                 {@link Configuration#UI_MODE_NIGHT_YES}
5001      */
5002     public void setColorStateList(
5003             @IdRes int viewId,
5004             @NonNull String methodName,
5005             @Nullable ColorStateList notNight,
5006             @Nullable ColorStateList night) {
5007         addAction(
5008                 new NightModeReflectionAction(
5009                         viewId,
5010                         methodName,
5011                         BaseReflectionAction.COLOR_STATE_LIST,
5012                         notNight,
5013                         night));
5014     }
5015 
5016     /**
5017      * Call a method taking one ColorStateList on a view in the layout for this RemoteViews.
5018      *
5019      * The ColorStateList will be resolved from the resources at the time the {@link RemoteViews} is
5020      * (re-)applied.
5021      *
5022      * Undefined resources will result in an exception, except 0 which will resolve to null.
5023      *
5024      * @param viewId The id of the view on which to call the method.
5025      * @param methodName The name of the method to call.
5026      * @param colorResource The resource to resolve and pass as argument to the method.
5027      */
5028     public void setColorStateList(@IdRes int viewId, @NonNull String methodName,
5029             @ColorRes int colorResource) {
5030         addAction(new ResourceReflectionAction(viewId, methodName,
5031                 BaseReflectionAction.COLOR_STATE_LIST, ResourceReflectionAction.COLOR_RESOURCE,
5032                 colorResource));
5033     }
5034 
5035     /**
5036      * Call a method taking one ColorStateList on a view in the layout for this RemoteViews.
5037      *
5038      * The ColorStateList will be resolved from the theme attribute at the time the
5039      * {@link RemoteViews} is (re-)applied.
5040      *
5041      * Unresolvable attributes will result in an exception, except 0 which will resolve to null.
5042      *
5043      * @param viewId The id of the view on which to call the method.
5044      * @param methodName The name of the method to call.
5045      * @param colorAttr The theme attribute to resolve and pass as argument to the method.
5046      */
5047     public void setColorStateListAttr(@IdRes int viewId, @NonNull String methodName,
5048             @AttrRes int colorAttr) {
5049         addAction(new AttributeReflectionAction(viewId, methodName,
5050                 BaseReflectionAction.COLOR_STATE_LIST, ResourceReflectionAction.COLOR_RESOURCE,
5051                 colorAttr));
5052     }
5053 
5054     /**
5055      * Call a method taking one long on a view in the layout for this RemoteViews.
5056      *
5057      * @param viewId The id of the view on which to call the method.
5058      * @param methodName The name of the method to call.
5059      * @param value The value to pass to the method.
5060      */
5061     public void setLong(@IdRes int viewId, String methodName, long value) {
5062         addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.LONG, value));
5063     }
5064 
5065     /**
5066      * Call a method taking one float on a view in the layout for this RemoteViews.
5067      *
5068      * @param viewId The id of the view on which to call the method.
5069      * @param methodName The name of the method to call.
5070      * @param value The value to pass to the method.
5071      */
5072     public void setFloat(@IdRes int viewId, String methodName, float value) {
5073         addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.FLOAT, value));
5074     }
5075 
5076     /**
5077      * Call a method taking one float, a size in pixels, on a view in the layout for this
5078      * RemoteViews.
5079      *
5080      * The dimension will be resolved from the resources at the time the {@link RemoteViews} is
5081      * (re-)applied.
5082      *
5083      * Undefined resources will result in an exception, except 0 which will resolve to 0f.
5084      *
5085      * @param viewId The id of the view on which to call the method.
5086      * @param methodName The name of the method to call.
5087      * @param dimenResource The resource to resolve and pass as argument to the method.
5088      */
5089     public void setFloatDimen(@IdRes int viewId, @NonNull String methodName,
5090             @DimenRes int dimenResource) {
5091         addAction(new ResourceReflectionAction(viewId, methodName, BaseReflectionAction.FLOAT,
5092                 ResourceReflectionAction.DIMEN_RESOURCE, dimenResource));
5093     }
5094 
5095     /**
5096      * Call a method taking one float, a size in pixels, on a view in the layout for this
5097      * RemoteViews.
5098      *
5099      * The dimension will be resolved from the resources at the time the {@link RemoteViews} is
5100      * (re-)applied.
5101      *
5102      * @param viewId The id of the view on which to call the method.
5103      * @param methodName The name of the method to call.
5104      * @param value The value of the dimension.
5105      * @param unit The unit in which the value is specified.
5106      */
5107     public void setFloatDimen(@IdRes int viewId, @NonNull String methodName,
5108             float value, @ComplexDimensionUnit int unit) {
5109         addAction(
5110                 new ComplexUnitDimensionReflectionAction(viewId, methodName, ReflectionAction.FLOAT,
5111                         value, unit));
5112     }
5113 
5114     /**
5115      * Call a method taking one float, a size in pixels, on a view in the layout for this
5116      * RemoteViews.
5117      *
5118      * The dimension will be resolved from the theme attribute at the time the {@link RemoteViews}
5119      * is (re-)applied.
5120      *
5121      * Unresolvable attributes will result in an exception, except 0 which will resolve to 0f.
5122      *
5123      * @param viewId The id of the view on which to call the method.
5124      * @param methodName The name of the method to call.
5125      * @param dimenAttr The attribute to resolve and pass as argument to the method.
5126      */
5127     public void setFloatDimenAttr(@IdRes int viewId, @NonNull String methodName,
5128             @AttrRes int dimenAttr) {
5129         addAction(new AttributeReflectionAction(viewId, methodName, BaseReflectionAction.FLOAT,
5130                 ResourceReflectionAction.DIMEN_RESOURCE, dimenAttr));
5131     }
5132 
5133     /**
5134      * Call a method taking one double on a view in the layout for this RemoteViews.
5135      *
5136      * @param viewId The id of the view on which to call the method.
5137      * @param methodName The name of the method to call.
5138      * @param value The value to pass to the method.
5139      */
5140     public void setDouble(@IdRes int viewId, String methodName, double value) {
5141         addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.DOUBLE, value));
5142     }
5143 
5144     /**
5145      * Call a method taking one char on a view in the layout for this RemoteViews.
5146      *
5147      * @param viewId The id of the view on which to call the method.
5148      * @param methodName The name of the method to call.
5149      * @param value The value to pass to the method.
5150      */
5151     public void setChar(@IdRes int viewId, String methodName, char value) {
5152         addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.CHAR, value));
5153     }
5154 
5155     /**
5156      * Call a method taking one String on a view in the layout for this RemoteViews.
5157      *
5158      * @param viewId The id of the view on which to call the method.
5159      * @param methodName The name of the method to call.
5160      * @param value The value to pass to the method.
5161      */
5162     public void setString(@IdRes int viewId, String methodName, String value) {
5163         addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.STRING, value));
5164     }
5165 
5166     /**
5167      * Call a method taking one CharSequence on a view in the layout for this RemoteViews.
5168      *
5169      * @param viewId The id of the view on which to call the method.
5170      * @param methodName The name of the method to call.
5171      * @param value The value to pass to the method.
5172      */
5173     public void setCharSequence(@IdRes int viewId, String methodName, CharSequence value) {
5174         addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.CHAR_SEQUENCE,
5175                 value));
5176     }
5177 
5178     /**
5179      * Call a method taking one CharSequence on a view in the layout for this RemoteViews.
5180      *
5181      * The CharSequence will be resolved from the resources at the time the {@link RemoteViews} is
5182      * (re-)applied.
5183      *
5184      * Undefined resources will result in an exception, except 0 which will resolve to null.
5185      *
5186      * @param viewId The id of the view on which to call the method.
5187      * @param methodName The name of the method to call.
5188      * @param stringResource The resource to resolve and pass as argument to the method.
5189      */
5190     public void setCharSequence(@IdRes int viewId, @NonNull String methodName,
5191             @StringRes int stringResource) {
5192         addAction(
5193                 new ResourceReflectionAction(viewId, methodName, BaseReflectionAction.CHAR_SEQUENCE,
5194                         ResourceReflectionAction.STRING_RESOURCE, stringResource));
5195     }
5196 
5197     /**
5198      * Call a method taking one CharSequence on a view in the layout for this RemoteViews.
5199      *
5200      * The CharSequence will be resolved from the theme attribute at the time the
5201      * {@link RemoteViews} is (re-)applied.
5202      *
5203      * Unresolvable attributes will result in an exception, except 0 which will resolve to null.
5204      *
5205      * @param viewId The id of the view on which to call the method.
5206      * @param methodName The name of the method to call.
5207      * @param stringAttribute The attribute to resolve and pass as argument to the method.
5208      */
5209     public void setCharSequenceAttr(@IdRes int viewId, @NonNull String methodName,
5210             @AttrRes int stringAttribute) {
5211         addAction(
5212                 new AttributeReflectionAction(viewId, methodName,
5213                         BaseReflectionAction.CHAR_SEQUENCE,
5214                         AttributeReflectionAction.STRING_RESOURCE, stringAttribute));
5215     }
5216 
5217     /**
5218      * Call a method taking one Uri on a view in the layout for this RemoteViews.
5219      *
5220      * @param viewId The id of the view on which to call the method.
5221      * @param methodName The name of the method to call.
5222      * @param value The value to pass to the method.
5223      */
5224     public void setUri(@IdRes int viewId, String methodName, Uri value) {
5225         if (value != null) {
5226             // Resolve any filesystem path before sending remotely
5227             value = value.getCanonicalUri();
5228             if (StrictMode.vmFileUriExposureEnabled()) {
5229                 value.checkFileUriExposed("RemoteViews.setUri()");
5230             }
5231         }
5232         addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.URI, value));
5233     }
5234 
5235     /**
5236      * Call a method taking one Bitmap on a view in the layout for this RemoteViews.
5237      * @more
5238      * <p class="note">The bitmap will be flattened into the parcel if this object is
5239      * sent across processes, so it may end up using a lot of memory, and may be fairly slow.</p>
5240      *
5241      * @param viewId The id of the view on which to call the method.
5242      * @param methodName The name of the method to call.
5243      * @param value The value to pass to the method.
5244      */
5245     public void setBitmap(@IdRes int viewId, String methodName, Bitmap value) {
5246         addAction(new BitmapReflectionAction(viewId, methodName, value));
5247     }
5248 
5249     /**
5250      * Call a method taking one BlendMode on a view in the layout for this RemoteViews.
5251      *
5252      * @param viewId The id of the view on which to call the method.
5253      * @param methodName The name of the method to call.
5254      * @param value The value to pass to the method.
5255      */
5256     public void setBlendMode(@IdRes int viewId, @NonNull String methodName,
5257             @Nullable BlendMode value) {
5258         addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.BLEND_MODE, value));
5259     }
5260 
5261     /**
5262      * Call a method taking one Bundle on a view in the layout for this RemoteViews.
5263      *
5264      * @param viewId The id of the view on which to call the method.
5265      * @param methodName The name of the method to call.
5266      * @param value The value to pass to the method.
5267      */
5268     public void setBundle(@IdRes int viewId, String methodName, Bundle value) {
5269         addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.BUNDLE, value));
5270     }
5271 
5272     /**
5273      * Call a method taking one Intent on a view in the layout for this RemoteViews.
5274      *
5275      * @param viewId The id of the view on which to call the method.
5276      * @param methodName The name of the method to call.
5277      * @param value The {@link android.content.Intent} to pass the method.
5278      */
5279     public void setIntent(@IdRes int viewId, String methodName, Intent value) {
5280         addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.INTENT, value));
5281     }
5282 
5283     /**
5284      * Call a method taking one Icon on a view in the layout for this RemoteViews.
5285      *
5286      * @param viewId The id of the view on which to call the method.
5287      * @param methodName The name of the method to call.
5288      * @param value The {@link android.graphics.drawable.Icon} to pass the method.
5289      */
5290     public void setIcon(@IdRes int viewId, String methodName, Icon value) {
5291         addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.ICON, value));
5292     }
5293 
5294     /**
5295      * Call a method taking one Icon on a view in the layout for this RemoteViews.
5296      *
5297      * @param viewId The id of the view on which to call the method.
5298      * @param methodName The name of the method to call.
5299      * @param notNight The value to pass to the method when the view's configuration is set to
5300      *                 {@link Configuration#UI_MODE_NIGHT_NO}
5301      * @param night The value to pass to the method when the view's configuration is set to
5302      *                 {@link Configuration#UI_MODE_NIGHT_YES}
5303      */
5304     public void setIcon(
5305             @IdRes int viewId,
5306             @NonNull String methodName,
5307             @Nullable Icon notNight,
5308             @Nullable Icon night) {
5309         addAction(
5310                 new NightModeReflectionAction(
5311                         viewId,
5312                         methodName,
5313                         BaseReflectionAction.ICON,
5314                         notNight,
5315                         night));
5316     }
5317 
5318     /**
5319      * Equivalent to calling View.setContentDescription(CharSequence).
5320      *
5321      * @param viewId The id of the view whose content description should change.
5322      * @param contentDescription The new content description for the view.
5323      */
5324     public void setContentDescription(@IdRes int viewId, CharSequence contentDescription) {
5325         setCharSequence(viewId, "setContentDescription", contentDescription);
5326     }
5327 
5328     /**
5329      * Equivalent to calling {@link android.view.View#setAccessibilityTraversalBefore(int)}.
5330      *
5331      * @param viewId The id of the view whose before view in accessibility traversal to set.
5332      * @param nextId The id of the next in the accessibility traversal.
5333      **/
5334     public void setAccessibilityTraversalBefore(@IdRes int viewId, @IdRes int nextId) {
5335         setInt(viewId, "setAccessibilityTraversalBefore", nextId);
5336     }
5337 
5338     /**
5339      * Equivalent to calling {@link android.view.View#setAccessibilityTraversalAfter(int)}.
5340      *
5341      * @param viewId The id of the view whose after view in accessibility traversal to set.
5342      * @param nextId The id of the next in the accessibility traversal.
5343      **/
5344     public void setAccessibilityTraversalAfter(@IdRes int viewId, @IdRes int nextId) {
5345         setInt(viewId, "setAccessibilityTraversalAfter", nextId);
5346     }
5347 
5348     /**
5349      * Equivalent to calling {@link View#setLabelFor(int)}.
5350      *
5351      * @param viewId The id of the view whose property to set.
5352      * @param labeledId The id of a view for which this view serves as a label.
5353      */
5354     public void setLabelFor(@IdRes int viewId, @IdRes int labeledId) {
5355         setInt(viewId, "setLabelFor", labeledId);
5356     }
5357 
5358     /**
5359      * Equivalent to calling {@link android.widget.CompoundButton#setChecked(boolean)}.
5360      *
5361      * @param viewId The id of the view whose property to set.
5362      * @param checked true to check the button, false to uncheck it.
5363      */
5364     public void setCompoundButtonChecked(@IdRes int viewId, boolean checked) {
5365         addAction(new SetCompoundButtonCheckedAction(viewId, checked));
5366     }
5367 
5368     /**
5369      * Equivalent to calling {@link android.widget.RadioGroup#check(int)}.
5370      *
5371      * @param viewId The id of the view whose property to set.
5372      * @param checkedId The unique id of the radio button to select in the group.
5373      */
5374     public void setRadioGroupChecked(@IdRes int viewId, @IdRes int checkedId) {
5375         addAction(new SetRadioGroupCheckedAction(viewId, checkedId));
5376     }
5377 
5378     /**
5379      * Provides an alternate layout ID, which can be used to inflate this view. This layout will be
5380      * used by the host when the widgets displayed on a light-background where foreground elements
5381      * and text can safely draw using a dark color without any additional background protection.
5382      */
5383     public void setLightBackgroundLayoutId(@LayoutRes int layoutId) {
5384         mLightBackgroundLayoutId = layoutId;
5385     }
5386 
5387     /**
5388      * If this view supports dark text versions, creates a copy representing that version,
5389      * otherwise returns itself.
5390      * @hide
5391      */
5392     public RemoteViews getDarkTextViews() {
5393         if (hasFlags(FLAG_USE_LIGHT_BACKGROUND_LAYOUT)) {
5394             return this;
5395         }
5396 
5397         try {
5398             addFlags(FLAG_USE_LIGHT_BACKGROUND_LAYOUT);
5399             return new RemoteViews(this);
5400         } finally {
5401             mApplyFlags &= ~FLAG_USE_LIGHT_BACKGROUND_LAYOUT;
5402         }
5403     }
5404 
5405     private RemoteViews getRemoteViewsToApply(Context context) {
5406         if (hasLandscapeAndPortraitLayouts()) {
5407             int orientation = context.getResources().getConfiguration().orientation;
5408             if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
5409                 return mLandscape;
5410             }
5411             return mPortrait;
5412         }
5413         if (hasSizedRemoteViews()) {
5414             return findSmallestRemoteView();
5415         }
5416         return this;
5417     }
5418 
5419     /**
5420      * Returns the square distance between two points.
5421      *
5422      * This is particularly useful when we only care about the ordering of the distances.
5423      */
5424     private static float squareDistance(SizeF p1, SizeF p2) {
5425         float dx = p1.getWidth() - p2.getWidth();
5426         float dy = p1.getHeight() - p2.getHeight();
5427         return dx * dx + dy * dy;
5428     }
5429 
5430     /**
5431      * Returns whether the layout fits in the space available to the widget.
5432      *
5433      * A layout fits on a widget if the widget size is known (i.e. not null) and both dimensions
5434      * are smaller than the ones of the widget, adding some padding to account for rounding errors.
5435      */
5436     private static boolean fitsIn(SizeF sizeLayout, @Nullable SizeF sizeWidget) {
5437         return sizeWidget != null && (Math.ceil(sizeWidget.getWidth()) + 1 > sizeLayout.getWidth())
5438                 && (Math.ceil(sizeWidget.getHeight()) + 1 > sizeLayout.getHeight());
5439     }
5440 
5441     private RemoteViews findBestFitLayout(@NonNull SizeF widgetSize) {
5442         // Find the better remote view
5443         RemoteViews bestFit = null;
5444         float bestSqDist = Float.MAX_VALUE;
5445         for (RemoteViews layout : mSizedRemoteViews) {
5446             SizeF layoutSize = layout.getIdealSize();
5447             if (layoutSize == null) {
5448                 throw new IllegalStateException("Expected RemoteViews to have ideal size");
5449             }
5450 
5451             if (fitsIn(layoutSize, widgetSize)) {
5452                 if (bestFit == null) {
5453                     bestFit = layout;
5454                     bestSqDist = squareDistance(layoutSize, widgetSize);
5455                 } else {
5456                     float newSqDist = squareDistance(layoutSize, widgetSize);
5457                     if (newSqDist < bestSqDist) {
5458                         bestFit = layout;
5459                         bestSqDist = newSqDist;
5460                     }
5461                 }
5462             }
5463         }
5464         if (bestFit == null) {
5465             Log.w(LOG_TAG, "Could not find a RemoteViews fitting the current size: " + widgetSize);
5466             return findSmallestRemoteView();
5467         }
5468         return bestFit;
5469     }
5470 
5471     /**
5472      * Returns the most appropriate {@link RemoteViews} given the context and, if not null, the
5473      * size of the widget.
5474      *
5475      * If {@link RemoteViews#hasSizedRemoteViews()} returns true, the most appropriate view is
5476      * the one that fits in the widget (according to {@link RemoteViews#fitsIn}) and has the
5477      * diagonal the most similar to the widget. If no layout fits or the size of the widget is
5478      * not specified, the one with the smallest area will be chosen.
5479      *
5480      * @hide
5481      */
5482     public RemoteViews getRemoteViewsToApply(@NonNull Context context,
5483             @Nullable SizeF widgetSize) {
5484         if (!hasSizedRemoteViews() || widgetSize == null) {
5485             // If there isn't multiple remote views, fall back on the previous methods.
5486             return getRemoteViewsToApply(context);
5487         }
5488         return findBestFitLayout(widgetSize);
5489     }
5490 
5491     /**
5492      * Checks whether the change of size will lead to using a different {@link RemoteViews}.
5493      *
5494      * @hide
5495      */
5496     @Nullable
5497     public RemoteViews getRemoteViewsToApplyIfDifferent(@Nullable SizeF oldSize,
5498             @NonNull SizeF newSize) {
5499         if (!hasSizedRemoteViews()) {
5500             return null;
5501         }
5502         RemoteViews oldBestFit = oldSize == null ? findSmallestRemoteView() : findBestFitLayout(
5503                 oldSize);
5504         RemoteViews newBestFit = findBestFitLayout(newSize);
5505         if (oldBestFit != newBestFit) {
5506             return newBestFit;
5507         }
5508         return null;
5509     }
5510 
5511 
5512     /**
5513      * Inflates the view hierarchy represented by this object and applies
5514      * all of the actions.
5515      *
5516      * <p><strong>Caller beware: this may throw</strong>
5517      *
5518      * @param context Default context to use
5519      * @param parent Parent that the resulting view hierarchy will be attached to. This method
5520      * does <strong>not</strong> attach the hierarchy. The caller should do so when appropriate.
5521      * @return The inflated view hierarchy
5522      */
5523     public View apply(Context context, ViewGroup parent) {
5524         return apply(context, parent, null);
5525     }
5526 
5527     /** @hide */
5528     public View apply(Context context, ViewGroup parent, InteractionHandler handler) {
5529         return apply(context, parent, handler, null);
5530     }
5531 
5532     /** @hide */
5533     public View apply(@NonNull Context context, @NonNull ViewGroup parent,
5534             @Nullable InteractionHandler handler, @Nullable SizeF size) {
5535         RemoteViews rvToApply = getRemoteViewsToApply(context, size);
5536 
5537         View result = inflateView(context, rvToApply, parent);
5538         rvToApply.performApply(result, parent, handler, null);
5539         return result;
5540     }
5541 
5542     /** @hide */
5543     public View applyWithTheme(@NonNull Context context, @NonNull ViewGroup parent,
5544             @Nullable InteractionHandler handler, @StyleRes int applyThemeResId) {
5545         return applyWithTheme(context, parent, handler, applyThemeResId, null);
5546     }
5547 
5548     /** @hide */
5549     public View applyWithTheme(@NonNull Context context, @NonNull ViewGroup parent,
5550             @Nullable InteractionHandler handler, @StyleRes int applyThemeResId,
5551             @Nullable SizeF size) {
5552         RemoteViews rvToApply = getRemoteViewsToApply(context, size);
5553 
5554         View result = inflateView(context, rvToApply, parent, applyThemeResId, null);
5555         rvToApply.performApply(result, parent, handler, null);
5556         return result;
5557     }
5558 
5559     /** @hide */
5560     public View apply(Context context, ViewGroup parent, InteractionHandler handler,
5561             @Nullable SizeF size, @Nullable ColorResources colorResources) {
5562         RemoteViews rvToApply = getRemoteViewsToApply(context, size);
5563 
5564         View result = inflateView(context, rvToApply, parent, 0, colorResources);
5565         rvToApply.performApply(result, parent, handler, colorResources);
5566         return result;
5567     }
5568 
5569     private View inflateView(Context context, RemoteViews rv, ViewGroup parent) {
5570         return inflateView(context, rv, parent, 0, null);
5571     }
5572 
5573     private View inflateView(Context context, RemoteViews rv, @Nullable ViewGroup parent,
5574             @StyleRes int applyThemeResId, @Nullable ColorResources colorResources) {
5575         // RemoteViews may be built by an application installed in another
5576         // user. So build a context that loads resources from that user but
5577         // still returns the current users userId so settings like data / time formats
5578         // are loaded without requiring cross user persmissions.
5579         final Context contextForResources =
5580                 getContextForResourcesEnsuringCorrectCachedApkPaths(context);
5581         if (colorResources != null) {
5582             colorResources.apply(contextForResources);
5583         }
5584         Context inflationContext = new RemoteViewsContextWrapper(context, contextForResources);
5585 
5586         // If mApplyThemeResId is not given, Theme.DeviceDefault will be used.
5587         if (applyThemeResId != 0) {
5588             inflationContext = new ContextThemeWrapper(inflationContext, applyThemeResId);
5589         }
5590         LayoutInflater inflater = LayoutInflater.from(context);
5591 
5592         // Clone inflater so we load resources from correct context and
5593         // we don't add a filter to the static version returned by getSystemService.
5594         inflater = inflater.cloneInContext(inflationContext);
5595         inflater.setFilter(shouldUseStaticFilter() ? INFLATER_FILTER : this);
5596         View v = inflater.inflate(rv.getLayoutId(), parent, false);
5597         if (mViewId != View.NO_ID) {
5598             v.setId(mViewId);
5599             v.setTagInternal(R.id.remote_views_override_id, mViewId);
5600         }
5601         v.setTagInternal(R.id.widget_frame, rv.getLayoutId());
5602         return v;
5603     }
5604 
5605     /**
5606      * A static filter is much lighter than RemoteViews itself. It's optimized here only for
5607      * RemoteVies class. Subclasses should always override this and return true if not overriding
5608      * {@link this#onLoadClass(Class)}.
5609      *
5610      * @hide
5611      */
5612     protected boolean shouldUseStaticFilter() {
5613         return this.getClass().equals(RemoteViews.class);
5614     }
5615 
5616     /**
5617      * Implement this interface to receive a callback when
5618      * {@link #applyAsync} or {@link #reapplyAsync} is finished.
5619      * @hide
5620      */
5621     public interface OnViewAppliedListener {
5622         /**
5623          * Callback when the RemoteView has finished inflating,
5624          * but no actions have been applied yet.
5625          */
5626         default void onViewInflated(View v) {};
5627 
5628         void onViewApplied(View v);
5629 
5630         void onError(Exception e);
5631     }
5632 
5633     /**
5634      * Applies the views asynchronously, moving as much of the task on the background
5635      * thread as possible.
5636      *
5637      * @see #apply(Context, ViewGroup)
5638      * @param context Default context to use
5639      * @param parent Parent that the resulting view hierarchy will be attached to. This method
5640      * does <strong>not</strong> attach the hierarchy. The caller should do so when appropriate.
5641      * @param listener the callback to run when all actions have been applied. May be null.
5642      * @param executor The executor to use. If null {@link AsyncTask#THREAD_POOL_EXECUTOR} is used.
5643      * @return CancellationSignal
5644      * @hide
5645      */
5646     public CancellationSignal applyAsync(
5647             Context context, ViewGroup parent, Executor executor, OnViewAppliedListener listener) {
5648         return applyAsync(context, parent, executor, listener, null /* handler */);
5649     }
5650 
5651 
5652     /** @hide */
5653     public CancellationSignal applyAsync(Context context, ViewGroup parent,
5654             Executor executor, OnViewAppliedListener listener, InteractionHandler handler) {
5655         return applyAsync(context, parent, executor, listener, handler, null /* size */);
5656     }
5657 
5658     /** @hide */
5659     public CancellationSignal applyAsync(Context context, ViewGroup parent,
5660             Executor executor, OnViewAppliedListener listener, InteractionHandler handler,
5661             SizeF size) {
5662         return applyAsync(context, parent, executor, listener, handler, size,
5663                 null /* themeColors */);
5664     }
5665 
5666     /** @hide */
5667     public CancellationSignal applyAsync(Context context, ViewGroup parent, Executor executor,
5668             OnViewAppliedListener listener, InteractionHandler handler, SizeF size,
5669             ColorResources colorResources) {
5670         return new AsyncApplyTask(getRemoteViewsToApply(context, size), parent, context, listener,
5671                 handler, colorResources, null /* result */,
5672                 true /* topLevel */).startTaskOnExecutor(executor);
5673     }
5674 
5675     private AsyncApplyTask getInternalAsyncApplyTask(Context context, ViewGroup parent,
5676             OnViewAppliedListener listener, InteractionHandler handler, SizeF size,
5677             ColorResources colorResources, View result) {
5678         return new AsyncApplyTask(getRemoteViewsToApply(context, size), parent, context, listener,
5679                 handler, colorResources, result, false /* topLevel */);
5680     }
5681 
5682     private class AsyncApplyTask extends AsyncTask<Void, Void, ViewTree>
5683             implements CancellationSignal.OnCancelListener {
5684         final CancellationSignal mCancelSignal = new CancellationSignal();
5685         final RemoteViews mRV;
5686         final ViewGroup mParent;
5687         final Context mContext;
5688         final OnViewAppliedListener mListener;
5689         final InteractionHandler mHandler;
5690         final ColorResources mColorResources;
5691         /**
5692          * Whether the remote view is the top-level one (i.e. not within an action).
5693          *
5694          * This is only used if the result is specified (i.e. the view is being recycled).
5695          */
5696         final boolean mTopLevel;
5697 
5698         private View mResult;
5699         private ViewTree mTree;
5700         private Action[] mActions;
5701         private Exception mError;
5702 
5703         private AsyncApplyTask(
5704                 RemoteViews rv, ViewGroup parent, Context context, OnViewAppliedListener listener,
5705                 InteractionHandler handler, ColorResources colorResources,
5706                 View result, boolean topLevel) {
5707             mRV = rv;
5708             mParent = parent;
5709             mContext = context;
5710             mListener = listener;
5711             mColorResources = colorResources;
5712             mHandler = handler;
5713             mTopLevel = topLevel;
5714 
5715             mResult = result;
5716         }
5717 
5718         @Nullable
5719         @Override
5720         protected ViewTree doInBackground(Void... params) {
5721             try {
5722                 if (mResult == null) {
5723                     mResult = inflateView(mContext, mRV, mParent, 0, mColorResources);
5724                 }
5725 
5726                 mTree = new ViewTree(mResult);
5727                 if (mRV.mActions != null) {
5728                     int count = mRV.mActions.size();
5729                     mActions = new Action[count];
5730                     for (int i = 0; i < count && !isCancelled(); i++) {
5731                         // TODO: check if isCancelled in nested views.
5732                         mActions[i] = mRV.mActions.get(i).initActionAsync(mTree, mParent, mHandler,
5733                                 mColorResources);
5734                     }
5735                 } else {
5736                     mActions = null;
5737                 }
5738                 return mTree;
5739             } catch (Exception e) {
5740                 mError = e;
5741                 return null;
5742             }
5743         }
5744 
5745         @Override
5746         protected void onPostExecute(ViewTree viewTree) {
5747             mCancelSignal.setOnCancelListener(null);
5748             if (mError == null) {
5749                 if (mListener != null) {
5750                     mListener.onViewInflated(viewTree.mRoot);
5751                 }
5752 
5753                 try {
5754                     if (mActions != null) {
5755                         InteractionHandler handler = mHandler == null
5756                                 ? DEFAULT_INTERACTION_HANDLER : mHandler;
5757                         for (Action a : mActions) {
5758                             a.apply(viewTree.mRoot, mParent, handler, mColorResources);
5759                         }
5760                     }
5761                     // If the parent of the view is has is a root, resolve the recycling.
5762                     if (mTopLevel && mResult instanceof ViewGroup) {
5763                         finalizeViewRecycling((ViewGroup) mResult);
5764                     }
5765                 } catch (Exception e) {
5766                     mError = e;
5767                 }
5768             }
5769 
5770             if (mListener != null) {
5771                 if (mError != null) {
5772                     mListener.onError(mError);
5773                 } else {
5774                     mListener.onViewApplied(viewTree.mRoot);
5775                 }
5776             } else if (mError != null) {
5777                 if (mError instanceof ActionException) {
5778                     throw (ActionException) mError;
5779                 } else {
5780                     throw new ActionException(mError);
5781                 }
5782             }
5783         }
5784 
5785         @Override
5786         public void onCancel() {
5787             cancel(true);
5788         }
5789 
5790         private CancellationSignal startTaskOnExecutor(Executor executor) {
5791             mCancelSignal.setOnCancelListener(this);
5792             executeOnExecutor(executor == null ? AsyncTask.THREAD_POOL_EXECUTOR : executor);
5793             return mCancelSignal;
5794         }
5795     }
5796 
5797     /**
5798      * Applies all of the actions to the provided view.
5799      *
5800      * <p><strong>Caller beware: this may throw</strong>
5801      *
5802      * @param v The view to apply the actions to.  This should be the result of
5803      * the {@link #apply(Context,ViewGroup)} call.
5804      */
5805     public void reapply(Context context, View v) {
5806         reapply(context, v, null /* handler */);
5807     }
5808 
5809     /** @hide */
5810     public void reapply(Context context, View v, InteractionHandler handler) {
5811         reapply(context, v, handler, null /* size */, null /* colorResources */);
5812     }
5813 
5814     /** @hide */
5815     public void reapply(Context context, View v, InteractionHandler handler, SizeF size,
5816             ColorResources colorResources) {
5817         reapply(context, v, handler, size, colorResources, true);
5818     }
5819 
5820     /** @hide */
5821     public boolean canRecycleView(@Nullable View v) {
5822         if (v == null) {
5823             return false;
5824         }
5825         Integer previousLayoutId = (Integer) v.getTag(R.id.widget_frame);
5826         if (previousLayoutId == null) {
5827             return false;
5828         }
5829         Integer overrideIdTag = (Integer) v.getTag(R.id.remote_views_override_id);
5830         int overrideId = overrideIdTag == null ? View.NO_ID : overrideIdTag;
5831         // If mViewId is View.NO_ID, we only recycle if overrideId is also View.NO_ID.
5832         // Otherwise, it might be that, on a previous iteration, the view's ID was set to
5833         // something else, and it should now be reset to the ID defined in the XML layout file,
5834         // whatever it is.
5835         return previousLayoutId == getLayoutId() && mViewId == overrideId;
5836     }
5837 
5838     /**
5839      * Returns the RemoteViews that should be used in the reapply operation.
5840      *
5841      * If the current RemoteViews has multiple layout, this will select the correct one.
5842      *
5843      * @throws RuntimeException If the current RemoteViews should not be reapplied onto the provided
5844      * View.
5845      */
5846     private RemoteViews getRemoteViewsToReapply(Context context, View v, @Nullable SizeF size) {
5847         RemoteViews rvToApply = getRemoteViewsToApply(context, size);
5848 
5849         // In the case that a view has this RemoteViews applied in one orientation or size, is
5850         // persisted across change, and has the RemoteViews re-applied in a different situation
5851         // (orientation or size), we throw an exception, since the layouts may be completely
5852         // unrelated.
5853         // If the ViewID has been changed on the view, or is changed by the RemoteViews, we also
5854         // may throw an exception, as the RemoteViews will probably not apply properly.
5855         // However, we need to let potentially unrelated RemoteViews apply, as this lack of testing
5856         // is already used in production code in some apps.
5857         if (hasMultipleLayouts()
5858                 || rvToApply.mViewId != View.NO_ID
5859                 || v.getTag(R.id.remote_views_override_id) != null) {
5860             if (!rvToApply.canRecycleView(v)) {
5861                 throw new RuntimeException("Attempting to re-apply RemoteViews to a view that" +
5862                         " that does not share the same root layout id.");
5863             }
5864         }
5865 
5866         return rvToApply;
5867     }
5868 
5869     // Note: topLevel should be true only for calls on the topLevel RemoteViews, internal calls
5870     // should set it to false.
5871     private void reapply(Context context, View v, InteractionHandler handler, SizeF size,
5872             ColorResources colorResources, boolean topLevel) {
5873 
5874         RemoteViews rvToApply = getRemoteViewsToReapply(context, v, size);
5875 
5876         rvToApply.performApply(v, (ViewGroup) v.getParent(), handler, colorResources);
5877 
5878         // If the parent of the view is has is a root, resolve the recycling.
5879         if (topLevel && v instanceof ViewGroup) {
5880             finalizeViewRecycling((ViewGroup) v);
5881         }
5882     }
5883 
5884     /**
5885      * Applies all the actions to the provided view, moving as much of the task on the background
5886      * thread as possible.
5887      *
5888      * @see #reapply(Context, View)
5889      * @param context Default context to use
5890      * @param v The view to apply the actions to.  This should be the result of
5891      * the {@link #apply(Context,ViewGroup)} call.
5892      * @param listener the callback to run when all actions have been applied. May be null.
5893      * @param executor The executor to use. If null {@link AsyncTask#THREAD_POOL_EXECUTOR} is used
5894      * @return CancellationSignal
5895      * @hide
5896      */
5897     public CancellationSignal reapplyAsync(Context context, View v, Executor executor,
5898             OnViewAppliedListener listener) {
5899         return reapplyAsync(context, v, executor, listener, null);
5900     }
5901 
5902     /** @hide */
5903     public CancellationSignal reapplyAsync(Context context, View v, Executor executor,
5904             OnViewAppliedListener listener, InteractionHandler handler) {
5905         return reapplyAsync(context, v, executor, listener, handler, null, null);
5906     }
5907 
5908     /** @hide */
5909     public CancellationSignal reapplyAsync(Context context, View v, Executor executor,
5910             OnViewAppliedListener listener, InteractionHandler handler, SizeF size,
5911             ColorResources colorResources) {
5912         RemoteViews rvToApply = getRemoteViewsToReapply(context, v, size);
5913 
5914         return new AsyncApplyTask(rvToApply, (ViewGroup) v.getParent(),
5915                 context, listener, handler, colorResources, v, true /* topLevel */)
5916                 .startTaskOnExecutor(executor);
5917     }
5918 
5919     private void performApply(View v, ViewGroup parent, InteractionHandler handler,
5920             ColorResources colorResources) {
5921         if (mActions != null) {
5922             handler = handler == null ? DEFAULT_INTERACTION_HANDLER : handler;
5923             final int count = mActions.size();
5924             for (int i = 0; i < count; i++) {
5925                 Action a = mActions.get(i);
5926                 a.apply(v, parent, handler, colorResources);
5927             }
5928         }
5929     }
5930 
5931     /**
5932      * Returns true if the RemoteViews contains potentially costly operations and should be
5933      * applied asynchronously.
5934      *
5935      * @hide
5936      */
5937     public boolean prefersAsyncApply() {
5938         if (mActions != null) {
5939             final int count = mActions.size();
5940             for (int i = 0; i < count; i++) {
5941                 if (mActions.get(i).prefersAsyncApply()) {
5942                     return true;
5943                 }
5944             }
5945         }
5946         return false;
5947     }
5948 
5949     /** @hide */
5950     public void updateAppInfo(@NonNull ApplicationInfo info) {
5951         ApplicationInfo existing = mApplicationInfoCache.get(info);
5952         if (existing != null && !existing.sourceDir.equals(info.sourceDir)) {
5953             // Overlay paths are generated against a particular version of an application.
5954             // The overlays paths of a newly upgraded application are incompatible with the
5955             // old version of the application.
5956             return;
5957         }
5958 
5959         // If we can update to the new AppInfo, put it in the cache and propagate the change
5960         // throughout the hierarchy.
5961         mApplicationInfoCache.put(info);
5962         configureDescendantsAsChildren();
5963     }
5964 
5965     private Context getContextForResourcesEnsuringCorrectCachedApkPaths(Context context) {
5966         if (mApplication != null) {
5967             if (context.getUserId() == UserHandle.getUserId(mApplication.uid)
5968                     && context.getPackageName().equals(mApplication.packageName)) {
5969                 return context;
5970             }
5971             try {
5972                 LoadedApk.checkAndUpdateApkPaths(mApplication);
5973                 return context.createApplicationContext(mApplication,
5974                         Context.CONTEXT_RESTRICTED);
5975             } catch (NameNotFoundException e) {
5976                 Log.e(LOG_TAG, "Package name " + mApplication.packageName + " not found");
5977             }
5978         }
5979 
5980         return context;
5981     }
5982 
5983     /**
5984      * Object allowing the modification of a context to overload the system's dynamic colors.
5985      *
5986      * Only colors from {@link android.R.color#system_accent1_0} to
5987      * {@link android.R.color#system_neutral2_1000} can be overloaded.
5988      * @hide
5989      */
5990     public static final class ColorResources {
5991         // Set of valid colors resources.
5992         private static final int FIRST_RESOURCE_COLOR_ID = android.R.color.system_neutral1_0;
5993         private static final int LAST_RESOURCE_COLOR_ID = android.R.color.system_accent3_1000;
5994         // Size, in bytes, of an entry in the array of colors in an ARSC file.
5995         private static final int ARSC_ENTRY_SIZE = 16;
5996 
5997         private ResourcesLoader mLoader;
5998 
5999         private ColorResources(ResourcesLoader loader) {
6000             mLoader = loader;
6001         }
6002 
6003         /**
6004          * Apply the color resources to the given context.
6005          *
6006          * No resource resolution must have be done on the context given to that method.
6007          */
6008         public void apply(Context context) {
6009             context.getResources().addLoaders(mLoader);
6010         }
6011 
6012         private static ByteArrayOutputStream readFileContent(InputStream input) throws IOException {
6013             ByteArrayOutputStream content = new ByteArrayOutputStream(2048);
6014             byte[] buffer = new byte[4096];
6015             while (input.available() > 0) {
6016                 int read = input.read(buffer);
6017                 content.write(buffer, 0, read);
6018             }
6019             return content;
6020         }
6021 
6022         /**
6023          * Creates the compiled resources content from the asset stored in the APK.
6024          *
6025          * The asset is a compiled resource with the correct resources name and correct ids, only
6026          * the values are incorrect. The last value is at the very end of the file. The resources
6027          * are in an array, the array's entries are 16 bytes each. We use this to work out the
6028          * location of all the positions of the various resources.
6029          */
6030         @Nullable
6031         private static byte[] createCompiledResourcesContent(Context context,
6032                 SparseIntArray colorResources) throws IOException {
6033             byte[] content;
6034             try (InputStream input = context.getResources().openRawResource(
6035                     com.android.internal.R.raw.remote_views_color_resources)) {
6036                 ByteArrayOutputStream rawContent = readFileContent(input);
6037                 content = rawContent.toByteArray();
6038             }
6039             int valuesOffset =
6040                     content.length - (LAST_RESOURCE_COLOR_ID & 0xffff) * ARSC_ENTRY_SIZE - 4;
6041             if (valuesOffset < 0) {
6042                 Log.e(LOG_TAG, "ARSC file for theme colors is invalid.");
6043                 return null;
6044             }
6045             for (int colorRes = FIRST_RESOURCE_COLOR_ID; colorRes <= LAST_RESOURCE_COLOR_ID;
6046                     colorRes++) {
6047                 // The last 2 bytes are the index in the color array.
6048                 int index = colorRes & 0xffff;
6049                 int offset = valuesOffset + index * ARSC_ENTRY_SIZE;
6050                 int value = colorResources.get(colorRes, context.getColor(colorRes));
6051                 // Write the 32 bit integer in little endian
6052                 for (int b = 0; b < 4; b++) {
6053                     content[offset + b] = (byte) (value & 0xff);
6054                     value >>= 8;
6055                 }
6056             }
6057             return content;
6058         }
6059 
6060         /**
6061          *  Adds a resource loader for theme colors to the given context.
6062          *
6063          * @param context Context of the view hosting the widget.
6064          * @param colorMapping Mapping of resources to color values.
6065          *
6066          * @hide
6067          */
6068         @Nullable
6069         public static ColorResources create(Context context, SparseIntArray colorMapping) {
6070             try {
6071                 byte[] contentBytes = createCompiledResourcesContent(context, colorMapping);
6072                 if (contentBytes == null) {
6073                     return null;
6074                 }
6075                 FileDescriptor arscFile = null;
6076                 try {
6077                     arscFile = Os.memfd_create("remote_views_theme_colors.arsc", 0 /* flags */);
6078                     // Note: This must not be closed through the OutputStream.
6079                     try (OutputStream pipeWriter = new FileOutputStream(arscFile)) {
6080                         pipeWriter.write(contentBytes);
6081 
6082                         try (ParcelFileDescriptor pfd = ParcelFileDescriptor.dup(arscFile)) {
6083                             ResourcesLoader colorsLoader = new ResourcesLoader();
6084                             colorsLoader.addProvider(ResourcesProvider
6085                                     .loadFromTable(pfd, null /* assetsProvider */));
6086                             return new ColorResources(colorsLoader);
6087                         }
6088                     }
6089                 } finally {
6090                     if (arscFile != null) {
6091                         Os.close(arscFile);
6092                     }
6093                 }
6094             } catch (Exception ex) {
6095                 Log.e(LOG_TAG, "Failed to setup the context for theme colors", ex);
6096             }
6097             return null;
6098         }
6099     }
6100 
6101     /**
6102      * Returns the number of actions in this RemoteViews. Can be used as a sequence number.
6103      *
6104      * @hide
6105      */
6106     public int getSequenceNumber() {
6107         return (mActions == null) ? 0 : mActions.size();
6108     }
6109 
6110     /**
6111      * Used to restrict the views which can be inflated
6112      *
6113      * @see android.view.LayoutInflater.Filter#onLoadClass(java.lang.Class)
6114      * @deprecated Used by system to enforce safe inflation of {@link RemoteViews}. Apps should not
6115      * override this method. Changing of this method will NOT affect the process where RemoteViews
6116      * is rendered.
6117      */
6118     @Deprecated
6119     public boolean onLoadClass(Class clazz) {
6120         return clazz.isAnnotationPresent(RemoteView.class);
6121     }
6122 
6123     public int describeContents() {
6124         return 0;
6125     }
6126 
6127     public void writeToParcel(Parcel dest, int flags) {
6128         boolean prevSquashingAllowed = dest.allowSquashing();
6129 
6130         if (!hasMultipleLayouts()) {
6131             dest.writeInt(MODE_NORMAL);
6132             // We only write the bitmap cache if we are the root RemoteViews, as this cache
6133             // is shared by all children.
6134             if (mIsRoot) {
6135                 mBitmapCache.writeBitmapsToParcel(dest, flags);
6136             }
6137             mApplication.writeToParcel(dest, flags);
6138             if (mIsRoot || mIdealSize == null) {
6139                 dest.writeInt(0);
6140             } else {
6141                 dest.writeInt(1);
6142                 mIdealSize.writeToParcel(dest, flags);
6143             }
6144             dest.writeInt(mLayoutId);
6145             dest.writeInt(mViewId);
6146             dest.writeInt(mLightBackgroundLayoutId);
6147             writeActionsToParcel(dest, flags);
6148         } else if (hasSizedRemoteViews()) {
6149             dest.writeInt(MODE_HAS_SIZED_REMOTEVIEWS);
6150             if (mIsRoot) {
6151                 mBitmapCache.writeBitmapsToParcel(dest, flags);
6152             }
6153             dest.writeInt(mSizedRemoteViews.size());
6154             for (RemoteViews view : mSizedRemoteViews) {
6155                 view.writeToParcel(dest, flags);
6156             }
6157         } else {
6158             dest.writeInt(MODE_HAS_LANDSCAPE_AND_PORTRAIT);
6159             // We only write the bitmap cache if we are the root RemoteViews, as this cache
6160             // is shared by all children.
6161             if (mIsRoot) {
6162                 mBitmapCache.writeBitmapsToParcel(dest, flags);
6163             }
6164             mLandscape.writeToParcel(dest, flags);
6165             // Both RemoteViews already share the same package and user
6166             mPortrait.writeToParcel(dest, flags);
6167         }
6168         dest.writeInt(mApplyFlags);
6169         dest.writeLong(mProviderInstanceId);
6170 
6171         dest.restoreAllowSquashing(prevSquashingAllowed);
6172     }
6173 
6174     private void writeActionsToParcel(Parcel parcel, int flags) {
6175         int count;
6176         if (mActions != null) {
6177             count = mActions.size();
6178         } else {
6179             count = 0;
6180         }
6181         parcel.writeInt(count);
6182         for (int i = 0; i < count; i++) {
6183             Action a = mActions.get(i);
6184             parcel.writeInt(a.getActionTag());
6185             a.writeToParcel(parcel, flags);
6186         }
6187     }
6188 
6189     @Nullable
6190     private static ApplicationInfo getApplicationInfo(@Nullable String packageName, int userId) {
6191         if (packageName == null) {
6192             return null;
6193         }
6194 
6195         // Get the application for the passed in package and user.
6196         Application application = ActivityThread.currentApplication();
6197         if (application == null) {
6198             throw new IllegalStateException("Cannot create remote views out of an aplication.");
6199         }
6200 
6201         ApplicationInfo applicationInfo = application.getApplicationInfo();
6202         if (UserHandle.getUserId(applicationInfo.uid) != userId
6203                 || !applicationInfo.packageName.equals(packageName)) {
6204             try {
6205                 Context context = application.getBaseContext().createPackageContextAsUser(
6206                         packageName, 0, new UserHandle(userId));
6207                 applicationInfo = context.getApplicationInfo();
6208             } catch (NameNotFoundException nnfe) {
6209                 throw new IllegalArgumentException("No such package " + packageName);
6210             }
6211         }
6212 
6213         return applicationInfo;
6214     }
6215 
6216     /**
6217      * Returns true if the {@link #mApplication} is same as the provided info.
6218      *
6219      * @hide
6220      */
6221     public boolean hasSameAppInfo(ApplicationInfo info) {
6222         return mApplication.packageName.equals(info.packageName) && mApplication.uid == info.uid;
6223     }
6224 
6225     /**
6226      * Parcelable.Creator that instantiates RemoteViews objects
6227      */
6228     public static final @android.annotation.NonNull Parcelable.Creator<RemoteViews> CREATOR = new Parcelable.Creator<RemoteViews>() {
6229         public RemoteViews createFromParcel(Parcel parcel) {
6230             return new RemoteViews(parcel);
6231         }
6232 
6233         public RemoteViews[] newArray(int size) {
6234             return new RemoteViews[size];
6235         }
6236     };
6237 
6238     /**
6239      * A representation of the view hierarchy. Only views which have a valid ID are added
6240      * and can be searched.
6241      */
6242     private static class ViewTree {
6243         private static final int INSERT_AT_END_INDEX = -1;
6244         private View mRoot;
6245         private ArrayList<ViewTree> mChildren;
6246 
6247         private ViewTree(View root) {
6248             mRoot = root;
6249         }
6250 
6251         public void createTree() {
6252             if (mChildren != null) {
6253                 return;
6254             }
6255 
6256             mChildren = new ArrayList<>();
6257             if (mRoot instanceof ViewGroup) {
6258                 ViewGroup vg = (ViewGroup) mRoot;
6259                 int count = vg.getChildCount();
6260                 for (int i = 0; i < count; i++) {
6261                     addViewChild(vg.getChildAt(i));
6262                 }
6263             }
6264         }
6265 
6266         @Nullable
6267         public ViewTree findViewTreeById(@IdRes int id) {
6268             if (mRoot.getId() == id) {
6269                 return this;
6270             }
6271             if (mChildren == null) {
6272                 return null;
6273             }
6274             for (ViewTree tree : mChildren) {
6275                 ViewTree result = tree.findViewTreeById(id);
6276                 if (result != null) {
6277                     return result;
6278                 }
6279             }
6280             return null;
6281         }
6282 
6283         @Nullable
6284         public ViewTree findViewTreeParentOf(ViewTree child) {
6285             if (mChildren == null) {
6286                 return null;
6287             }
6288             for (ViewTree tree : mChildren) {
6289                 if (tree == child) {
6290                     return this;
6291                 }
6292                 ViewTree result = tree.findViewTreeParentOf(child);
6293                 if (result != null) {
6294                     return result;
6295                 }
6296             }
6297             return null;
6298         }
6299 
6300         public void replaceView(View v) {
6301             mRoot = v;
6302             mChildren = null;
6303             createTree();
6304         }
6305 
6306         @Nullable
6307         public <T extends View> T findViewById(@IdRes int id) {
6308             if (mChildren == null) {
6309                 return mRoot.findViewById(id);
6310             }
6311             ViewTree tree = findViewTreeById(id);
6312             return tree == null ? null : (T) tree.mRoot;
6313         }
6314 
6315         public void addChild(ViewTree child) {
6316             addChild(child, INSERT_AT_END_INDEX);
6317         }
6318 
6319         /**
6320          * Adds the given {@link ViewTree} as a child at the given index.
6321          *
6322          * @param index The position at which to add the child or -1 to add last.
6323          */
6324         public void addChild(ViewTree child, int index) {
6325             if (mChildren == null) {
6326                 mChildren = new ArrayList<>();
6327             }
6328             child.createTree();
6329 
6330             if (index == INSERT_AT_END_INDEX) {
6331                 mChildren.add(child);
6332                 return;
6333             }
6334 
6335             mChildren.add(index, child);
6336         }
6337 
6338         public void removeChildren(int start, int count) {
6339             if (mChildren != null) {
6340                 for (int i = 0; i < count; i++) {
6341                     mChildren.remove(start);
6342                 }
6343             }
6344         }
6345 
6346         private void addViewChild(View v) {
6347             // ViewTree only contains Views which can be found using findViewById.
6348             // If isRootNamespace is true, this view is skipped.
6349             // @see ViewGroup#findViewTraversal(int)
6350             if (v.isRootNamespace()) {
6351                 return;
6352             }
6353             final ViewTree target;
6354 
6355             // If the view has a valid id, i.e., if can be found using findViewById, add it to the
6356             // tree, otherwise skip this view and add its children instead.
6357             if (v.getId() != 0) {
6358                 ViewTree tree = new ViewTree(v);
6359                 mChildren.add(tree);
6360                 target = tree;
6361             } else {
6362                 target = this;
6363             }
6364 
6365             if (v instanceof ViewGroup) {
6366                 if (target.mChildren == null) {
6367                     target.mChildren = new ArrayList<>();
6368                     ViewGroup vg = (ViewGroup) v;
6369                     int count = vg.getChildCount();
6370                     for (int i = 0; i < count; i++) {
6371                         target.addViewChild(vg.getChildAt(i));
6372                     }
6373                 }
6374             }
6375         }
6376 
6377         /** Find the first child for which the condition is true and return its index. */
6378         public int findChildIndex(Predicate<View> condition) {
6379             return findChildIndex(0, condition);
6380         }
6381 
6382         /**
6383          * Find the first child, starting at {@code startIndex}, for which the condition is true and
6384          * return its index.
6385          */
6386         public int findChildIndex(int startIndex, Predicate<View> condition) {
6387             if (mChildren == null) {
6388                 return -1;
6389             }
6390 
6391             for (int i = startIndex; i < mChildren.size(); i++) {
6392                 if (condition.test(mChildren.get(i).mRoot)) {
6393                     return i;
6394                 }
6395             }
6396             return -1;
6397         }
6398     }
6399 
6400     /**
6401      * Class representing a response to an action performed on any element of a RemoteViews.
6402      */
6403     public static class RemoteResponse {
6404 
6405         /** @hide **/
6406         @IntDef(prefix = "INTERACTION_TYPE_", value = {
6407                 INTERACTION_TYPE_CLICK,
6408                 INTERACTION_TYPE_CHECKED_CHANGE,
6409         })
6410         @Retention(RetentionPolicy.SOURCE)
6411         @interface InteractionType {}
6412         /** @hide */
6413         public static final int INTERACTION_TYPE_CLICK = 0;
6414         /** @hide */
6415         public static final int INTERACTION_TYPE_CHECKED_CHANGE = 1;
6416 
6417         private PendingIntent mPendingIntent;
6418         private Intent mFillIntent;
6419 
6420         private int mInteractionType = INTERACTION_TYPE_CLICK;
6421         private IntArray mViewIds;
6422         private ArrayList<String> mElementNames;
6423 
6424         /**
6425          * Creates a response which sends a pending intent as part of the response. The source
6426          * bounds ({@link Intent#getSourceBounds()}) of the intent will be set to the bounds of the
6427          * target view in screen space.
6428          * Note that any activity options associated with the mPendingIntent may get overridden
6429          * before starting the intent.
6430          *
6431          * @param pendingIntent The {@link PendingIntent} to send as part of the response
6432          */
6433         @NonNull
6434         public static RemoteResponse fromPendingIntent(@NonNull PendingIntent pendingIntent) {
6435             RemoteResponse response = new RemoteResponse();
6436             response.mPendingIntent = pendingIntent;
6437             return response;
6438         }
6439 
6440         /**
6441          * When using collections (eg. {@link ListView}, {@link StackView} etc.) in widgets, it is
6442          * very costly to set PendingIntents on the individual items, and is hence not recommended.
6443          * Instead a single PendingIntent template can be set on the collection, see {@link
6444          * RemoteViews#setPendingIntentTemplate(int, PendingIntent)}, and the individual on-click
6445          * action of a given item can be distinguished by setting a fillInIntent on that item. The
6446          * fillInIntent is then combined with the PendingIntent template in order to determine the
6447          * final intent which will be executed when the item is clicked. This works as follows: any
6448          * fields which are left blank in the PendingIntent template, but are provided by the
6449          * fillInIntent will be overwritten, and the resulting PendingIntent will be used. The rest
6450          * of the PendingIntent template will then be filled in with the associated fields that are
6451          * set in fillInIntent. See {@link Intent#fillIn(Intent, int)} for more details.
6452          * Creates a response which sends a pending intent as part of the response. The source
6453          * bounds ({@link Intent#getSourceBounds()}) of the intent will be set to the bounds of the
6454          * target view in screen space.
6455          * Note that any activity options associated with the mPendingIntent may get overridden
6456          * before starting the intent.
6457          *
6458          * @param fillIntent The intent which will be combined with the parent's PendingIntent in
6459          *                   order to determine the behavior of the response
6460          * @see RemoteViews#setPendingIntentTemplate(int, PendingIntent)
6461          * @see RemoteViews#setOnClickFillInIntent(int, Intent)
6462          */
6463         @NonNull
6464         public static RemoteResponse fromFillInIntent(@NonNull Intent fillIntent) {
6465             RemoteResponse response = new RemoteResponse();
6466             response.mFillIntent = fillIntent;
6467             return response;
6468         }
6469 
6470         /**
6471          * Adds a shared element to be transferred as part of the transition between Activities
6472          * using cross-Activity scene animations. The position of the first element will be used as
6473          * the epicenter for the exit Transition. The position of the associated shared element in
6474          * the launched Activity will be the epicenter of its entering Transition.
6475          *
6476          * @param viewId            The id of the view to be shared as part of the transition
6477          * @param sharedElementName The shared element name for this view
6478          * @see ActivityOptions#makeSceneTransitionAnimation(Activity, Pair[])
6479          */
6480         @NonNull
6481         public RemoteResponse addSharedElement(@IdRes int viewId,
6482                 @NonNull String sharedElementName) {
6483             if (mViewIds == null) {
6484                 mViewIds = new IntArray();
6485                 mElementNames = new ArrayList<>();
6486             }
6487             mViewIds.add(viewId);
6488             mElementNames.add(sharedElementName);
6489             return this;
6490         }
6491 
6492         /**
6493          * Sets the interaction type for which this RemoteResponse responds.
6494          *
6495          * @param type the type of interaction for which this is a response, such as clicking or
6496          *             checked state changing
6497          *
6498          * @hide
6499          */
6500         @NonNull
6501         public RemoteResponse setInteractionType(@InteractionType int type) {
6502             mInteractionType = type;
6503             return this;
6504         }
6505 
6506         private void writeToParcel(Parcel dest, int flags) {
6507             PendingIntent.writePendingIntentOrNullToParcel(mPendingIntent, dest);
6508             if (mPendingIntent == null) {
6509                 // Only write the intent if pending intent is null
6510                 dest.writeTypedObject(mFillIntent, flags);
6511             }
6512             dest.writeInt(mInteractionType);
6513             dest.writeIntArray(mViewIds == null ? null : mViewIds.toArray());
6514             dest.writeStringList(mElementNames);
6515         }
6516 
6517         private void readFromParcel(Parcel parcel) {
6518             mPendingIntent = PendingIntent.readPendingIntentOrNullFromParcel(parcel);
6519             if (mPendingIntent == null) {
6520                 mFillIntent = parcel.readTypedObject(Intent.CREATOR);
6521             }
6522             mInteractionType = parcel.readInt();
6523             int[] viewIds = parcel.createIntArray();
6524             mViewIds = viewIds == null ? null : IntArray.wrap(viewIds);
6525             mElementNames = parcel.createStringArrayList();
6526         }
6527 
6528         private void handleViewInteraction(
6529                 View v,
6530                 InteractionHandler handler) {
6531             final PendingIntent pi;
6532             if (mPendingIntent != null) {
6533                 pi = mPendingIntent;
6534             } else if (mFillIntent != null) {
6535                 AdapterView<?> ancestor = getAdapterViewAncestor(v);
6536                 if (ancestor == null) {
6537                     Log.e(LOG_TAG, "Collection item doesn't have AdapterView parent");
6538                     return;
6539                 }
6540 
6541                 // Ensure that a template pending intent has been set on the ancestor
6542                 if (!(ancestor.getTag() instanceof PendingIntent)) {
6543                     Log.e(LOG_TAG, "Attempting setOnClickFillInIntent or "
6544                             + "setOnCheckedChangeFillInIntent without calling "
6545                             + "setPendingIntentTemplate on parent.");
6546                     return;
6547                 }
6548 
6549                 pi = (PendingIntent) ancestor.getTag();
6550             } else {
6551                 Log.e(LOG_TAG, "Response has neither pendingIntent nor fillInIntent");
6552                 return;
6553             }
6554 
6555             handler.onInteraction(v, pi, this);
6556         }
6557 
6558         /**
6559          * Returns the closest ancestor of the view that is an AdapterView or null if none could be
6560          * found.
6561          */
6562         @Nullable
6563         private static AdapterView<?> getAdapterViewAncestor(@Nullable View view) {
6564             if (view == null) return null;
6565 
6566             View parent = (View) view.getParent();
6567             // Break the for loop on the first encounter of:
6568             //    1) an AdapterView,
6569             //    2) an AppWidgetHostView that is not a RemoteViewsFrameLayout, or
6570             //    3) a null parent.
6571             // 2) and 3) are unexpected and catch the case where a child is not
6572             // correctly parented in an AdapterView.
6573             while (parent != null && !(parent instanceof AdapterView<?>)
6574                     && !((parent instanceof AppWidgetHostView)
6575                     && !(parent instanceof RemoteViewsAdapter.RemoteViewsFrameLayout))) {
6576                 parent = (View) parent.getParent();
6577             }
6578 
6579             return parent instanceof AdapterView<?> ? (AdapterView<?>) parent : null;
6580         }
6581 
6582         /** @hide */
6583         public Pair<Intent, ActivityOptions> getLaunchOptions(View view) {
6584             Intent intent = mPendingIntent != null ? new Intent() : new Intent(mFillIntent);
6585             intent.setSourceBounds(getSourceBounds(view));
6586 
6587             if (view instanceof CompoundButton
6588                     && mInteractionType == INTERACTION_TYPE_CHECKED_CHANGE) {
6589                 intent.putExtra(EXTRA_CHECKED, ((CompoundButton) view).isChecked());
6590             }
6591 
6592             ActivityOptions opts = null;
6593 
6594             Context context = view.getContext();
6595             if (context.getResources().getBoolean(
6596                     com.android.internal.R.bool.config_overrideRemoteViewsActivityTransition)) {
6597                 TypedArray windowStyle = context.getTheme().obtainStyledAttributes(
6598                         com.android.internal.R.styleable.Window);
6599                 int windowAnimations = windowStyle.getResourceId(
6600                         com.android.internal.R.styleable.Window_windowAnimationStyle, 0);
6601                 TypedArray windowAnimationStyle = context.obtainStyledAttributes(
6602                         windowAnimations, com.android.internal.R.styleable.WindowAnimation);
6603                 int enterAnimationId = windowAnimationStyle.getResourceId(com.android.internal.R
6604                         .styleable.WindowAnimation_activityOpenRemoteViewsEnterAnimation, 0);
6605                 windowStyle.recycle();
6606                 windowAnimationStyle.recycle();
6607 
6608                 if (enterAnimationId != 0) {
6609                     opts = ActivityOptions.makeCustomAnimation(context,
6610                             enterAnimationId, 0);
6611                     opts.setPendingIntentLaunchFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
6612                 }
6613             }
6614 
6615             if (opts == null && mViewIds != null && mElementNames != null) {
6616                 View parent = (View) view.getParent();
6617                 while (parent != null && !(parent instanceof AppWidgetHostView)) {
6618                     parent = (View) parent.getParent();
6619                 }
6620                 if (parent instanceof AppWidgetHostView) {
6621                     opts = ((AppWidgetHostView) parent).createSharedElementActivityOptions(
6622                             mViewIds.toArray(),
6623                             mElementNames.toArray(new String[mElementNames.size()]), intent);
6624                 }
6625             }
6626 
6627             if (opts == null) {
6628                 opts = ActivityOptions.makeBasic();
6629                 opts.setPendingIntentLaunchFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
6630             }
6631             return Pair.create(intent, opts);
6632         }
6633     }
6634 
6635     /** @hide */
6636     public static boolean startPendingIntent(View view, PendingIntent pendingIntent,
6637             Pair<Intent, ActivityOptions> options) {
6638         try {
6639             // TODO: Unregister this handler if PendingIntent.FLAG_ONE_SHOT?
6640             Context context = view.getContext();
6641             // The NEW_TASK flags are applied through the activity options and not as a part of
6642             // the call to startIntentSender() to ensure that they are consistently applied to
6643             // both mutable and immutable PendingIntents.
6644             context.startIntentSender(
6645                     pendingIntent.getIntentSender(), options.first,
6646                     0, 0, 0, options.second.toBundle());
6647         } catch (IntentSender.SendIntentException e) {
6648             Log.e(LOG_TAG, "Cannot send pending intent: ", e);
6649             return false;
6650         } catch (Exception e) {
6651             Log.e(LOG_TAG, "Cannot send pending intent due to unknown exception: ", e);
6652             return false;
6653         }
6654         return true;
6655     }
6656 
6657     /** Representation of a fixed list of items to be displayed in a RemoteViews collection. */
6658     public static final class RemoteCollectionItems implements Parcelable {
6659         private final long[] mIds;
6660         private final RemoteViews[] mViews;
6661         private final boolean mHasStableIds;
6662         private final int mViewTypeCount;
6663 
6664         private HierarchyRootData mHierarchyRootData;
6665 
6666         RemoteCollectionItems(
6667                 long[] ids, RemoteViews[] views, boolean hasStableIds, int viewTypeCount) {
6668             mIds = ids;
6669             mViews = views;
6670             mHasStableIds = hasStableIds;
6671             mViewTypeCount = viewTypeCount;
6672             if (ids.length != views.length) {
6673                 throw new IllegalArgumentException(
6674                         "RemoteCollectionItems has different number of ids and views");
6675             }
6676             if (viewTypeCount < 1) {
6677                 throw new IllegalArgumentException("View type count must be >= 1");
6678             }
6679             int layoutIdCount = (int) Arrays.stream(views)
6680                     .mapToInt(RemoteViews::getLayoutId)
6681                     .distinct()
6682                     .count();
6683             if (layoutIdCount > viewTypeCount) {
6684                 throw new IllegalArgumentException(
6685                         "View type count is set to " + viewTypeCount + ", but the collection "
6686                                 + "contains " + layoutIdCount + " different layout ids");
6687             }
6688 
6689             // Until the collection items are attached to a parent, we configure the first item
6690             // to be the root of the others to share caches and save space during serialization.
6691             if (views.length > 0) {
6692                 setHierarchyRootData(views[0].getHierarchyRootData());
6693                 views[0].mIsRoot = true;
6694             }
6695         }
6696 
6697         RemoteCollectionItems(@NonNull Parcel in, @Nullable HierarchyRootData hierarchyRootData) {
6698             mHasStableIds = in.readBoolean();
6699             mViewTypeCount = in.readInt();
6700             int length = in.readInt();
6701             mIds = new long[length];
6702             in.readLongArray(mIds);
6703 
6704             boolean attached = in.readBoolean();
6705             mViews = new RemoteViews[length];
6706             int firstChildIndex;
6707             if (attached) {
6708                 if (hierarchyRootData == null) {
6709                     throw new IllegalStateException("Cannot unparcel a RemoteCollectionItems that "
6710                             + "was parceled as attached without providing data for a root "
6711                             + "RemoteViews");
6712                 }
6713                 mHierarchyRootData = hierarchyRootData;
6714                 firstChildIndex = 0;
6715             } else {
6716                 mViews[0] = new RemoteViews(in);
6717                 mHierarchyRootData = mViews[0].getHierarchyRootData();
6718                 firstChildIndex = 1;
6719             }
6720 
6721             for (int i = firstChildIndex; i < length; i++) {
6722                 mViews[i] = new RemoteViews(
6723                         in,
6724                         mHierarchyRootData,
6725                         /* info= */ null,
6726                         /* depth= */ 0);
6727             }
6728         }
6729 
6730         void setHierarchyRootData(@NonNull HierarchyRootData rootData) {
6731             mHierarchyRootData = rootData;
6732             for (RemoteViews view : mViews) {
6733                 view.configureAsChild(rootData);
6734             }
6735         }
6736 
6737         @Override
6738         public int describeContents() {
6739             return 0;
6740         }
6741 
6742         @Override
6743         public void writeToParcel(@NonNull Parcel dest, int flags) {
6744             writeToParcel(dest, flags, /* attached= */ false);
6745         }
6746 
6747         private void writeToParcel(@NonNull Parcel dest, int flags, boolean attached) {
6748             boolean prevAllowSquashing = dest.allowSquashing();
6749 
6750             dest.writeBoolean(mHasStableIds);
6751             dest.writeInt(mViewTypeCount);
6752             dest.writeInt(mIds.length);
6753             dest.writeLongArray(mIds);
6754 
6755             if (attached && mHierarchyRootData == null) {
6756                 throw new IllegalStateException("Cannot call writeToParcelAttached for a "
6757                         + "RemoteCollectionItems without first calling setHierarchyRootData()");
6758             }
6759 
6760             // Write whether we parceled as attached or not. This allows cleaner validation and
6761             // proper error messaging when unparceling later.
6762             dest.writeBoolean(attached);
6763             boolean restoreRoot = false;
6764             if (!attached && mViews.length > 0 && !mViews[0].mIsRoot) {
6765                 // If we're writing unattached, temporarily set the first item as the root so that
6766                 // the bitmap cache is written to the parcel.
6767                 restoreRoot = true;
6768                 mViews[0].mIsRoot = true;
6769             }
6770 
6771             for (RemoteViews view : mViews) {
6772                 view.writeToParcel(dest, flags);
6773             }
6774 
6775             if (restoreRoot) mViews[0].mIsRoot = false;
6776             dest.restoreAllowSquashing(prevAllowSquashing);
6777         }
6778 
6779         /**
6780          * Returns the id for {@code position}. See {@link #hasStableIds()} for whether this id
6781          * should be considered meaningful across collection updates.
6782          *
6783          * @return Id for the position.
6784          */
6785         public long getItemId(int position) {
6786             return mIds[position];
6787         }
6788 
6789         /**
6790          * Returns the {@link RemoteViews} to display at {@code position}.
6791          *
6792          * @return RemoteViews for the position.
6793          */
6794         @NonNull
6795         public RemoteViews getItemView(int position) {
6796             return mViews[position];
6797         }
6798 
6799         /**
6800          * Returns the number of elements in the collection.
6801          *
6802          * @return Count of items.
6803          */
6804         public int getItemCount() {
6805             return mIds.length;
6806         }
6807 
6808         /**
6809          * Returns the view type count for the collection when used in an adapter
6810          *
6811          * @return Count of view types for the collection when used in an adapter.
6812          * @see android.widget.Adapter#getViewTypeCount()
6813          */
6814         public int getViewTypeCount() {
6815             return mViewTypeCount;
6816         }
6817 
6818         /**
6819          * Indicates whether the item ids are stable across changes to the underlying data.
6820          *
6821          * @return True if the same id always refers to the same object.
6822          * @see android.widget.Adapter#hasStableIds()
6823          */
6824         public boolean hasStableIds() {
6825             return mHasStableIds;
6826         }
6827 
6828         @NonNull
6829         public static final Creator<RemoteCollectionItems> CREATOR =
6830                 new Creator<RemoteCollectionItems>() {
6831             @NonNull
6832             @Override
6833             public RemoteCollectionItems createFromParcel(@NonNull Parcel source) {
6834                 return new RemoteCollectionItems(source, /* hierarchyRoot= */ null);
6835             }
6836 
6837             @NonNull
6838             @Override
6839             public RemoteCollectionItems[] newArray(int size) {
6840                 return new RemoteCollectionItems[size];
6841             }
6842         };
6843 
6844         /** Builder class for {@link RemoteCollectionItems} objects.*/
6845         public static final class Builder {
6846             private final LongArray mIds = new LongArray();
6847             private final List<RemoteViews> mViews = new ArrayList<>();
6848             private boolean mHasStableIds;
6849             private int mViewTypeCount;
6850 
6851             /**
6852              * Adds a {@link RemoteViews} to the collection.
6853              *
6854              * @param id Id to associate with the row. Use {@link #setHasStableIds(boolean)} to
6855              *           indicate that ids are stable across changes to the collection.
6856              * @param view RemoteViews to display for the row.
6857              */
6858             @NonNull
6859             // Covered by getItemId, getItemView, getItemCount.
6860             @SuppressLint("MissingGetterMatchingBuilder")
6861             public Builder addItem(long id, @NonNull RemoteViews view) {
6862                 if (view == null) throw new NullPointerException();
6863                 if (view.hasMultipleLayouts()) {
6864                     throw new IllegalArgumentException(
6865                             "RemoteViews used in a RemoteCollectionItems cannot specify separate "
6866                                     + "layouts for orientations or sizes.");
6867                 }
6868                 mIds.add(id);
6869                 mViews.add(view);
6870                 return this;
6871             }
6872 
6873             /**
6874              * Sets whether the item ids are stable across changes to the underlying data.
6875              *
6876              * @see android.widget.Adapter#hasStableIds()
6877              */
6878             @NonNull
6879             public Builder setHasStableIds(boolean hasStableIds) {
6880                 mHasStableIds = hasStableIds;
6881                 return this;
6882             }
6883 
6884             /**
6885              * Sets the view type count for the collection when used in an adapter. This can be set
6886              * to the maximum number of different layout ids that will be used by RemoteViews in
6887              * this collection.
6888              *
6889              * If this value is not set, then a value will be inferred from the provided items. As
6890              * a result, the adapter may need to be recreated when the list is updated with
6891              * previously unseen RemoteViews layouts for new items.
6892              *
6893              * @see android.widget.Adapter#getViewTypeCount()
6894              */
6895             @NonNull
6896             public Builder setViewTypeCount(int viewTypeCount) {
6897                 mViewTypeCount = viewTypeCount;
6898                 return this;
6899             }
6900 
6901             /** Creates the {@link RemoteCollectionItems} defined by this builder. */
6902             @NonNull
6903             public RemoteCollectionItems build() {
6904                 if (mViewTypeCount < 1) {
6905                     // If a view type count wasn't specified, set it to be the number of distinct
6906                     // layout ids used in the items.
6907                     mViewTypeCount = (int) mViews.stream()
6908                             .mapToInt(RemoteViews::getLayoutId)
6909                             .distinct()
6910                             .count();
6911                 }
6912                 return new RemoteCollectionItems(
6913                         mIds.toArray(),
6914                         mViews.toArray(new RemoteViews[0]),
6915                         mHasStableIds,
6916                         Math.max(mViewTypeCount, 1));
6917             }
6918         }
6919     }
6920 
6921     /**
6922      * Get the ID of the top-level view of the XML layout, if set using
6923      * {@link RemoteViews#RemoteViews(String, int, int)}.
6924      */
6925     public @IdRes int getViewId() {
6926         return mViewId;
6927     }
6928 
6929     /**
6930      * Set the provider instance ID.
6931      *
6932      * This should only be used by {@link com.android.server.appwidget.AppWidgetService}.
6933      * @hide
6934      */
6935     public void setProviderInstanceId(long id) {
6936         mProviderInstanceId = id;
6937     }
6938 
6939     /**
6940      * Get the provider instance id.
6941      *
6942      * This should uniquely identifies {@link RemoteViews} coming from a given App Widget
6943      * Provider. This changes each time the App Widget provider update the {@link RemoteViews} of
6944      * its widget. Returns -1 if the {@link RemoteViews} doesn't come from an App Widget provider.
6945      * @hide
6946      */
6947     public long getProviderInstanceId() {
6948         return mProviderInstanceId;
6949     }
6950 
6951     /**
6952      * Identify the child of this {@link RemoteViews}, or 0 if this is not a child.
6953      *
6954      * The returned value is always a small integer, currently between 0 and 17.
6955      */
6956     private int getChildId(@NonNull RemoteViews child) {
6957         if (child == this) {
6958             return 0;
6959         }
6960         if (hasSizedRemoteViews()) {
6961             for (int i = 0; i < mSizedRemoteViews.size(); i++) {
6962                 if (mSizedRemoteViews.get(i) == child) {
6963                     return i + 1;
6964                 }
6965             }
6966         }
6967         if (hasLandscapeAndPortraitLayouts()) {
6968             if (mLandscape == child) {
6969                 return 1;
6970             } else if (mPortrait == child) {
6971                 return 2;
6972             }
6973         }
6974         // This is not a child of this RemoteViews.
6975         return 0;
6976     }
6977 
6978     /**
6979      * Identify uniquely this RemoteViews, or returns -1 if not possible.
6980      *
6981      * @param parent If the {@link RemoteViews} is not a root {@link RemoteViews}, this should be
6982      *              the parent that contains it.
6983      *
6984      * @hide
6985      */
6986     public long computeUniqueId(@Nullable RemoteViews parent) {
6987         if (mIsRoot) {
6988             long viewId = getProviderInstanceId();
6989             if (viewId != -1) {
6990                 viewId <<= 8;
6991             }
6992             return viewId;
6993         }
6994         if (parent == null) {
6995             return -1;
6996         }
6997         long viewId = parent.getProviderInstanceId();
6998         if (viewId == -1) {
6999             return -1;
7000         }
7001         int childId = parent.getChildId(this);
7002         if (childId == -1) {
7003             return -1;
7004         }
7005         viewId <<= 8;
7006         viewId |= childId;
7007         return viewId;
7008     }
7009 
7010     @Nullable
7011     private static Pair<String, Integer> getPackageUserKey(@Nullable ApplicationInfo info) {
7012         if (info == null || info.packageName ==  null) return null;
7013         return Pair.create(info.packageName, info.uid);
7014     }
7015 
7016     private HierarchyRootData getHierarchyRootData() {
7017         return new HierarchyRootData(mBitmapCache, mApplicationInfoCache, mClassCookies);
7018     }
7019 
7020     private static final class HierarchyRootData {
7021         final BitmapCache mBitmapCache;
7022         final ApplicationInfoCache mApplicationInfoCache;
7023         final Map<Class, Object> mClassCookies;
7024 
7025         HierarchyRootData(
7026                 BitmapCache bitmapCache,
7027                 ApplicationInfoCache applicationInfoCache,
7028                 Map<Class, Object> classCookies) {
7029             mBitmapCache = bitmapCache;
7030             mApplicationInfoCache = applicationInfoCache;
7031             mClassCookies = classCookies;
7032         }
7033     }
7034 }
7035