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