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.ColorInt;
20 import android.annotation.DimenRes;
21 import android.annotation.IntDef;
22 import android.annotation.LayoutRes;
23 import android.annotation.NonNull;
24 import android.annotation.StyleRes;
25 import android.app.Activity;
26 import android.app.ActivityOptions;
27 import android.app.ActivityThread;
28 import android.app.Application;
29 import android.app.PendingIntent;
30 import android.app.RemoteInput;
31 import android.appwidget.AppWidgetHostView;
32 import android.compat.annotation.UnsupportedAppUsage;
33 import android.content.Context;
34 import android.content.ContextWrapper;
35 import android.content.Intent;
36 import android.content.IntentSender;
37 import android.content.pm.ApplicationInfo;
38 import android.content.pm.PackageManager.NameNotFoundException;
39 import android.content.res.ColorStateList;
40 import android.content.res.Configuration;
41 import android.content.res.Resources;
42 import android.content.res.TypedArray;
43 import android.graphics.Bitmap;
44 import android.graphics.PorterDuff;
45 import android.graphics.Rect;
46 import android.graphics.drawable.Drawable;
47 import android.graphics.drawable.Icon;
48 import android.graphics.drawable.RippleDrawable;
49 import android.net.Uri;
50 import android.os.AsyncTask;
51 import android.os.Binder;
52 import android.os.Build;
53 import android.os.Bundle;
54 import android.os.CancellationSignal;
55 import android.os.Parcel;
56 import android.os.Parcelable;
57 import android.os.Process;
58 import android.os.StrictMode;
59 import android.os.UserHandle;
60 import android.text.TextUtils;
61 import android.util.ArrayMap;
62 import android.util.IntArray;
63 import android.util.Log;
64 import android.util.Pair;
65 import android.view.ContextThemeWrapper;
66 import android.view.LayoutInflater;
67 import android.view.LayoutInflater.Filter;
68 import android.view.RemotableViewMethod;
69 import android.view.View;
70 import android.view.ViewGroup;
71 import android.view.ViewStub;
72 import android.widget.AdapterView.OnItemClickListener;
73 
74 import com.android.internal.R;
75 import com.android.internal.util.ContrastColorUtil;
76 import com.android.internal.util.Preconditions;
77 
78 import java.lang.annotation.ElementType;
79 import java.lang.annotation.Retention;
80 import java.lang.annotation.RetentionPolicy;
81 import java.lang.annotation.Target;
82 import java.lang.invoke.MethodHandle;
83 import java.lang.invoke.MethodHandles;
84 import java.lang.invoke.MethodType;
85 import java.lang.reflect.Method;
86 import java.util.ArrayList;
87 import java.util.HashMap;
88 import java.util.Map;
89 import java.util.Objects;
90 import java.util.Stack;
91 import java.util.concurrent.Executor;
92 import java.util.function.Consumer;
93 
94 /**
95  * A class that describes a view hierarchy that can be displayed in
96  * another process. The hierarchy is inflated from a layout resource
97  * file, and this class provides some basic operations for modifying
98  * the content of the inflated hierarchy.
99  *
100  * <p>{@code RemoteViews} is limited to support for the following layouts:</p>
101  * <ul>
102  *   <li>{@link android.widget.AdapterViewFlipper}</li>
103  *   <li>{@link android.widget.FrameLayout}</li>
104  *   <li>{@link android.widget.GridLayout}</li>
105  *   <li>{@link android.widget.GridView}</li>
106  *   <li>{@link android.widget.LinearLayout}</li>
107  *   <li>{@link android.widget.ListView}</li>
108  *   <li>{@link android.widget.RelativeLayout}</li>
109  *   <li>{@link android.widget.StackView}</li>
110  *   <li>{@link android.widget.ViewFlipper}</li>
111  * </ul>
112  * <p>And the following widgets:</p>
113  * <ul>
114  *   <li>{@link android.widget.AnalogClock}</li>
115  *   <li>{@link android.widget.Button}</li>
116  *   <li>{@link android.widget.Chronometer}</li>
117  *   <li>{@link android.widget.ImageButton}</li>
118  *   <li>{@link android.widget.ImageView}</li>
119  *   <li>{@link android.widget.ProgressBar}</li>
120  *   <li>{@link android.widget.TextClock}</li>
121  *   <li>{@link android.widget.TextView}</li>
122  * </ul>
123  * <p>Descendants of these classes are not supported.</p>
124  */
125 public class RemoteViews implements Parcelable, Filter {
126 
127     private static final String LOG_TAG = "RemoteViews";
128 
129     /**
130      * The intent extra that contains the appWidgetId.
131      * @hide
132      */
133     static final String EXTRA_REMOTEADAPTER_APPWIDGET_ID = "remoteAdapterAppWidgetId";
134 
135     /**
136      * The intent extra that contains {@code true} if inflating as dak text theme.
137      * @hide
138      */
139     static final String EXTRA_REMOTEADAPTER_ON_LIGHT_BACKGROUND = "remoteAdapterOnLightBackground";
140 
141     /**
142      * The intent extra that contains the bounds for all shared elements.
143      */
144     public static final String EXTRA_SHARED_ELEMENT_BOUNDS =
145             "android.widget.extra.SHARED_ELEMENT_BOUNDS";
146 
147     /**
148      * Maximum depth of nested views calls from {@link #addView(int, RemoteViews)} and
149      * {@link #RemoteViews(RemoteViews, RemoteViews)}.
150      */
151     private static final int MAX_NESTED_VIEWS = 10;
152 
153     // The unique identifiers for each custom {@link Action}.
154     private static final int SET_ON_CLICK_RESPONSE_TAG = 1;
155     private static final int REFLECTION_ACTION_TAG = 2;
156     private static final int SET_DRAWABLE_TINT_TAG = 3;
157     private static final int VIEW_GROUP_ACTION_ADD_TAG = 4;
158     private static final int VIEW_CONTENT_NAVIGATION_TAG = 5;
159     private static final int SET_EMPTY_VIEW_ACTION_TAG = 6;
160     private static final int VIEW_GROUP_ACTION_REMOVE_TAG = 7;
161     private static final int SET_PENDING_INTENT_TEMPLATE_TAG = 8;
162     private static final int SET_REMOTE_VIEW_ADAPTER_INTENT_TAG = 10;
163     private static final int TEXT_VIEW_DRAWABLE_ACTION_TAG = 11;
164     private static final int BITMAP_REFLECTION_ACTION_TAG = 12;
165     private static final int TEXT_VIEW_SIZE_ACTION_TAG = 13;
166     private static final int VIEW_PADDING_ACTION_TAG = 14;
167     private static final int SET_REMOTE_VIEW_ADAPTER_LIST_TAG = 15;
168     private static final int SET_REMOTE_INPUTS_ACTION_TAG = 18;
169     private static final int LAYOUT_PARAM_ACTION_TAG = 19;
170     private static final int OVERRIDE_TEXT_COLORS_TAG = 20;
171     private static final int SET_RIPPLE_DRAWABLE_COLOR_TAG = 21;
172     private static final int SET_INT_TAG_TAG = 22;
173 
174     /** @hide **/
175     @IntDef(flag = true, value = {
176             FLAG_REAPPLY_DISALLOWED,
177             FLAG_WIDGET_IS_COLLECTION_CHILD,
178             FLAG_USE_LIGHT_BACKGROUND_LAYOUT
179     })
180     @Retention(RetentionPolicy.SOURCE)
181     public @interface ApplyFlags {}
182     /**
183      * Whether reapply is disallowed on this remoteview. This maybe be true if some actions modify
184      * the layout in a way that isn't recoverable, since views are being removed.
185      * @hide
186      */
187     public static final int FLAG_REAPPLY_DISALLOWED = 1;
188     /**
189      * This flag indicates whether this RemoteViews object is being created from a
190      * RemoteViewsService for use as a child of a widget collection. This flag is used
191      * to determine whether or not certain features are available, in particular,
192      * setting on click extras and setting on click pending intents. The former is enabled,
193      * and the latter disabled when this flag is true.
194      * @hide
195      */
196     public static final int FLAG_WIDGET_IS_COLLECTION_CHILD = 2;
197     /**
198      * When this flag is set, the views is inflated with {@link #mLightBackgroundLayoutId} instead
199      * of {link #mLayoutId}
200      * @hide
201      */
202     public static final int FLAG_USE_LIGHT_BACKGROUND_LAYOUT = 4;
203 
204     /**
205      * Used to restrict the views which can be inflated
206      *
207      * @see android.view.LayoutInflater.Filter#onLoadClass(java.lang.Class)
208      */
209     private static final LayoutInflater.Filter INFLATER_FILTER =
210             (clazz) -> clazz.isAnnotationPresent(RemoteViews.RemoteView.class);
211 
212     /**
213      * Application that hosts the remote views.
214      *
215      * @hide
216      */
217     @UnsupportedAppUsage
218     public ApplicationInfo mApplication;
219 
220     /**
221      * The resource ID of the layout file. (Added to the parcel)
222      */
223     @UnsupportedAppUsage
224     private final int mLayoutId;
225 
226     /**
227      * The resource ID of the layout file in dark text mode. (Added to the parcel)
228      */
229     private int mLightBackgroundLayoutId = 0;
230 
231     /**
232      * An array of actions to perform on the view tree once it has been
233      * inflated
234      */
235     @UnsupportedAppUsage
236     private ArrayList<Action> mActions;
237 
238     /**
239      * Maps bitmaps to unique indicies to avoid Bitmap duplication.
240      */
241     @UnsupportedAppUsage
242     private BitmapCache mBitmapCache;
243 
244     /**
245      * Indicates whether or not this RemoteViews object is contained as a child of any other
246      * RemoteViews.
247      */
248     private boolean mIsRoot = true;
249 
250     /**
251      * Constants to whether or not this RemoteViews is composed of a landscape and portrait
252      * RemoteViews.
253      */
254     private static final int MODE_NORMAL = 0;
255     private static final int MODE_HAS_LANDSCAPE_AND_PORTRAIT = 1;
256 
257     /**
258      * Used in conjunction with the special constructor
259      * {@link #RemoteViews(RemoteViews, RemoteViews)} to keep track of the landscape and portrait
260      * RemoteViews.
261      */
262     private RemoteViews mLandscape = null;
263     @UnsupportedAppUsage
264     private RemoteViews mPortrait = null;
265 
266     @ApplyFlags
267     private int mApplyFlags = 0;
268 
269     /** Class cookies of the Parcel this instance was read from. */
270     private final Map<Class, Object> mClassCookies;
271 
272     private static final OnClickHandler DEFAULT_ON_CLICK_HANDLER = (view, pendingIntent, response)
273             -> startPendingIntent(view, pendingIntent, response.getLaunchOptions(view));
274 
275     private static final ArrayMap<MethodKey, MethodArgs> sMethods = new ArrayMap<>();
276 
277     /**
278      * This key is used to perform lookups in sMethods without causing allocations.
279      */
280     private static final MethodKey sLookupKey = new MethodKey();
281 
282     /**
283      * @hide
284      */
setRemoteInputs(int viewId, RemoteInput[] remoteInputs)285     public void setRemoteInputs(int viewId, RemoteInput[] remoteInputs) {
286         mActions.add(new SetRemoteInputsAction(viewId, remoteInputs));
287     }
288 
289     /**
290      * Reduces all images and ensures that they are all below the given sizes.
291      *
292      * @param maxWidth the maximum width allowed
293      * @param maxHeight the maximum height allowed
294      *
295      * @hide
296      */
reduceImageSizes(int maxWidth, int maxHeight)297     public void reduceImageSizes(int maxWidth, int maxHeight) {
298         ArrayList<Bitmap> cache = mBitmapCache.mBitmaps;
299         for (int i = 0; i < cache.size(); i++) {
300             Bitmap bitmap = cache.get(i);
301             cache.set(i, Icon.scaleDownIfNecessary(bitmap, maxWidth, maxHeight));
302         }
303     }
304 
305     /**
306      * Override all text colors in this layout and replace them by the given text color.
307      *
308      * @param textColor The color to use.
309      *
310      * @hide
311      */
overrideTextColors(int textColor)312     public void overrideTextColors(int textColor) {
313         addAction(new OverrideTextColorsAction(textColor));
314     }
315 
316     /**
317      * Sets an integer tag to the view.
318      *
319      * @hide
320      */
setIntTag(int viewId, int key, int tag)321     public void setIntTag(int viewId, int key, int tag) {
322         addAction(new SetIntTagAction(viewId, key, tag));
323     }
324 
325     /**
326      * Set that it is disallowed to reapply another remoteview with the same layout as this view.
327      * This should be done if an action is destroying the view tree of the base layout.
328      *
329      * @hide
330      */
addFlags(@pplyFlags int flags)331     public void addFlags(@ApplyFlags int flags) {
332         mApplyFlags = mApplyFlags | flags;
333     }
334 
335     /**
336      * @hide
337      */
hasFlags(@pplyFlags int flag)338     public boolean hasFlags(@ApplyFlags int flag) {
339         return (mApplyFlags & flag) == flag;
340     }
341 
342     /**
343      * Stores information related to reflection method lookup.
344      */
345     static class MethodKey {
346         public Class targetClass;
347         public Class paramClass;
348         public String methodName;
349 
350         @Override
equals(Object o)351         public boolean equals(Object o) {
352             if (!(o instanceof MethodKey)) {
353                 return false;
354             }
355             MethodKey p = (MethodKey) o;
356             return Objects.equals(p.targetClass, targetClass)
357                     && Objects.equals(p.paramClass, paramClass)
358                     && Objects.equals(p.methodName, methodName);
359         }
360 
361         @Override
hashCode()362         public int hashCode() {
363             return Objects.hashCode(targetClass) ^ Objects.hashCode(paramClass)
364                     ^ Objects.hashCode(methodName);
365         }
366 
set(Class targetClass, Class paramClass, String methodName)367         public void set(Class targetClass, Class paramClass, String methodName) {
368             this.targetClass = targetClass;
369             this.paramClass = paramClass;
370             this.methodName = methodName;
371         }
372     }
373 
374 
375     /**
376      * Stores information related to reflection method lookup result.
377      */
378     static class MethodArgs {
379         public MethodHandle syncMethod;
380         public MethodHandle asyncMethod;
381         public String asyncMethodName;
382     }
383 
384     /**
385      * This annotation indicates that a subclass of View is allowed to be used
386      * with the {@link RemoteViews} mechanism.
387      */
388     @Target({ ElementType.TYPE })
389     @Retention(RetentionPolicy.RUNTIME)
390     public @interface RemoteView {
391     }
392 
393     /**
394      * Exception to send when something goes wrong executing an action
395      *
396      */
397     public static class ActionException extends RuntimeException {
ActionException(Exception ex)398         public ActionException(Exception ex) {
399             super(ex);
400         }
ActionException(String message)401         public ActionException(String message) {
402             super(message);
403         }
404         /**
405          * @hide
406          */
ActionException(Throwable t)407         public ActionException(Throwable t) {
408             super(t);
409         }
410     }
411 
412     /** @hide */
413     public interface OnClickHandler {
414 
415         /** @hide */
onClickHandler(View view, PendingIntent pendingIntent, RemoteResponse response)416         boolean onClickHandler(View view, PendingIntent pendingIntent, RemoteResponse response);
417     }
418 
419     /**
420      * Base class for all actions that can be performed on an
421      * inflated view.
422      *
423      *  SUBCLASSES MUST BE IMMUTABLE SO CLONE WORKS!!!!!
424      */
425     private abstract static class Action implements Parcelable {
apply(View root, ViewGroup rootParent, OnClickHandler handler)426         public abstract void apply(View root, ViewGroup rootParent,
427                 OnClickHandler handler) throws ActionException;
428 
429         public static final int MERGE_REPLACE = 0;
430         public static final int MERGE_APPEND = 1;
431         public static final int MERGE_IGNORE = 2;
432 
describeContents()433         public int describeContents() {
434             return 0;
435         }
436 
setBitmapCache(BitmapCache bitmapCache)437         public void setBitmapCache(BitmapCache bitmapCache) {
438             // Do nothing
439         }
440 
441         @UnsupportedAppUsage
mergeBehavior()442         public int mergeBehavior() {
443             return MERGE_REPLACE;
444         }
445 
getActionTag()446         public abstract int getActionTag();
447 
getUniqueKey()448         public String getUniqueKey() {
449             return (getActionTag() + "_" + viewId);
450         }
451 
452         /**
453          * This is called on the background thread. It should perform any non-ui computations
454          * and return the final action which will run on the UI thread.
455          * Override this if some of the tasks can be performed async.
456          */
initActionAsync(ViewTree root, ViewGroup rootParent, OnClickHandler handler)457         public Action initActionAsync(ViewTree root, ViewGroup rootParent, OnClickHandler handler) {
458             return this;
459         }
460 
prefersAsyncApply()461         public boolean prefersAsyncApply() {
462             return false;
463         }
464 
465         /**
466          * Overridden by subclasses which have (or inherit) an ApplicationInfo instance
467          * as member variable
468          */
hasSameAppInfo(ApplicationInfo parentInfo)469         public boolean hasSameAppInfo(ApplicationInfo parentInfo) {
470             return true;
471         }
472 
visitUris(@onNull Consumer<Uri> visitor)473         public void visitUris(@NonNull Consumer<Uri> visitor) {
474             // Nothing to visit by default
475         }
476 
477         @UnsupportedAppUsage
478         int viewId;
479     }
480 
481     /**
482      * Action class used during async inflation of RemoteViews. Subclasses are not parcelable.
483      */
484     private static abstract class RuntimeAction extends Action {
485         @Override
getActionTag()486         public final int getActionTag() {
487             return 0;
488         }
489 
490         @Override
writeToParcel(Parcel dest, int flags)491         public final void writeToParcel(Parcel dest, int flags) {
492             throw new UnsupportedOperationException();
493         }
494     }
495 
496     // Constant used during async execution. It is not parcelable.
497     private static final Action ACTION_NOOP = new RuntimeAction() {
498         @Override
499         public void apply(View root, ViewGroup rootParent, OnClickHandler handler) { }
500     };
501 
502     /**
503      * Merges the passed RemoteViews actions with this RemoteViews actions according to
504      * action-specific merge rules.
505      *
506      * @param newRv
507      *
508      * @hide
509      */
510     @UnsupportedAppUsage
mergeRemoteViews(RemoteViews newRv)511     public void mergeRemoteViews(RemoteViews newRv) {
512         if (newRv == null) return;
513         // We first copy the new RemoteViews, as the process of merging modifies the way the actions
514         // reference the bitmap cache. We don't want to modify the object as it may need to
515         // be merged and applied multiple times.
516         RemoteViews copy = new RemoteViews(newRv);
517 
518         HashMap<String, Action> map = new HashMap<String, Action>();
519         if (mActions == null) {
520             mActions = new ArrayList<Action>();
521         }
522 
523         int count = mActions.size();
524         for (int i = 0; i < count; i++) {
525             Action a = mActions.get(i);
526             map.put(a.getUniqueKey(), a);
527         }
528 
529         ArrayList<Action> newActions = copy.mActions;
530         if (newActions == null) return;
531         count = newActions.size();
532         for (int i = 0; i < count; i++) {
533             Action a = newActions.get(i);
534             String key = newActions.get(i).getUniqueKey();
535             int mergeBehavior = newActions.get(i).mergeBehavior();
536             if (map.containsKey(key) && mergeBehavior == Action.MERGE_REPLACE) {
537                 mActions.remove(map.get(key));
538                 map.remove(key);
539             }
540 
541             // If the merge behavior is ignore, we don't bother keeping the extra action
542             if (mergeBehavior == Action.MERGE_REPLACE || mergeBehavior == Action.MERGE_APPEND) {
543                 mActions.add(a);
544             }
545         }
546 
547         // Because pruning can remove the need for bitmaps, we reconstruct the bitmap cache
548         mBitmapCache = new BitmapCache();
549         setBitmapCache(mBitmapCache);
550     }
551 
552     /**
553      * Note all {@link Uri} that are referenced internally, with the expectation
554      * that Uri permission grants will need to be issued to ensure the recipient
555      * of this object is able to render its contents.
556      *
557      * @hide
558      */
visitUris(@onNull Consumer<Uri> visitor)559     public void visitUris(@NonNull Consumer<Uri> visitor) {
560         if (mActions != null) {
561             for (int i = 0; i < mActions.size(); i++) {
562                 mActions.get(i).visitUris(visitor);
563             }
564         }
565     }
566 
visitIconUri(Icon icon, @NonNull Consumer<Uri> visitor)567     private static void visitIconUri(Icon icon, @NonNull Consumer<Uri> visitor) {
568         if (icon != null && (icon.getType() == Icon.TYPE_URI
569                 || icon.getType() == Icon.TYPE_URI_ADAPTIVE_BITMAP)) {
570             visitor.accept(icon.getUri());
571         }
572     }
573 
574     private static class RemoteViewsContextWrapper extends ContextWrapper {
575         private final Context mContextForResources;
576 
RemoteViewsContextWrapper(Context context, Context contextForResources)577         RemoteViewsContextWrapper(Context context, Context contextForResources) {
578             super(context);
579             mContextForResources = contextForResources;
580         }
581 
582         @Override
getResources()583         public Resources getResources() {
584             return mContextForResources.getResources();
585         }
586 
587         @Override
getTheme()588         public Resources.Theme getTheme() {
589             return mContextForResources.getTheme();
590         }
591 
592         @Override
getPackageName()593         public String getPackageName() {
594             return mContextForResources.getPackageName();
595         }
596     }
597 
598     private class SetEmptyView extends Action {
599         int emptyViewId;
600 
SetEmptyView(int viewId, int emptyViewId)601         SetEmptyView(int viewId, int emptyViewId) {
602             this.viewId = viewId;
603             this.emptyViewId = emptyViewId;
604         }
605 
SetEmptyView(Parcel in)606         SetEmptyView(Parcel in) {
607             this.viewId = in.readInt();
608             this.emptyViewId = in.readInt();
609         }
610 
writeToParcel(Parcel out, int flags)611         public void writeToParcel(Parcel out, int flags) {
612             out.writeInt(this.viewId);
613             out.writeInt(this.emptyViewId);
614         }
615 
616         @Override
apply(View root, ViewGroup rootParent, OnClickHandler handler)617         public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
618             final View view = root.findViewById(viewId);
619             if (!(view instanceof AdapterView<?>)) return;
620 
621             AdapterView<?> adapterView = (AdapterView<?>) view;
622 
623             final View emptyView = root.findViewById(emptyViewId);
624             if (emptyView == null) return;
625 
626             adapterView.setEmptyView(emptyView);
627         }
628 
629         @Override
getActionTag()630         public int getActionTag() {
631             return SET_EMPTY_VIEW_ACTION_TAG;
632         }
633     }
634 
635     private class SetPendingIntentTemplate extends Action {
SetPendingIntentTemplate(int id, PendingIntent pendingIntentTemplate)636         public SetPendingIntentTemplate(int id, PendingIntent pendingIntentTemplate) {
637             this.viewId = id;
638             this.pendingIntentTemplate = pendingIntentTemplate;
639         }
640 
SetPendingIntentTemplate(Parcel parcel)641         public SetPendingIntentTemplate(Parcel parcel) {
642             viewId = parcel.readInt();
643             pendingIntentTemplate = PendingIntent.readPendingIntentOrNullFromParcel(parcel);
644         }
645 
writeToParcel(Parcel dest, int flags)646         public void writeToParcel(Parcel dest, int flags) {
647             dest.writeInt(viewId);
648             PendingIntent.writePendingIntentOrNullToParcel(pendingIntentTemplate, dest);
649         }
650 
651         @Override
apply(View root, ViewGroup rootParent, final OnClickHandler handler)652         public void apply(View root, ViewGroup rootParent, final OnClickHandler handler) {
653             final View target = root.findViewById(viewId);
654             if (target == null) return;
655 
656             // If the view isn't an AdapterView, setting a PendingIntent template doesn't make sense
657             if (target instanceof AdapterView<?>) {
658                 AdapterView<?> av = (AdapterView<?>) target;
659                 // The PendingIntent template is stored in the view's tag.
660                 OnItemClickListener listener = new OnItemClickListener() {
661                     public void onItemClick(AdapterView<?> parent, View view,
662                             int position, long id) {
663                         // The view should be a frame layout
664                         if (view instanceof ViewGroup) {
665                             ViewGroup vg = (ViewGroup) view;
666 
667                             // AdapterViews contain their children in a frame
668                             // so we need to go one layer deeper here.
669                             if (parent instanceof AdapterViewAnimator) {
670                                 vg = (ViewGroup) vg.getChildAt(0);
671                             }
672                             if (vg == null) return;
673 
674                             RemoteResponse response = null;
675                             int childCount = vg.getChildCount();
676                             for (int i = 0; i < childCount; i++) {
677                                 Object tag = vg.getChildAt(i).getTag(com.android.internal.R.id.fillInIntent);
678                                 if (tag instanceof RemoteResponse) {
679                                     response = (RemoteResponse) tag;
680                                     break;
681                                 }
682                             }
683                             if (response == null) return;
684                             response.handleViewClick(view, handler);
685                         }
686                     }
687                 };
688                 av.setOnItemClickListener(listener);
689                 av.setTag(pendingIntentTemplate);
690             } else {
691                 Log.e(LOG_TAG, "Cannot setPendingIntentTemplate on a view which is not" +
692                         "an AdapterView (id: " + viewId + ")");
693                 return;
694             }
695         }
696 
697         @Override
getActionTag()698         public int getActionTag() {
699             return SET_PENDING_INTENT_TEMPLATE_TAG;
700         }
701 
702         @UnsupportedAppUsage
703         PendingIntent pendingIntentTemplate;
704     }
705 
706     private class SetRemoteViewsAdapterList extends Action {
SetRemoteViewsAdapterList(int id, ArrayList<RemoteViews> list, int viewTypeCount)707         public SetRemoteViewsAdapterList(int id, ArrayList<RemoteViews> list, int viewTypeCount) {
708             this.viewId = id;
709             this.list = list;
710             this.viewTypeCount = viewTypeCount;
711         }
712 
SetRemoteViewsAdapterList(Parcel parcel)713         public SetRemoteViewsAdapterList(Parcel parcel) {
714             viewId = parcel.readInt();
715             viewTypeCount = parcel.readInt();
716             list = parcel.createTypedArrayList(RemoteViews.CREATOR);
717         }
718 
writeToParcel(Parcel dest, int flags)719         public void writeToParcel(Parcel dest, int flags) {
720             dest.writeInt(viewId);
721             dest.writeInt(viewTypeCount);
722             dest.writeTypedList(list, flags);
723         }
724 
725         @Override
apply(View root, ViewGroup rootParent, OnClickHandler handler)726         public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
727             final View target = root.findViewById(viewId);
728             if (target == null) return;
729 
730             // Ensure that we are applying to an AppWidget root
731             if (!(rootParent instanceof AppWidgetHostView)) {
732                 Log.e(LOG_TAG, "SetRemoteViewsAdapterIntent action can only be used for " +
733                         "AppWidgets (root id: " + viewId + ")");
734                 return;
735             }
736             // Ensure that we are calling setRemoteAdapter on an AdapterView that supports it
737             if (!(target instanceof AbsListView) && !(target instanceof AdapterViewAnimator)) {
738                 Log.e(LOG_TAG, "Cannot setRemoteViewsAdapter on a view which is not " +
739                         "an AbsListView or AdapterViewAnimator (id: " + viewId + ")");
740                 return;
741             }
742 
743             if (target instanceof AbsListView) {
744                 AbsListView v = (AbsListView) target;
745                 Adapter a = v.getAdapter();
746                 if (a instanceof RemoteViewsListAdapter && viewTypeCount <= a.getViewTypeCount()) {
747                     ((RemoteViewsListAdapter) a).setViewsList(list);
748                 } else {
749                     v.setAdapter(new RemoteViewsListAdapter(v.getContext(), list, viewTypeCount));
750                 }
751             } else if (target instanceof AdapterViewAnimator) {
752                 AdapterViewAnimator v = (AdapterViewAnimator) target;
753                 Adapter a = v.getAdapter();
754                 if (a instanceof RemoteViewsListAdapter && viewTypeCount <= a.getViewTypeCount()) {
755                     ((RemoteViewsListAdapter) a).setViewsList(list);
756                 } else {
757                     v.setAdapter(new RemoteViewsListAdapter(v.getContext(), list, viewTypeCount));
758                 }
759             }
760         }
761 
762         @Override
getActionTag()763         public int getActionTag() {
764             return SET_REMOTE_VIEW_ADAPTER_LIST_TAG;
765         }
766 
767         int viewTypeCount;
768         ArrayList<RemoteViews> list;
769     }
770 
771     private class SetRemoteViewsAdapterIntent extends Action {
SetRemoteViewsAdapterIntent(int id, Intent intent)772         public SetRemoteViewsAdapterIntent(int id, Intent intent) {
773             this.viewId = id;
774             this.intent = intent;
775         }
776 
SetRemoteViewsAdapterIntent(Parcel parcel)777         public SetRemoteViewsAdapterIntent(Parcel parcel) {
778             viewId = parcel.readInt();
779             intent = parcel.readTypedObject(Intent.CREATOR);
780         }
781 
writeToParcel(Parcel dest, int flags)782         public void writeToParcel(Parcel dest, int flags) {
783             dest.writeInt(viewId);
784             dest.writeTypedObject(intent, flags);
785         }
786 
787         @Override
apply(View root, ViewGroup rootParent, OnClickHandler handler)788         public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
789             final View target = root.findViewById(viewId);
790             if (target == null) return;
791 
792             // Ensure that we are applying to an AppWidget root
793             if (!(rootParent instanceof AppWidgetHostView)) {
794                 Log.e(LOG_TAG, "SetRemoteViewsAdapterIntent action can only be used for " +
795                         "AppWidgets (root id: " + viewId + ")");
796                 return;
797             }
798             // Ensure that we are calling setRemoteAdapter on an AdapterView that supports it
799             if (!(target instanceof AbsListView) && !(target instanceof AdapterViewAnimator)) {
800                 Log.e(LOG_TAG, "Cannot setRemoteViewsAdapter on a view which is not " +
801                         "an AbsListView or AdapterViewAnimator (id: " + viewId + ")");
802                 return;
803             }
804 
805             // Embed the AppWidget Id for use in RemoteViewsAdapter when connecting to the intent
806             // RemoteViewsService
807             AppWidgetHostView host = (AppWidgetHostView) rootParent;
808             intent.putExtra(EXTRA_REMOTEADAPTER_APPWIDGET_ID, host.getAppWidgetId())
809                     .putExtra(EXTRA_REMOTEADAPTER_ON_LIGHT_BACKGROUND,
810                             hasFlags(FLAG_USE_LIGHT_BACKGROUND_LAYOUT));
811 
812             if (target instanceof AbsListView) {
813                 AbsListView v = (AbsListView) target;
814                 v.setRemoteViewsAdapter(intent, isAsync);
815                 v.setRemoteViewsOnClickHandler(handler);
816             } else if (target instanceof AdapterViewAnimator) {
817                 AdapterViewAnimator v = (AdapterViewAnimator) target;
818                 v.setRemoteViewsAdapter(intent, isAsync);
819                 v.setRemoteViewsOnClickHandler(handler);
820             }
821         }
822 
823         @Override
initActionAsync(ViewTree root, ViewGroup rootParent, OnClickHandler handler)824         public Action initActionAsync(ViewTree root, ViewGroup rootParent,
825                 OnClickHandler handler) {
826             SetRemoteViewsAdapterIntent copy = new SetRemoteViewsAdapterIntent(viewId, intent);
827             copy.isAsync = true;
828             return copy;
829         }
830 
831         @Override
getActionTag()832         public int getActionTag() {
833             return SET_REMOTE_VIEW_ADAPTER_INTENT_TAG;
834         }
835 
836         Intent intent;
837         boolean isAsync = false;
838     }
839 
840     /**
841      * Equivalent to calling
842      * {@link android.view.View#setOnClickListener(android.view.View.OnClickListener)}
843      * to launch the provided {@link PendingIntent}.
844      */
845     private class SetOnClickResponse extends Action {
846 
SetOnClickResponse(int id, RemoteResponse response)847         SetOnClickResponse(int id, RemoteResponse response) {
848             this.viewId = id;
849             this.mResponse = response;
850         }
851 
SetOnClickResponse(Parcel parcel)852         SetOnClickResponse(Parcel parcel) {
853             viewId = parcel.readInt();
854             mResponse = new RemoteResponse();
855             mResponse.readFromParcel(parcel);
856         }
857 
writeToParcel(Parcel dest, int flags)858         public void writeToParcel(Parcel dest, int flags) {
859             dest.writeInt(viewId);
860             mResponse.writeToParcel(dest, flags);
861         }
862 
863         @Override
apply(View root, ViewGroup rootParent, final OnClickHandler handler)864         public void apply(View root, ViewGroup rootParent, final OnClickHandler handler) {
865             final View target = root.findViewById(viewId);
866             if (target == null) return;
867 
868             if (mResponse.mPendingIntent != null) {
869                 // If the view is an AdapterView, setting a PendingIntent on click doesn't make
870                 // much sense, do they mean to set a PendingIntent template for the
871                 // AdapterView's children?
872                 if (hasFlags(FLAG_WIDGET_IS_COLLECTION_CHILD)) {
873                     Log.w(LOG_TAG, "Cannot SetOnClickResponse for collection item "
874                             + "(id: " + viewId + ")");
875                     ApplicationInfo appInfo = root.getContext().getApplicationInfo();
876 
877                     // We let this slide for HC and ICS so as to not break compatibility. It should
878                     // have been disabled from the outset, but was left open by accident.
879                     if (appInfo != null
880                             && appInfo.targetSdkVersion >= Build.VERSION_CODES.JELLY_BEAN) {
881                         return;
882                     }
883                 }
884                 target.setTagInternal(R.id.pending_intent_tag, mResponse.mPendingIntent);
885             } else if (mResponse.mFillIntent != null) {
886                 if (!hasFlags(FLAG_WIDGET_IS_COLLECTION_CHILD)) {
887                     Log.e(LOG_TAG, "The method setOnClickFillInIntent is available "
888                             + "only from RemoteViewsFactory (ie. on collection items).");
889                     return;
890                 }
891                 if (target == root) {
892                     // Target is a root node of an AdapterView child. Set the response in the tag.
893                     // Actual click handling is done by OnItemClickListener in
894                     // SetPendingIntentTemplate, which uses this tag information.
895                     target.setTagInternal(com.android.internal.R.id.fillInIntent, mResponse);
896                     return;
897                 }
898             } else {
899                 // No intent to apply
900                 target.setOnClickListener(null);
901                 return;
902             }
903             target.setOnClickListener(v -> mResponse.handleViewClick(v, handler));
904         }
905 
906         @Override
getActionTag()907         public int getActionTag() {
908             return SET_ON_CLICK_RESPONSE_TAG;
909         }
910 
911         final RemoteResponse mResponse;
912     }
913 
914     /** @hide **/
getSourceBounds(View v)915     public static Rect getSourceBounds(View v) {
916         final float appScale = v.getContext().getResources()
917                 .getCompatibilityInfo().applicationScale;
918         final int[] pos = new int[2];
919         v.getLocationOnScreen(pos);
920 
921         final Rect rect = new Rect();
922         rect.left = (int) (pos[0] * appScale + 0.5f);
923         rect.top = (int) (pos[1] * appScale + 0.5f);
924         rect.right = (int) ((pos[0] + v.getWidth()) * appScale + 0.5f);
925         rect.bottom = (int) ((pos[1] + v.getHeight()) * appScale + 0.5f);
926         return rect;
927     }
928 
getMethod(View view, String methodName, Class<?> paramType, boolean async)929     private MethodHandle getMethod(View view, String methodName, Class<?> paramType,
930             boolean async) {
931         MethodArgs result;
932         Class<? extends View> klass = view.getClass();
933 
934         synchronized (sMethods) {
935             // The key is defined by the view class, param class and method name.
936             sLookupKey.set(klass, paramType, methodName);
937             result = sMethods.get(sLookupKey);
938 
939             if (result == null) {
940                 Method method;
941                 try {
942                     if (paramType == null) {
943                         method = klass.getMethod(methodName);
944                     } else {
945                         method = klass.getMethod(methodName, paramType);
946                     }
947                     if (!method.isAnnotationPresent(RemotableViewMethod.class)) {
948                         throw new ActionException("view: " + klass.getName()
949                                 + " can't use method with RemoteViews: "
950                                 + methodName + getParameters(paramType));
951                     }
952 
953                     result = new MethodArgs();
954                     result.syncMethod = MethodHandles.publicLookup().unreflect(method);
955                     result.asyncMethodName =
956                             method.getAnnotation(RemotableViewMethod.class).asyncImpl();
957                 } catch (NoSuchMethodException | IllegalAccessException ex) {
958                     throw new ActionException("view: " + klass.getName() + " doesn't have method: "
959                             + methodName + getParameters(paramType));
960                 }
961 
962                 MethodKey key = new MethodKey();
963                 key.set(klass, paramType, methodName);
964                 sMethods.put(key, result);
965             }
966 
967             if (!async) {
968                 return result.syncMethod;
969             }
970             // Check this so see if async method is implemented or not.
971             if (result.asyncMethodName.isEmpty()) {
972                 return null;
973             }
974             // Async method is lazily loaded. If it is not yet loaded, load now.
975             if (result.asyncMethod == null) {
976                 MethodType asyncType = result.syncMethod.type()
977                         .dropParameterTypes(0, 1).changeReturnType(Runnable.class);
978                 try {
979                     result.asyncMethod = MethodHandles.publicLookup().findVirtual(
980                             klass, result.asyncMethodName, asyncType);
981                 } catch (NoSuchMethodException | IllegalAccessException ex) {
982                     throw new ActionException("Async implementation declared as "
983                             + result.asyncMethodName + " but not defined for " + methodName
984                             + ": public Runnable " + result.asyncMethodName + " ("
985                             + TextUtils.join(",", asyncType.parameterArray()) + ")");
986                 }
987             }
988             return result.asyncMethod;
989         }
990     }
991 
getParameters(Class<?> paramType)992     private static String getParameters(Class<?> paramType) {
993         if (paramType == null) return "()";
994         return "(" + paramType + ")";
995     }
996 
997     /**
998      * Equivalent to calling
999      * {@link Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)},
1000      * on the {@link Drawable} of a given view.
1001      * <p>
1002      * The operation will be performed on the {@link Drawable} returned by the
1003      * target {@link View#getBackground()} by default.  If targetBackground is false,
1004      * we assume the target is an {@link ImageView} and try applying the operations
1005      * to {@link ImageView#getDrawable()}.
1006      * <p>
1007      */
1008     private class SetDrawableTint extends Action {
SetDrawableTint(int id, boolean targetBackground, int colorFilter, @NonNull PorterDuff.Mode mode)1009         SetDrawableTint(int id, boolean targetBackground,
1010                 int colorFilter, @NonNull PorterDuff.Mode mode) {
1011             this.viewId = id;
1012             this.targetBackground = targetBackground;
1013             this.colorFilter = colorFilter;
1014             this.filterMode = mode;
1015         }
1016 
SetDrawableTint(Parcel parcel)1017         SetDrawableTint(Parcel parcel) {
1018             viewId = parcel.readInt();
1019             targetBackground = parcel.readInt() != 0;
1020             colorFilter = parcel.readInt();
1021             filterMode = PorterDuff.intToMode(parcel.readInt());
1022         }
1023 
writeToParcel(Parcel dest, int flags)1024         public void writeToParcel(Parcel dest, int flags) {
1025             dest.writeInt(viewId);
1026             dest.writeInt(targetBackground ? 1 : 0);
1027             dest.writeInt(colorFilter);
1028             dest.writeInt(PorterDuff.modeToInt(filterMode));
1029         }
1030 
1031         @Override
apply(View root, ViewGroup rootParent, OnClickHandler handler)1032         public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
1033             final View target = root.findViewById(viewId);
1034             if (target == null) return;
1035 
1036             // Pick the correct drawable to modify for this view
1037             Drawable targetDrawable = null;
1038             if (targetBackground) {
1039                 targetDrawable = target.getBackground();
1040             } else if (target instanceof ImageView) {
1041                 ImageView imageView = (ImageView) target;
1042                 targetDrawable = imageView.getDrawable();
1043             }
1044 
1045             if (targetDrawable != null) {
1046                 targetDrawable.mutate().setColorFilter(colorFilter, filterMode);
1047             }
1048         }
1049 
1050         @Override
getActionTag()1051         public int getActionTag() {
1052             return SET_DRAWABLE_TINT_TAG;
1053         }
1054 
1055         boolean targetBackground;
1056         int colorFilter;
1057         PorterDuff.Mode filterMode;
1058     }
1059 
1060     /**
1061      * Equivalent to calling
1062      * {@link RippleDrawable#setColor(ColorStateList)},
1063      * on the {@link Drawable} of a given view.
1064      * <p>
1065      * The operation will be performed on the {@link Drawable} returned by the
1066      * target {@link View#getBackground()}.
1067      * <p>
1068      */
1069     private class SetRippleDrawableColor extends Action {
1070 
1071         ColorStateList mColorStateList;
1072 
SetRippleDrawableColor(int id, ColorStateList colorStateList)1073         SetRippleDrawableColor(int id, ColorStateList colorStateList) {
1074             this.viewId = id;
1075             this.mColorStateList = colorStateList;
1076         }
1077 
SetRippleDrawableColor(Parcel parcel)1078         SetRippleDrawableColor(Parcel parcel) {
1079             viewId = parcel.readInt();
1080             mColorStateList = parcel.readParcelable(null);
1081         }
1082 
writeToParcel(Parcel dest, int flags)1083         public void writeToParcel(Parcel dest, int flags) {
1084             dest.writeInt(viewId);
1085             dest.writeParcelable(mColorStateList, 0);
1086         }
1087 
1088         @Override
apply(View root, ViewGroup rootParent, OnClickHandler handler)1089         public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
1090             final View target = root.findViewById(viewId);
1091             if (target == null) return;
1092 
1093             // Pick the correct drawable to modify for this view
1094             Drawable targetDrawable = target.getBackground();
1095 
1096             if (targetDrawable instanceof RippleDrawable) {
1097                 ((RippleDrawable) targetDrawable.mutate()).setColor(mColorStateList);
1098             }
1099         }
1100 
1101         @Override
getActionTag()1102         public int getActionTag() {
1103             return SET_RIPPLE_DRAWABLE_COLOR_TAG;
1104         }
1105     }
1106 
1107     private final class ViewContentNavigation extends Action {
1108         final boolean mNext;
1109 
ViewContentNavigation(int viewId, boolean next)1110         ViewContentNavigation(int viewId, boolean next) {
1111             this.viewId = viewId;
1112             this.mNext = next;
1113         }
1114 
ViewContentNavigation(Parcel in)1115         ViewContentNavigation(Parcel in) {
1116             this.viewId = in.readInt();
1117             this.mNext = in.readBoolean();
1118         }
1119 
writeToParcel(Parcel out, int flags)1120         public void writeToParcel(Parcel out, int flags) {
1121             out.writeInt(this.viewId);
1122             out.writeBoolean(this.mNext);
1123         }
1124 
1125         @Override
apply(View root, ViewGroup rootParent, OnClickHandler handler)1126         public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
1127             final View view = root.findViewById(viewId);
1128             if (view == null) return;
1129 
1130             try {
1131                 getMethod(view,
1132                         mNext ? "showNext" : "showPrevious", null, false /* async */).invoke(view);
1133             } catch (Throwable ex) {
1134                 throw new ActionException(ex);
1135             }
1136         }
1137 
mergeBehavior()1138         public int mergeBehavior() {
1139             return MERGE_IGNORE;
1140         }
1141 
1142         @Override
getActionTag()1143         public int getActionTag() {
1144             return VIEW_CONTENT_NAVIGATION_TAG;
1145         }
1146     }
1147 
1148     private static class BitmapCache {
1149 
1150         @UnsupportedAppUsage
1151         ArrayList<Bitmap> mBitmaps;
1152         int mBitmapMemory = -1;
1153 
BitmapCache()1154         public BitmapCache() {
1155             mBitmaps = new ArrayList<>();
1156         }
1157 
BitmapCache(Parcel source)1158         public BitmapCache(Parcel source) {
1159             mBitmaps = source.createTypedArrayList(Bitmap.CREATOR);
1160         }
1161 
getBitmapId(Bitmap b)1162         public int getBitmapId(Bitmap b) {
1163             if (b == null) {
1164                 return -1;
1165             } else {
1166                 if (mBitmaps.contains(b)) {
1167                     return mBitmaps.indexOf(b);
1168                 } else {
1169                     mBitmaps.add(b);
1170                     mBitmapMemory = -1;
1171                     return (mBitmaps.size() - 1);
1172                 }
1173             }
1174         }
1175 
getBitmapForId(int id)1176         public Bitmap getBitmapForId(int id) {
1177             if (id == -1 || id >= mBitmaps.size()) {
1178                 return null;
1179             } else {
1180                 return mBitmaps.get(id);
1181             }
1182         }
1183 
writeBitmapsToParcel(Parcel dest, int flags)1184         public void writeBitmapsToParcel(Parcel dest, int flags) {
1185             dest.writeTypedList(mBitmaps, flags);
1186         }
1187 
getBitmapMemory()1188         public int getBitmapMemory() {
1189             if (mBitmapMemory < 0) {
1190                 mBitmapMemory = 0;
1191                 int count = mBitmaps.size();
1192                 for (int i = 0; i < count; i++) {
1193                     mBitmapMemory += mBitmaps.get(i).getAllocationByteCount();
1194                 }
1195             }
1196             return mBitmapMemory;
1197         }
1198     }
1199 
1200     private class BitmapReflectionAction extends Action {
1201         int bitmapId;
1202         @UnsupportedAppUsage
1203         Bitmap bitmap;
1204         @UnsupportedAppUsage
1205         String methodName;
1206 
BitmapReflectionAction(int viewId, String methodName, Bitmap bitmap)1207         BitmapReflectionAction(int viewId, String methodName, Bitmap bitmap) {
1208             this.bitmap = bitmap;
1209             this.viewId = viewId;
1210             this.methodName = methodName;
1211             bitmapId = mBitmapCache.getBitmapId(bitmap);
1212         }
1213 
BitmapReflectionAction(Parcel in)1214         BitmapReflectionAction(Parcel in) {
1215             viewId = in.readInt();
1216             methodName = in.readString8();
1217             bitmapId = in.readInt();
1218             bitmap = mBitmapCache.getBitmapForId(bitmapId);
1219         }
1220 
1221         @Override
writeToParcel(Parcel dest, int flags)1222         public void writeToParcel(Parcel dest, int flags) {
1223             dest.writeInt(viewId);
1224             dest.writeString8(methodName);
1225             dest.writeInt(bitmapId);
1226         }
1227 
1228         @Override
apply(View root, ViewGroup rootParent, OnClickHandler handler)1229         public void apply(View root, ViewGroup rootParent,
1230                 OnClickHandler handler) throws ActionException {
1231             ReflectionAction ra = new ReflectionAction(viewId, methodName, ReflectionAction.BITMAP,
1232                     bitmap);
1233             ra.apply(root, rootParent, handler);
1234         }
1235 
1236         @Override
setBitmapCache(BitmapCache bitmapCache)1237         public void setBitmapCache(BitmapCache bitmapCache) {
1238             bitmapId = bitmapCache.getBitmapId(bitmap);
1239         }
1240 
1241         @Override
getActionTag()1242         public int getActionTag() {
1243             return BITMAP_REFLECTION_ACTION_TAG;
1244         }
1245     }
1246 
1247     /**
1248      * Base class for the reflection actions.
1249      */
1250     private final class ReflectionAction extends Action {
1251         static final int BOOLEAN = 1;
1252         static final int BYTE = 2;
1253         static final int SHORT = 3;
1254         static final int INT = 4;
1255         static final int LONG = 5;
1256         static final int FLOAT = 6;
1257         static final int DOUBLE = 7;
1258         static final int CHAR = 8;
1259         static final int STRING = 9;
1260         static final int CHAR_SEQUENCE = 10;
1261         static final int URI = 11;
1262         // BITMAP actions are never stored in the list of actions. They are only used locally
1263         // to implement BitmapReflectionAction, which eliminates duplicates using BitmapCache.
1264         static final int BITMAP = 12;
1265         static final int BUNDLE = 13;
1266         static final int INTENT = 14;
1267         static final int COLOR_STATE_LIST = 15;
1268         static final int ICON = 16;
1269 
1270         @UnsupportedAppUsage
1271         String methodName;
1272         int type;
1273         @UnsupportedAppUsage
1274         Object value;
1275 
ReflectionAction(int viewId, String methodName, int type, Object value)1276         ReflectionAction(int viewId, String methodName, int type, Object value) {
1277             this.viewId = viewId;
1278             this.methodName = methodName;
1279             this.type = type;
1280             this.value = value;
1281         }
1282 
ReflectionAction(Parcel in)1283         ReflectionAction(Parcel in) {
1284             this.viewId = in.readInt();
1285             this.methodName = in.readString8();
1286             this.type = in.readInt();
1287             //noinspection ConstantIfStatement
1288             if (false) {
1289                 Log.d(LOG_TAG, "read viewId=0x" + Integer.toHexString(this.viewId)
1290                         + " methodName=" + this.methodName + " type=" + this.type);
1291             }
1292 
1293             // For some values that may have been null, we first check a flag to see if they were
1294             // written to the parcel.
1295             switch (this.type) {
1296                 case BOOLEAN:
1297                     this.value = in.readBoolean();
1298                     break;
1299                 case BYTE:
1300                     this.value = in.readByte();
1301                     break;
1302                 case SHORT:
1303                     this.value = (short)in.readInt();
1304                     break;
1305                 case INT:
1306                     this.value = in.readInt();
1307                     break;
1308                 case LONG:
1309                     this.value = in.readLong();
1310                     break;
1311                 case FLOAT:
1312                     this.value = in.readFloat();
1313                     break;
1314                 case DOUBLE:
1315                     this.value = in.readDouble();
1316                     break;
1317                 case CHAR:
1318                     this.value = (char)in.readInt();
1319                     break;
1320                 case STRING:
1321                     this.value = in.readString8();
1322                     break;
1323                 case CHAR_SEQUENCE:
1324                     this.value = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
1325                     break;
1326                 case URI:
1327                     this.value = in.readTypedObject(Uri.CREATOR);
1328                     break;
1329                 case BITMAP:
1330                     this.value = in.readTypedObject(Bitmap.CREATOR);
1331                     break;
1332                 case BUNDLE:
1333                     this.value = in.readBundle();
1334                     break;
1335                 case INTENT:
1336                     this.value = in.readTypedObject(Intent.CREATOR);
1337                     break;
1338                 case COLOR_STATE_LIST:
1339                     this.value = in.readTypedObject(ColorStateList.CREATOR);
1340                     break;
1341                 case ICON:
1342                     this.value = in.readTypedObject(Icon.CREATOR);
1343                 default:
1344                     break;
1345             }
1346         }
1347 
writeToParcel(Parcel out, int flags)1348         public void writeToParcel(Parcel out, int flags) {
1349             out.writeInt(this.viewId);
1350             out.writeString8(this.methodName);
1351             out.writeInt(this.type);
1352             //noinspection ConstantIfStatement
1353             if (false) {
1354                 Log.d(LOG_TAG, "write viewId=0x" + Integer.toHexString(this.viewId)
1355                         + " methodName=" + this.methodName + " type=" + this.type);
1356             }
1357 
1358             // For some values which are null, we record an integer flag to indicate whether
1359             // we have written a valid value to the parcel.
1360             switch (this.type) {
1361                 case BOOLEAN:
1362                     out.writeBoolean((Boolean) this.value);
1363                     break;
1364                 case BYTE:
1365                     out.writeByte((Byte) this.value);
1366                     break;
1367                 case SHORT:
1368                     out.writeInt((Short) this.value);
1369                     break;
1370                 case INT:
1371                     out.writeInt((Integer) this.value);
1372                     break;
1373                 case LONG:
1374                     out.writeLong((Long) this.value);
1375                     break;
1376                 case FLOAT:
1377                     out.writeFloat((Float) this.value);
1378                     break;
1379                 case DOUBLE:
1380                     out.writeDouble((Double) this.value);
1381                     break;
1382                 case CHAR:
1383                     out.writeInt((int)((Character)this.value).charValue());
1384                     break;
1385                 case STRING:
1386                     out.writeString8((String)this.value);
1387                     break;
1388                 case CHAR_SEQUENCE:
1389                     TextUtils.writeToParcel((CharSequence)this.value, out, flags);
1390                     break;
1391                 case BUNDLE:
1392                     out.writeBundle((Bundle) this.value);
1393                     break;
1394                 case URI:
1395                 case BITMAP:
1396                 case INTENT:
1397                 case COLOR_STATE_LIST:
1398                 case ICON:
1399                     out.writeTypedObject((Parcelable) this.value, flags);
1400                     break;
1401                 default:
1402                     break;
1403             }
1404         }
1405 
getParameterType()1406         private Class<?> getParameterType() {
1407             switch (this.type) {
1408                 case BOOLEAN:
1409                     return boolean.class;
1410                 case BYTE:
1411                     return byte.class;
1412                 case SHORT:
1413                     return short.class;
1414                 case INT:
1415                     return int.class;
1416                 case LONG:
1417                     return long.class;
1418                 case FLOAT:
1419                     return float.class;
1420                 case DOUBLE:
1421                     return double.class;
1422                 case CHAR:
1423                     return char.class;
1424                 case STRING:
1425                     return String.class;
1426                 case CHAR_SEQUENCE:
1427                     return CharSequence.class;
1428                 case URI:
1429                     return Uri.class;
1430                 case BITMAP:
1431                     return Bitmap.class;
1432                 case BUNDLE:
1433                     return Bundle.class;
1434                 case INTENT:
1435                     return Intent.class;
1436                 case COLOR_STATE_LIST:
1437                     return ColorStateList.class;
1438                 case ICON:
1439                     return Icon.class;
1440                 default:
1441                     return null;
1442             }
1443         }
1444 
1445         @Override
apply(View root, ViewGroup rootParent, OnClickHandler handler)1446         public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
1447             final View view = root.findViewById(viewId);
1448             if (view == null) return;
1449 
1450             Class<?> param = getParameterType();
1451             if (param == null) {
1452                 throw new ActionException("bad type: " + this.type);
1453             }
1454             try {
1455                 getMethod(view, this.methodName, param, false /* async */).invoke(view, this.value);
1456             } catch (Throwable ex) {
1457                 throw new ActionException(ex);
1458             }
1459         }
1460 
1461         @Override
initActionAsync(ViewTree root, ViewGroup rootParent, OnClickHandler handler)1462         public Action initActionAsync(ViewTree root, ViewGroup rootParent, OnClickHandler handler) {
1463             final View view = root.findViewById(viewId);
1464             if (view == null) return ACTION_NOOP;
1465 
1466             Class<?> param = getParameterType();
1467             if (param == null) {
1468                 throw new ActionException("bad type: " + this.type);
1469             }
1470 
1471             try {
1472                 MethodHandle method = getMethod(view, this.methodName, param, true /* async */);
1473 
1474                 if (method != null) {
1475                     Runnable endAction = (Runnable) method.invoke(view, this.value);
1476                     if (endAction == null) {
1477                         return ACTION_NOOP;
1478                     } else {
1479                         // Special case view stub
1480                         if (endAction instanceof ViewStub.ViewReplaceRunnable) {
1481                             root.createTree();
1482                             // Replace child tree
1483                             root.findViewTreeById(viewId).replaceView(
1484                                     ((ViewStub.ViewReplaceRunnable) endAction).view);
1485                         }
1486                         return new RunnableAction(endAction);
1487                     }
1488                 }
1489             } catch (Throwable ex) {
1490                 throw new ActionException(ex);
1491             }
1492 
1493             return this;
1494         }
1495 
mergeBehavior()1496         public int mergeBehavior() {
1497             // smoothScrollBy is cumulative, everything else overwites.
1498             if (methodName.equals("smoothScrollBy")) {
1499                 return MERGE_APPEND;
1500             } else {
1501                 return MERGE_REPLACE;
1502             }
1503         }
1504 
1505         @Override
getActionTag()1506         public int getActionTag() {
1507             return REFLECTION_ACTION_TAG;
1508         }
1509 
1510         @Override
getUniqueKey()1511         public String getUniqueKey() {
1512             // Each type of reflection action corresponds to a setter, so each should be seen as
1513             // unique from the standpoint of merging.
1514             return super.getUniqueKey() + this.methodName + this.type;
1515         }
1516 
1517         @Override
prefersAsyncApply()1518         public boolean prefersAsyncApply() {
1519             return this.type == URI || this.type == ICON;
1520         }
1521 
1522         @Override
visitUris(@onNull Consumer<Uri> visitor)1523         public void visitUris(@NonNull Consumer<Uri> visitor) {
1524             switch (this.type) {
1525                 case URI:
1526                     final Uri uri = (Uri) this.value;
1527                     visitor.accept(uri);
1528                     break;
1529                 case ICON:
1530                     final Icon icon = (Icon) this.value;
1531                     visitIconUri(icon, visitor);
1532                     break;
1533             }
1534         }
1535     }
1536 
1537     /**
1538      * This is only used for async execution of actions and it not parcelable.
1539      */
1540     private static final class RunnableAction extends RuntimeAction {
1541         private final Runnable mRunnable;
1542 
RunnableAction(Runnable r)1543         RunnableAction(Runnable r) {
1544             mRunnable = r;
1545         }
1546 
1547         @Override
apply(View root, ViewGroup rootParent, OnClickHandler handler)1548         public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
1549             mRunnable.run();
1550         }
1551     }
1552 
configureRemoteViewsAsChild(RemoteViews rv)1553     private void configureRemoteViewsAsChild(RemoteViews rv) {
1554         rv.setBitmapCache(mBitmapCache);
1555         rv.setNotRoot();
1556     }
1557 
setNotRoot()1558     void setNotRoot() {
1559         mIsRoot = false;
1560     }
1561 
1562     /**
1563      * ViewGroup methods that are related to adding Views.
1564      */
1565     private class ViewGroupActionAdd extends Action {
1566         @UnsupportedAppUsage
1567         private RemoteViews mNestedViews;
1568         private int mIndex;
1569 
ViewGroupActionAdd(int viewId, RemoteViews nestedViews)1570         ViewGroupActionAdd(int viewId, RemoteViews nestedViews) {
1571             this(viewId, nestedViews, -1 /* index */);
1572         }
1573 
ViewGroupActionAdd(int viewId, RemoteViews nestedViews, int index)1574         ViewGroupActionAdd(int viewId, RemoteViews nestedViews, int index) {
1575             this.viewId = viewId;
1576             mNestedViews = nestedViews;
1577             mIndex = index;
1578             if (nestedViews != null) {
1579                 configureRemoteViewsAsChild(nestedViews);
1580             }
1581         }
1582 
ViewGroupActionAdd(Parcel parcel, BitmapCache bitmapCache, ApplicationInfo info, int depth, Map<Class, Object> classCookies)1583         ViewGroupActionAdd(Parcel parcel, BitmapCache bitmapCache, ApplicationInfo info,
1584                 int depth, Map<Class, Object> classCookies) {
1585             viewId = parcel.readInt();
1586             mIndex = parcel.readInt();
1587             mNestedViews = new RemoteViews(parcel, bitmapCache, info, depth, classCookies);
1588             mNestedViews.addFlags(mApplyFlags);
1589         }
1590 
writeToParcel(Parcel dest, int flags)1591         public void writeToParcel(Parcel dest, int flags) {
1592             dest.writeInt(viewId);
1593             dest.writeInt(mIndex);
1594             mNestedViews.writeToParcel(dest, flags);
1595         }
1596 
1597         @Override
hasSameAppInfo(ApplicationInfo parentInfo)1598         public boolean hasSameAppInfo(ApplicationInfo parentInfo) {
1599             return mNestedViews.hasSameAppInfo(parentInfo);
1600         }
1601 
1602         @Override
apply(View root, ViewGroup rootParent, OnClickHandler handler)1603         public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
1604             final Context context = root.getContext();
1605             final ViewGroup target = root.findViewById(viewId);
1606 
1607             if (target == null) {
1608                 return;
1609             }
1610 
1611             // Inflate nested views and add as children
1612             target.addView(mNestedViews.apply(context, target, handler), mIndex);
1613         }
1614 
1615         @Override
initActionAsync(ViewTree root, ViewGroup rootParent, OnClickHandler handler)1616         public Action initActionAsync(ViewTree root, ViewGroup rootParent, OnClickHandler handler) {
1617             // In the async implementation, update the view tree so that subsequent calls to
1618             // findViewById return the current view.
1619             root.createTree();
1620             ViewTree target = root.findViewTreeById(viewId);
1621             if ((target == null) || !(target.mRoot instanceof ViewGroup)) {
1622                 return ACTION_NOOP;
1623             }
1624             final ViewGroup targetVg = (ViewGroup) target.mRoot;
1625 
1626             // Inflate nested views and perform all the async tasks for the child remoteView.
1627             final Context context = root.mRoot.getContext();
1628             final AsyncApplyTask task = mNestedViews.getAsyncApplyTask(
1629                     context, targetVg, null, handler);
1630             final ViewTree tree = task.doInBackground();
1631 
1632             if (tree == null) {
1633                 throw new ActionException(task.mError);
1634             }
1635 
1636             // Update the global view tree, so that next call to findViewTreeById
1637             // goes through the subtree as well.
1638             target.addChild(tree, mIndex);
1639 
1640             return new RuntimeAction() {
1641                 @Override
1642                 public void apply(View root, ViewGroup rootParent, OnClickHandler handler)
1643                         throws ActionException {
1644                     task.onPostExecute(tree);
1645                     targetVg.addView(task.mResult, mIndex);
1646                 }
1647             };
1648         }
1649 
1650         @Override
setBitmapCache(BitmapCache bitmapCache)1651         public void setBitmapCache(BitmapCache bitmapCache) {
1652             mNestedViews.setBitmapCache(bitmapCache);
1653         }
1654 
1655         @Override
mergeBehavior()1656         public int mergeBehavior() {
1657             return MERGE_APPEND;
1658         }
1659 
1660         @Override
prefersAsyncApply()1661         public boolean prefersAsyncApply() {
1662             return mNestedViews.prefersAsyncApply();
1663         }
1664 
1665         @Override
getActionTag()1666         public int getActionTag() {
1667             return VIEW_GROUP_ACTION_ADD_TAG;
1668         }
1669     }
1670 
1671     /**
1672      * ViewGroup methods related to removing child views.
1673      */
1674     private class ViewGroupActionRemove extends Action {
1675         /**
1676          * Id that indicates that all child views of the affected ViewGroup should be removed.
1677          *
1678          * <p>Using -2 because the default id is -1. This avoids accidentally matching that.
1679          */
1680         private static final int REMOVE_ALL_VIEWS_ID = -2;
1681 
1682         private int mViewIdToKeep;
1683 
1684         ViewGroupActionRemove(int viewId) {
1685             this(viewId, REMOVE_ALL_VIEWS_ID);
1686         }
1687 
1688         ViewGroupActionRemove(int viewId, int viewIdToKeep) {
1689             this.viewId = viewId;
1690             mViewIdToKeep = viewIdToKeep;
1691         }
1692 
1693         ViewGroupActionRemove(Parcel parcel) {
1694             viewId = parcel.readInt();
1695             mViewIdToKeep = parcel.readInt();
1696         }
1697 
1698         public void writeToParcel(Parcel dest, int flags) {
1699             dest.writeInt(viewId);
1700             dest.writeInt(mViewIdToKeep);
1701         }
1702 
1703         @Override
1704         public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
1705             final ViewGroup target = root.findViewById(viewId);
1706 
1707             if (target == null) {
1708                 return;
1709             }
1710 
1711             if (mViewIdToKeep == REMOVE_ALL_VIEWS_ID) {
1712                 target.removeAllViews();
1713                 return;
1714             }
1715 
1716             removeAllViewsExceptIdToKeep(target);
1717         }
1718 
1719         @Override
1720         public Action initActionAsync(ViewTree root, ViewGroup rootParent, OnClickHandler handler) {
1721             // In the async implementation, update the view tree so that subsequent calls to
1722             // findViewById return the current view.
1723             root.createTree();
1724             ViewTree target = root.findViewTreeById(viewId);
1725 
1726             if ((target == null) || !(target.mRoot instanceof ViewGroup)) {
1727                 return ACTION_NOOP;
1728             }
1729 
1730             final ViewGroup targetVg = (ViewGroup) target.mRoot;
1731 
1732             // Clear all children when nested views omitted
1733             target.mChildren = null;
1734             return new RuntimeAction() {
1735                 @Override
1736                 public void apply(View root, ViewGroup rootParent, OnClickHandler handler)
1737                         throws ActionException {
1738                     if (mViewIdToKeep == REMOVE_ALL_VIEWS_ID) {
1739                         targetVg.removeAllViews();
1740                         return;
1741                     }
1742 
1743                     removeAllViewsExceptIdToKeep(targetVg);
1744                 }
1745             };
1746         }
1747 
1748         /**
1749          * Iterates through the children in the given ViewGroup and removes all the views that
1750          * do not have an id of {@link #mViewIdToKeep}.
1751          */
1752         private void removeAllViewsExceptIdToKeep(ViewGroup viewGroup) {
1753             // Otherwise, remove all the views that do not match the id to keep.
1754             int index = viewGroup.getChildCount() - 1;
1755             while (index >= 0) {
1756                 if (viewGroup.getChildAt(index).getId() != mViewIdToKeep) {
1757                     viewGroup.removeViewAt(index);
1758                 }
1759                 index--;
1760             }
1761         }
1762 
1763         @Override
1764         public int getActionTag() {
1765             return VIEW_GROUP_ACTION_REMOVE_TAG;
1766         }
1767 
1768         @Override
1769         public int mergeBehavior() {
1770             return MERGE_APPEND;
1771         }
1772     }
1773 
1774     /**
1775      * Helper action to set compound drawables on a TextView. Supports relative
1776      * (s/t/e/b) or cardinal (l/t/r/b) arrangement.
1777      */
1778     private class TextViewDrawableAction extends Action {
1779         public TextViewDrawableAction(int viewId, boolean isRelative, int d1, int d2, int d3, int d4) {
1780             this.viewId = viewId;
1781             this.isRelative = isRelative;
1782             this.useIcons = false;
1783             this.d1 = d1;
1784             this.d2 = d2;
1785             this.d3 = d3;
1786             this.d4 = d4;
1787         }
1788 
1789         public TextViewDrawableAction(int viewId, boolean isRelative,
1790                 Icon i1, Icon i2, Icon i3, Icon i4) {
1791             this.viewId = viewId;
1792             this.isRelative = isRelative;
1793             this.useIcons = true;
1794             this.i1 = i1;
1795             this.i2 = i2;
1796             this.i3 = i3;
1797             this.i4 = i4;
1798         }
1799 
1800         public TextViewDrawableAction(Parcel parcel) {
1801             viewId = parcel.readInt();
1802             isRelative = (parcel.readInt() != 0);
1803             useIcons = (parcel.readInt() != 0);
1804             if (useIcons) {
1805                 i1 = parcel.readTypedObject(Icon.CREATOR);
1806                 i2 = parcel.readTypedObject(Icon.CREATOR);
1807                 i3 = parcel.readTypedObject(Icon.CREATOR);
1808                 i4 = parcel.readTypedObject(Icon.CREATOR);
1809             } else {
1810                 d1 = parcel.readInt();
1811                 d2 = parcel.readInt();
1812                 d3 = parcel.readInt();
1813                 d4 = parcel.readInt();
1814             }
1815         }
1816 
1817         public void writeToParcel(Parcel dest, int flags) {
1818             dest.writeInt(viewId);
1819             dest.writeInt(isRelative ? 1 : 0);
1820             dest.writeInt(useIcons ? 1 : 0);
1821             if (useIcons) {
1822                 dest.writeTypedObject(i1, 0);
1823                 dest.writeTypedObject(i2, 0);
1824                 dest.writeTypedObject(i3, 0);
1825                 dest.writeTypedObject(i4, 0);
1826             } else {
1827                 dest.writeInt(d1);
1828                 dest.writeInt(d2);
1829                 dest.writeInt(d3);
1830                 dest.writeInt(d4);
1831             }
1832         }
1833 
1834         @Override
1835         public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
1836             final TextView target = root.findViewById(viewId);
1837             if (target == null) return;
1838             if (drawablesLoaded) {
1839                 if (isRelative) {
1840                     target.setCompoundDrawablesRelativeWithIntrinsicBounds(id1, id2, id3, id4);
1841                 } else {
1842                     target.setCompoundDrawablesWithIntrinsicBounds(id1, id2, id3, id4);
1843                 }
1844             } else if (useIcons) {
1845                 final Context ctx = target.getContext();
1846                 final Drawable id1 = i1 == null ? null : i1.loadDrawable(ctx);
1847                 final Drawable id2 = i2 == null ? null : i2.loadDrawable(ctx);
1848                 final Drawable id3 = i3 == null ? null : i3.loadDrawable(ctx);
1849                 final Drawable id4 = i4 == null ? null : i4.loadDrawable(ctx);
1850                 if (isRelative) {
1851                     target.setCompoundDrawablesRelativeWithIntrinsicBounds(id1, id2, id3, id4);
1852                 } else {
1853                     target.setCompoundDrawablesWithIntrinsicBounds(id1, id2, id3, id4);
1854                 }
1855             } else {
1856                 if (isRelative) {
1857                     target.setCompoundDrawablesRelativeWithIntrinsicBounds(d1, d2, d3, d4);
1858                 } else {
1859                     target.setCompoundDrawablesWithIntrinsicBounds(d1, d2, d3, d4);
1860                 }
1861             }
1862         }
1863 
1864         @Override
1865         public Action initActionAsync(ViewTree root, ViewGroup rootParent, OnClickHandler handler) {
1866             final TextView target = root.findViewById(viewId);
1867             if (target == null) return ACTION_NOOP;
1868 
1869             TextViewDrawableAction copy = useIcons ?
1870                     new TextViewDrawableAction(viewId, isRelative, i1, i2, i3, i4) :
1871                     new TextViewDrawableAction(viewId, isRelative, d1, d2, d3, d4);
1872 
1873             // Load the drawables on the background thread.
1874             copy.drawablesLoaded = true;
1875             final Context ctx = target.getContext();
1876 
1877             if (useIcons) {
1878                 copy.id1 = i1 == null ? null : i1.loadDrawable(ctx);
1879                 copy.id2 = i2 == null ? null : i2.loadDrawable(ctx);
1880                 copy.id3 = i3 == null ? null : i3.loadDrawable(ctx);
1881                 copy.id4 = i4 == null ? null : i4.loadDrawable(ctx);
1882             } else {
1883                 copy.id1 = d1 == 0 ? null : ctx.getDrawable(d1);
1884                 copy.id2 = d2 == 0 ? null : ctx.getDrawable(d2);
1885                 copy.id3 = d3 == 0 ? null : ctx.getDrawable(d3);
1886                 copy.id4 = d4 == 0 ? null : ctx.getDrawable(d4);
1887             }
1888             return copy;
1889         }
1890 
1891         @Override
1892         public boolean prefersAsyncApply() {
1893             return useIcons;
1894         }
1895 
1896         @Override
1897         public int getActionTag() {
1898             return TEXT_VIEW_DRAWABLE_ACTION_TAG;
1899         }
1900 
1901         @Override
1902         public void visitUris(@NonNull Consumer<Uri> visitor) {
1903             if (useIcons) {
1904                 visitIconUri(i1, visitor);
1905                 visitIconUri(i2, visitor);
1906                 visitIconUri(i3, visitor);
1907                 visitIconUri(i4, visitor);
1908             }
1909         }
1910 
1911         boolean isRelative = false;
1912         boolean useIcons = false;
1913         int d1, d2, d3, d4;
1914         Icon i1, i2, i3, i4;
1915 
1916         boolean drawablesLoaded = false;
1917         Drawable id1, id2, id3, id4;
1918     }
1919 
1920     /**
1921      * Helper action to set text size on a TextView in any supported units.
1922      */
1923     private class TextViewSizeAction extends Action {
1924         public TextViewSizeAction(int viewId, int units, float size) {
1925             this.viewId = viewId;
1926             this.units = units;
1927             this.size = size;
1928         }
1929 
1930         public TextViewSizeAction(Parcel parcel) {
1931             viewId = parcel.readInt();
1932             units = parcel.readInt();
1933             size  = parcel.readFloat();
1934         }
1935 
1936         public void writeToParcel(Parcel dest, int flags) {
1937             dest.writeInt(viewId);
1938             dest.writeInt(units);
1939             dest.writeFloat(size);
1940         }
1941 
1942         @Override
1943         public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
1944             final TextView target = root.findViewById(viewId);
1945             if (target == null) return;
1946             target.setTextSize(units, size);
1947         }
1948 
1949         @Override
1950         public int getActionTag() {
1951             return TEXT_VIEW_SIZE_ACTION_TAG;
1952         }
1953 
1954         int units;
1955         float size;
1956     }
1957 
1958     /**
1959      * Helper action to set padding on a View.
1960      */
1961     private class ViewPaddingAction extends Action {
1962         public ViewPaddingAction(int viewId, int left, int top, int right, int bottom) {
1963             this.viewId = viewId;
1964             this.left = left;
1965             this.top = top;
1966             this.right = right;
1967             this.bottom = bottom;
1968         }
1969 
1970         public ViewPaddingAction(Parcel parcel) {
1971             viewId = parcel.readInt();
1972             left = parcel.readInt();
1973             top = parcel.readInt();
1974             right = parcel.readInt();
1975             bottom = parcel.readInt();
1976         }
1977 
1978         public void writeToParcel(Parcel dest, int flags) {
1979             dest.writeInt(viewId);
1980             dest.writeInt(left);
1981             dest.writeInt(top);
1982             dest.writeInt(right);
1983             dest.writeInt(bottom);
1984         }
1985 
1986         @Override
1987         public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
1988             final View target = root.findViewById(viewId);
1989             if (target == null) return;
1990             target.setPadding(left, top, right, bottom);
1991         }
1992 
1993         @Override
1994         public int getActionTag() {
1995             return VIEW_PADDING_ACTION_TAG;
1996         }
1997 
1998         int left, top, right, bottom;
1999     }
2000 
2001     /**
2002      * Helper action to set layout params on a View.
2003      */
2004     private static class LayoutParamAction extends Action {
2005 
2006         /** Set marginEnd */
2007         public static final int LAYOUT_MARGIN_END_DIMEN = 1;
2008         /** Set width */
2009         public static final int LAYOUT_WIDTH = 2;
2010         public static final int LAYOUT_MARGIN_BOTTOM_DIMEN = 3;
2011         public static final int LAYOUT_MARGIN_END = 4;
2012 
2013         final int mProperty;
2014         final int mValue;
2015 
2016         /**
2017          * @param viewId ID of the view alter
2018          * @param property which layout parameter to alter
2019          * @param value new value of the layout parameter
2020          */
2021         public LayoutParamAction(int viewId, int property, int value) {
2022             this.viewId = viewId;
2023             this.mProperty = property;
2024             this.mValue = value;
2025         }
2026 
2027         public LayoutParamAction(Parcel parcel) {
2028             viewId = parcel.readInt();
2029             mProperty = parcel.readInt();
2030             mValue = parcel.readInt();
2031         }
2032 
2033         public void writeToParcel(Parcel dest, int flags) {
2034             dest.writeInt(viewId);
2035             dest.writeInt(mProperty);
2036             dest.writeInt(mValue);
2037         }
2038 
2039         @Override
2040         public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
2041             final View target = root.findViewById(viewId);
2042             if (target == null) {
2043                 return;
2044             }
2045             ViewGroup.LayoutParams layoutParams = target.getLayoutParams();
2046             if (layoutParams == null) {
2047                 return;
2048             }
2049             int value = mValue;
2050             switch (mProperty) {
2051                 case LAYOUT_MARGIN_END_DIMEN:
2052                     value = resolveDimenPixelOffset(target, mValue);
2053                     // fall-through
2054                 case LAYOUT_MARGIN_END:
2055                     if (layoutParams instanceof ViewGroup.MarginLayoutParams) {
2056                         ((ViewGroup.MarginLayoutParams) layoutParams).setMarginEnd(value);
2057                         target.setLayoutParams(layoutParams);
2058                     }
2059                     break;
2060                 case LAYOUT_MARGIN_BOTTOM_DIMEN:
2061                     if (layoutParams instanceof ViewGroup.MarginLayoutParams) {
2062                         int resolved = resolveDimenPixelOffset(target, mValue);
2063                         ((ViewGroup.MarginLayoutParams) layoutParams).bottomMargin = resolved;
2064                         target.setLayoutParams(layoutParams);
2065                     }
2066                     break;
2067                 case LAYOUT_WIDTH:
2068                     layoutParams.width = mValue;
2069                     target.setLayoutParams(layoutParams);
2070                     break;
2071                 default:
2072                     throw new IllegalArgumentException("Unknown property " + mProperty);
2073             }
2074         }
2075 
2076         private static int resolveDimenPixelOffset(View target, int value) {
2077             if (value == 0) {
2078                 return 0;
2079             }
2080             return target.getContext().getResources().getDimensionPixelOffset(value);
2081         }
2082 
2083         @Override
2084         public int getActionTag() {
2085             return LAYOUT_PARAM_ACTION_TAG;
2086         }
2087 
2088         @Override
2089         public String getUniqueKey() {
2090             return super.getUniqueKey() + mProperty;
2091         }
2092     }
2093 
2094     /**
2095      * Helper action to add a view tag with RemoteInputs.
2096      */
2097     private class SetRemoteInputsAction extends Action {
2098 
2099         public SetRemoteInputsAction(int viewId, RemoteInput[] remoteInputs) {
2100             this.viewId = viewId;
2101             this.remoteInputs = remoteInputs;
2102         }
2103 
2104         public SetRemoteInputsAction(Parcel parcel) {
2105             viewId = parcel.readInt();
2106             remoteInputs = parcel.createTypedArray(RemoteInput.CREATOR);
2107         }
2108 
2109         public void writeToParcel(Parcel dest, int flags) {
2110             dest.writeInt(viewId);
2111             dest.writeTypedArray(remoteInputs, flags);
2112         }
2113 
2114         @Override
2115         public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
2116             final View target = root.findViewById(viewId);
2117             if (target == null) return;
2118 
2119             target.setTagInternal(R.id.remote_input_tag, remoteInputs);
2120         }
2121 
2122         @Override
2123         public int getActionTag() {
2124             return SET_REMOTE_INPUTS_ACTION_TAG;
2125         }
2126 
2127         final Parcelable[] remoteInputs;
2128     }
2129 
2130     /**
2131      * Helper action to override all textViewColors
2132      */
2133     private class OverrideTextColorsAction extends Action {
2134 
2135         private final int textColor;
2136 
2137         public OverrideTextColorsAction(int textColor) {
2138             this.textColor = textColor;
2139         }
2140 
2141         public OverrideTextColorsAction(Parcel parcel) {
2142             textColor = parcel.readInt();
2143         }
2144 
2145         public void writeToParcel(Parcel dest, int flags) {
2146             dest.writeInt(textColor);
2147         }
2148 
2149         @Override
2150         public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
2151             // Let's traverse the viewtree and override all textColors!
2152             Stack<View> viewsToProcess = new Stack<>();
2153             viewsToProcess.add(root);
2154             while (!viewsToProcess.isEmpty()) {
2155                 View v = viewsToProcess.pop();
2156                 if (v instanceof TextView) {
2157                     TextView textView = (TextView) v;
2158                     textView.setText(ContrastColorUtil.clearColorSpans(textView.getText()));
2159                     textView.setTextColor(textColor);
2160                 }
2161                 if (v instanceof ViewGroup) {
2162                     ViewGroup viewGroup = (ViewGroup) v;
2163                     for (int i = 0; i < viewGroup.getChildCount(); i++) {
2164                         viewsToProcess.push(viewGroup.getChildAt(i));
2165                     }
2166                 }
2167             }
2168         }
2169 
2170         @Override
2171         public int getActionTag() {
2172             return OVERRIDE_TEXT_COLORS_TAG;
2173         }
2174     }
2175 
2176     private class SetIntTagAction extends Action {
2177         private final int mViewId;
2178         private final int mKey;
2179         private final int mTag;
2180 
2181         SetIntTagAction(int viewId, int key, int tag) {
2182             mViewId = viewId;
2183             mKey = key;
2184             mTag = tag;
2185         }
2186 
2187         SetIntTagAction(Parcel parcel) {
2188             mViewId = parcel.readInt();
2189             mKey = parcel.readInt();
2190             mTag = parcel.readInt();
2191         }
2192 
2193         public void writeToParcel(Parcel dest, int flags) {
2194             dest.writeInt(mViewId);
2195             dest.writeInt(mKey);
2196             dest.writeInt(mTag);
2197         }
2198 
2199         @Override
2200         public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
2201             final View target = root.findViewById(mViewId);
2202             if (target == null) return;
2203 
2204             target.setTagInternal(mKey, mTag);
2205         }
2206 
2207         @Override
2208         public int getActionTag() {
2209             return SET_INT_TAG_TAG;
2210         }
2211     }
2212 
2213     /**
2214      * Create a new RemoteViews object that will display the views contained
2215      * in the specified layout file.
2216      *
2217      * @param packageName Name of the package that contains the layout resource
2218      * @param layoutId The id of the layout resource
2219      */
2220     public RemoteViews(String packageName, int layoutId) {
2221         this(getApplicationInfo(packageName, UserHandle.myUserId()), layoutId);
2222     }
2223 
2224     /**
2225      * Create a new RemoteViews object that will display the views contained
2226      * in the specified layout file.
2227      *
2228      * @param packageName Name of the package that contains the layout resource.
2229      * @param userId The user under which the package is running.
2230      * @param layoutId The id of the layout resource.
2231      *
2232      * @hide
2233      */
2234     public RemoteViews(String packageName, int userId, @LayoutRes int layoutId) {
2235         this(getApplicationInfo(packageName, userId), layoutId);
2236     }
2237 
2238     /**
2239      * Create a new RemoteViews object that will display the views contained
2240      * in the specified layout file.
2241      *
2242      * @param application The application whose content is shown by the views.
2243      * @param layoutId The id of the layout resource.
2244      *
2245      * @hide
2246      */
2247     protected RemoteViews(ApplicationInfo application, @LayoutRes int layoutId) {
2248         mApplication = application;
2249         mLayoutId = layoutId;
2250         mBitmapCache = new BitmapCache();
2251         mClassCookies = null;
2252     }
2253 
2254     private boolean hasLandscapeAndPortraitLayouts() {
2255         return (mLandscape != null) && (mPortrait != null);
2256     }
2257 
2258     /**
2259      * Create a new RemoteViews object that will inflate as the specified
2260      * landspace or portrait RemoteViews, depending on the current configuration.
2261      *
2262      * @param landscape The RemoteViews to inflate in landscape configuration
2263      * @param portrait The RemoteViews to inflate in portrait configuration
2264      */
2265     public RemoteViews(RemoteViews landscape, RemoteViews portrait) {
2266         if (landscape == null || portrait == null) {
2267             throw new RuntimeException("Both RemoteViews must be non-null");
2268         }
2269         if (!landscape.hasSameAppInfo(portrait.mApplication)) {
2270             throw new RuntimeException("Both RemoteViews must share the same package and user");
2271         }
2272         mApplication = portrait.mApplication;
2273         mLayoutId = portrait.mLayoutId;
2274         mLightBackgroundLayoutId = portrait.mLightBackgroundLayoutId;
2275 
2276         mLandscape = landscape;
2277         mPortrait = portrait;
2278 
2279         mBitmapCache = new BitmapCache();
2280         configureRemoteViewsAsChild(landscape);
2281         configureRemoteViewsAsChild(portrait);
2282 
2283         mClassCookies = (portrait.mClassCookies != null)
2284                 ? portrait.mClassCookies : landscape.mClassCookies;
2285     }
2286 
2287     /**
2288      * Creates a copy of another RemoteViews.
2289      */
2290     public RemoteViews(RemoteViews src) {
2291         mBitmapCache = src.mBitmapCache;
2292         mApplication = src.mApplication;
2293         mIsRoot = src.mIsRoot;
2294         mLayoutId = src.mLayoutId;
2295         mLightBackgroundLayoutId = src.mLightBackgroundLayoutId;
2296         mApplyFlags = src.mApplyFlags;
2297         mClassCookies = src.mClassCookies;
2298 
2299         if (src.hasLandscapeAndPortraitLayouts()) {
2300             mLandscape = new RemoteViews(src.mLandscape);
2301             mPortrait = new RemoteViews(src.mPortrait);
2302         }
2303 
2304         if (src.mActions != null) {
2305             Parcel p = Parcel.obtain();
2306             p.putClassCookies(mClassCookies);
2307             src.writeActionsToParcel(p);
2308             p.setDataPosition(0);
2309             // Since src is already in memory, we do not care about stack overflow as it has
2310             // already been read once.
2311             readActionsFromParcel(p, 0);
2312             p.recycle();
2313         }
2314 
2315         // Now that everything is initialized and duplicated, setting a new BitmapCache will
2316         // re-initialize the cache.
2317         setBitmapCache(new BitmapCache());
2318     }
2319 
2320     /**
2321      * Reads a RemoteViews object from a parcel.
2322      *
2323      * @param parcel
2324      */
2325     public RemoteViews(Parcel parcel) {
2326         this(parcel, null, null, 0, null);
2327     }
2328 
2329     private RemoteViews(Parcel parcel, BitmapCache bitmapCache, ApplicationInfo info, int depth,
2330             Map<Class, Object> classCookies) {
2331         if (depth > MAX_NESTED_VIEWS
2332                 && (UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID)) {
2333             throw new IllegalArgumentException("Too many nested views.");
2334         }
2335         depth++;
2336 
2337         int mode = parcel.readInt();
2338 
2339         // We only store a bitmap cache in the root of the RemoteViews.
2340         if (bitmapCache == null) {
2341             mBitmapCache = new BitmapCache(parcel);
2342             // Store the class cookies such that they are available when we clone this RemoteView.
2343             mClassCookies = parcel.copyClassCookies();
2344         } else {
2345             setBitmapCache(bitmapCache);
2346             mClassCookies = classCookies;
2347             setNotRoot();
2348         }
2349 
2350         if (mode == MODE_NORMAL) {
2351             mApplication = parcel.readInt() == 0 ? info :
2352                     ApplicationInfo.CREATOR.createFromParcel(parcel);
2353             mLayoutId = parcel.readInt();
2354             mLightBackgroundLayoutId = parcel.readInt();
2355 
2356             readActionsFromParcel(parcel, depth);
2357         } else {
2358             // MODE_HAS_LANDSCAPE_AND_PORTRAIT
2359             mLandscape = new RemoteViews(parcel, mBitmapCache, info, depth, mClassCookies);
2360             mPortrait = new RemoteViews(parcel, mBitmapCache, mLandscape.mApplication, depth,
2361                     mClassCookies);
2362             mApplication = mPortrait.mApplication;
2363             mLayoutId = mPortrait.mLayoutId;
2364             mLightBackgroundLayoutId = mPortrait.mLightBackgroundLayoutId;
2365         }
2366         mApplyFlags = parcel.readInt();
2367     }
2368 
2369     private void readActionsFromParcel(Parcel parcel, int depth) {
2370         int count = parcel.readInt();
2371         if (count > 0) {
2372             mActions = new ArrayList<>(count);
2373             for (int i = 0; i < count; i++) {
2374                 mActions.add(getActionFromParcel(parcel, depth));
2375             }
2376         }
2377     }
2378 
2379     private Action getActionFromParcel(Parcel parcel, int depth) {
2380         int tag = parcel.readInt();
2381         switch (tag) {
2382             case SET_ON_CLICK_RESPONSE_TAG:
2383                 return new SetOnClickResponse(parcel);
2384             case SET_DRAWABLE_TINT_TAG:
2385                 return new SetDrawableTint(parcel);
2386             case REFLECTION_ACTION_TAG:
2387                 return new ReflectionAction(parcel);
2388             case VIEW_GROUP_ACTION_ADD_TAG:
2389                 return new ViewGroupActionAdd(parcel, mBitmapCache, mApplication, depth,
2390                         mClassCookies);
2391             case VIEW_GROUP_ACTION_REMOVE_TAG:
2392                 return new ViewGroupActionRemove(parcel);
2393             case VIEW_CONTENT_NAVIGATION_TAG:
2394                 return new ViewContentNavigation(parcel);
2395             case SET_EMPTY_VIEW_ACTION_TAG:
2396                 return new SetEmptyView(parcel);
2397             case SET_PENDING_INTENT_TEMPLATE_TAG:
2398                 return new SetPendingIntentTemplate(parcel);
2399             case SET_REMOTE_VIEW_ADAPTER_INTENT_TAG:
2400                 return new SetRemoteViewsAdapterIntent(parcel);
2401             case TEXT_VIEW_DRAWABLE_ACTION_TAG:
2402                 return new TextViewDrawableAction(parcel);
2403             case TEXT_VIEW_SIZE_ACTION_TAG:
2404                 return new TextViewSizeAction(parcel);
2405             case VIEW_PADDING_ACTION_TAG:
2406                 return new ViewPaddingAction(parcel);
2407             case BITMAP_REFLECTION_ACTION_TAG:
2408                 return new BitmapReflectionAction(parcel);
2409             case SET_REMOTE_VIEW_ADAPTER_LIST_TAG:
2410                 return new SetRemoteViewsAdapterList(parcel);
2411             case SET_REMOTE_INPUTS_ACTION_TAG:
2412                 return new SetRemoteInputsAction(parcel);
2413             case LAYOUT_PARAM_ACTION_TAG:
2414                 return new LayoutParamAction(parcel);
2415             case OVERRIDE_TEXT_COLORS_TAG:
2416                 return new OverrideTextColorsAction(parcel);
2417             case SET_RIPPLE_DRAWABLE_COLOR_TAG:
2418                 return new SetRippleDrawableColor(parcel);
2419             case SET_INT_TAG_TAG:
2420                 return new SetIntTagAction(parcel);
2421             default:
2422                 throw new ActionException("Tag " + tag + " not found");
2423         }
2424     };
2425 
2426     /**
2427      * Returns a deep copy of the RemoteViews object. The RemoteView may not be
2428      * attached to another RemoteView -- it must be the root of a hierarchy.
2429      *
2430      * @deprecated use {@link #RemoteViews(RemoteViews)} instead.
2431      * @throws IllegalStateException if this is not the root of a RemoteView
2432      *         hierarchy
2433      */
2434     @Override
2435     @Deprecated
2436     public RemoteViews clone() {
2437         Preconditions.checkState(mIsRoot, "RemoteView has been attached to another RemoteView. "
2438                 + "May only clone the root of a RemoteView hierarchy.");
2439 
2440         return new RemoteViews(this);
2441     }
2442 
2443     public String getPackage() {
2444         return (mApplication != null) ? mApplication.packageName : null;
2445     }
2446 
2447     /**
2448      * Returns the layout id of the root layout associated with this RemoteViews. In the case
2449      * that the RemoteViews has both a landscape and portrait root, this will return the layout
2450      * id associated with the portrait layout.
2451      *
2452      * @return the layout id.
2453      */
2454     public int getLayoutId() {
2455         return hasFlags(FLAG_USE_LIGHT_BACKGROUND_LAYOUT) && (mLightBackgroundLayoutId != 0)
2456                 ? mLightBackgroundLayoutId : mLayoutId;
2457     }
2458 
2459     /**
2460      * Recursively sets BitmapCache in the hierarchy and update the bitmap ids.
2461      */
2462     private void setBitmapCache(BitmapCache bitmapCache) {
2463         mBitmapCache = bitmapCache;
2464         if (!hasLandscapeAndPortraitLayouts()) {
2465             if (mActions != null) {
2466                 final int count = mActions.size();
2467                 for (int i= 0; i < count; ++i) {
2468                     mActions.get(i).setBitmapCache(bitmapCache);
2469                 }
2470             }
2471         } else {
2472             mLandscape.setBitmapCache(bitmapCache);
2473             mPortrait.setBitmapCache(bitmapCache);
2474         }
2475     }
2476 
2477     /**
2478      * Returns an estimate of the bitmap heap memory usage for this RemoteViews.
2479      */
2480     /** @hide */
2481     @UnsupportedAppUsage
2482     public int estimateMemoryUsage() {
2483         return mBitmapCache.getBitmapMemory();
2484     }
2485 
2486     /**
2487      * Add an action to be executed on the remote side when apply is called.
2488      *
2489      * @param a The action to add
2490      */
2491     private void addAction(Action a) {
2492         if (hasLandscapeAndPortraitLayouts()) {
2493             throw new RuntimeException("RemoteViews specifying separate landscape and portrait" +
2494                     " layouts cannot be modified. Instead, fully configure the landscape and" +
2495                     " portrait layouts individually before constructing the combined layout.");
2496         }
2497         if (mActions == null) {
2498             mActions = new ArrayList<>();
2499         }
2500         mActions.add(a);
2501     }
2502 
2503     /**
2504      * Equivalent to calling {@link ViewGroup#addView(View)} after inflating the
2505      * given {@link RemoteViews}. This allows users to build "nested"
2506      * {@link RemoteViews}. In cases where consumers of {@link RemoteViews} may
2507      * recycle layouts, use {@link #removeAllViews(int)} to clear any existing
2508      * children.
2509      *
2510      * @param viewId The id of the parent {@link ViewGroup} to add child into.
2511      * @param nestedView {@link RemoteViews} that describes the child.
2512      */
2513     public void addView(int viewId, RemoteViews nestedView) {
2514         addAction(nestedView == null
2515                 ? new ViewGroupActionRemove(viewId)
2516                 : new ViewGroupActionAdd(viewId, nestedView));
2517     }
2518 
2519     /**
2520      * Equivalent to calling {@link ViewGroup#addView(View, int)} after inflating the
2521      * given {@link RemoteViews}.
2522      *
2523      * @param viewId The id of the parent {@link ViewGroup} to add the child into.
2524      * @param nestedView {@link RemoteViews} of the child to add.
2525      * @param index The position at which to add the child.
2526      *
2527      * @hide
2528      */
2529     @UnsupportedAppUsage
2530     public void addView(int viewId, RemoteViews nestedView, int index) {
2531         addAction(new ViewGroupActionAdd(viewId, nestedView, index));
2532     }
2533 
2534     /**
2535      * Equivalent to calling {@link ViewGroup#removeAllViews()}.
2536      *
2537      * @param viewId The id of the parent {@link ViewGroup} to remove all
2538      *            children from.
2539      */
2540     public void removeAllViews(int viewId) {
2541         addAction(new ViewGroupActionRemove(viewId));
2542     }
2543 
2544     /**
2545      * Removes all views in the {@link ViewGroup} specified by the {@code viewId} except for any
2546      * child that has the {@code viewIdToKeep} as its id.
2547      *
2548      * @param viewId The id of the parent {@link ViewGroup} to remove children from.
2549      * @param viewIdToKeep The id of a child that should not be removed.
2550      *
2551      * @hide
2552      */
2553     public void removeAllViewsExceptId(int viewId, int viewIdToKeep) {
2554         addAction(new ViewGroupActionRemove(viewId, viewIdToKeep));
2555     }
2556 
2557     /**
2558      * Equivalent to calling {@link AdapterViewAnimator#showNext()}
2559      *
2560      * @param viewId The id of the view on which to call {@link AdapterViewAnimator#showNext()}
2561      */
2562     public void showNext(int viewId) {
2563         addAction(new ViewContentNavigation(viewId, true /* next */));
2564     }
2565 
2566     /**
2567      * Equivalent to calling {@link AdapterViewAnimator#showPrevious()}
2568      *
2569      * @param viewId The id of the view on which to call {@link AdapterViewAnimator#showPrevious()}
2570      */
2571     public void showPrevious(int viewId) {
2572         addAction(new ViewContentNavigation(viewId, false /* next */));
2573     }
2574 
2575     /**
2576      * Equivalent to calling {@link AdapterViewAnimator#setDisplayedChild(int)}
2577      *
2578      * @param viewId The id of the view on which to call
2579      *               {@link AdapterViewAnimator#setDisplayedChild(int)}
2580      */
2581     public void setDisplayedChild(int viewId, int childIndex) {
2582         setInt(viewId, "setDisplayedChild", childIndex);
2583     }
2584 
2585     /**
2586      * Equivalent to calling {@link View#setVisibility(int)}
2587      *
2588      * @param viewId The id of the view whose visibility should change
2589      * @param visibility The new visibility for the view
2590      */
2591     public void setViewVisibility(int viewId, int visibility) {
2592         setInt(viewId, "setVisibility", visibility);
2593     }
2594 
2595     /**
2596      * Equivalent to calling {@link TextView#setText(CharSequence)}
2597      *
2598      * @param viewId The id of the view whose text should change
2599      * @param text The new text for the view
2600      */
2601     public void setTextViewText(int viewId, CharSequence text) {
2602         setCharSequence(viewId, "setText", text);
2603     }
2604 
2605     /**
2606      * Equivalent to calling {@link TextView#setTextSize(int, float)}
2607      *
2608      * @param viewId The id of the view whose text size should change
2609      * @param units The units of size (e.g. COMPLEX_UNIT_SP)
2610      * @param size The size of the text
2611      */
2612     public void setTextViewTextSize(int viewId, int units, float size) {
2613         addAction(new TextViewSizeAction(viewId, units, size));
2614     }
2615 
2616     /**
2617      * Equivalent to calling
2618      * {@link TextView#setCompoundDrawablesWithIntrinsicBounds(int, int, int, int)}.
2619      *
2620      * @param viewId The id of the view whose text should change
2621      * @param left The id of a drawable to place to the left of the text, or 0
2622      * @param top The id of a drawable to place above the text, or 0
2623      * @param right The id of a drawable to place to the right of the text, or 0
2624      * @param bottom The id of a drawable to place below the text, or 0
2625      */
2626     public void setTextViewCompoundDrawables(int viewId, int left, int top, int right, int bottom) {
2627         addAction(new TextViewDrawableAction(viewId, false, left, top, right, bottom));
2628     }
2629 
2630     /**
2631      * Equivalent to calling {@link
2632      * TextView#setCompoundDrawablesRelativeWithIntrinsicBounds(int, int, int, int)}.
2633      *
2634      * @param viewId The id of the view whose text should change
2635      * @param start The id of a drawable to place before the text (relative to the
2636      * layout direction), or 0
2637      * @param top The id of a drawable to place above the text, or 0
2638      * @param end The id of a drawable to place after the text, or 0
2639      * @param bottom The id of a drawable to place below the text, or 0
2640      */
2641     public void setTextViewCompoundDrawablesRelative(int viewId, int start, int top, int end, int bottom) {
2642         addAction(new TextViewDrawableAction(viewId, true, start, top, end, bottom));
2643     }
2644 
2645     /**
2646      * Equivalent to calling {@link
2647      * TextView#setCompoundDrawablesWithIntrinsicBounds(Drawable, Drawable, Drawable, Drawable)}
2648      * using the drawables yielded by {@link Icon#loadDrawable(Context)}.
2649      *
2650      * @param viewId The id of the view whose text should change
2651      * @param left an Icon to place to the left of the text, or 0
2652      * @param top an Icon to place above the text, or 0
2653      * @param right an Icon to place to the right of the text, or 0
2654      * @param bottom an Icon to place below the text, or 0
2655      *
2656      * @hide
2657      */
2658     public void setTextViewCompoundDrawables(int viewId, Icon left, Icon top, Icon right, Icon bottom) {
2659         addAction(new TextViewDrawableAction(viewId, false, left, top, right, bottom));
2660     }
2661 
2662     /**
2663      * Equivalent to calling {@link
2664      * TextView#setCompoundDrawablesRelativeWithIntrinsicBounds(Drawable, Drawable, Drawable, Drawable)}
2665      * using the drawables yielded by {@link Icon#loadDrawable(Context)}.
2666      *
2667      * @param viewId The id of the view whose text should change
2668      * @param start an Icon to place before the text (relative to the
2669      * layout direction), or 0
2670      * @param top an Icon to place above the text, or 0
2671      * @param end an Icon to place after the text, or 0
2672      * @param bottom an Icon to place below the text, or 0
2673      *
2674      * @hide
2675      */
2676     public void setTextViewCompoundDrawablesRelative(int viewId, Icon start, Icon top, Icon end, Icon bottom) {
2677         addAction(new TextViewDrawableAction(viewId, true, start, top, end, bottom));
2678     }
2679 
2680     /**
2681      * Equivalent to calling {@link ImageView#setImageResource(int)}
2682      *
2683      * @param viewId The id of the view whose drawable should change
2684      * @param srcId The new resource id for the drawable
2685      */
2686     public void setImageViewResource(int viewId, int srcId) {
2687         setInt(viewId, "setImageResource", srcId);
2688     }
2689 
2690     /**
2691      * Equivalent to calling {@link ImageView#setImageURI(Uri)}
2692      *
2693      * @param viewId The id of the view whose drawable should change
2694      * @param uri The Uri for the image
2695      */
2696     public void setImageViewUri(int viewId, Uri uri) {
2697         setUri(viewId, "setImageURI", uri);
2698     }
2699 
2700     /**
2701      * Equivalent to calling {@link ImageView#setImageBitmap(Bitmap)}
2702      *
2703      * @param viewId The id of the view whose bitmap should change
2704      * @param bitmap The new Bitmap for the drawable
2705      */
2706     public void setImageViewBitmap(int viewId, Bitmap bitmap) {
2707         setBitmap(viewId, "setImageBitmap", bitmap);
2708     }
2709 
2710     /**
2711      * Equivalent to calling {@link ImageView#setImageIcon(Icon)}
2712      *
2713      * @param viewId The id of the view whose bitmap should change
2714      * @param icon The new Icon for the ImageView
2715      */
2716     public void setImageViewIcon(int viewId, Icon icon) {
2717         setIcon(viewId, "setImageIcon", icon);
2718     }
2719 
2720     /**
2721      * Equivalent to calling {@link AdapterView#setEmptyView(View)}
2722      *
2723      * @param viewId The id of the view on which to set the empty view
2724      * @param emptyViewId The view id of the empty view
2725      */
2726     public void setEmptyView(int viewId, int emptyViewId) {
2727         addAction(new SetEmptyView(viewId, emptyViewId));
2728     }
2729 
2730     /**
2731      * Equivalent to calling {@link Chronometer#setBase Chronometer.setBase},
2732      * {@link Chronometer#setFormat Chronometer.setFormat},
2733      * and {@link Chronometer#start Chronometer.start()} or
2734      * {@link Chronometer#stop Chronometer.stop()}.
2735      *
2736      * @param viewId The id of the {@link Chronometer} to change
2737      * @param base The time at which the timer would have read 0:00.  This
2738      *             time should be based off of
2739      *             {@link android.os.SystemClock#elapsedRealtime SystemClock.elapsedRealtime()}.
2740      * @param format The Chronometer format string, or null to
2741      *               simply display the timer value.
2742      * @param started True if you want the clock to be started, false if not.
2743      *
2744      * @see #setChronometerCountDown(int, boolean)
2745      */
2746     public void setChronometer(int viewId, long base, String format, boolean started) {
2747         setLong(viewId, "setBase", base);
2748         setString(viewId, "setFormat", format);
2749         setBoolean(viewId, "setStarted", started);
2750     }
2751 
2752     /**
2753      * Equivalent to calling {@link Chronometer#setCountDown(boolean) Chronometer.setCountDown} on
2754      * the chronometer with the given viewId.
2755      *
2756      * @param viewId The id of the {@link Chronometer} to change
2757      * @param isCountDown True if you want the chronometer to count down to base instead of
2758      *                    counting up.
2759      */
2760     public void setChronometerCountDown(int viewId, boolean isCountDown) {
2761         setBoolean(viewId, "setCountDown", isCountDown);
2762     }
2763 
2764     /**
2765      * Equivalent to calling {@link ProgressBar#setMax ProgressBar.setMax},
2766      * {@link ProgressBar#setProgress ProgressBar.setProgress}, and
2767      * {@link ProgressBar#setIndeterminate ProgressBar.setIndeterminate}
2768      *
2769      * If indeterminate is true, then the values for max and progress are ignored.
2770      *
2771      * @param viewId The id of the {@link ProgressBar} to change
2772      * @param max The 100% value for the progress bar
2773      * @param progress The current value of the progress bar.
2774      * @param indeterminate True if the progress bar is indeterminate,
2775      *                false if not.
2776      */
2777     public void setProgressBar(int viewId, int max, int progress,
2778             boolean indeterminate) {
2779         setBoolean(viewId, "setIndeterminate", indeterminate);
2780         if (!indeterminate) {
2781             setInt(viewId, "setMax", max);
2782             setInt(viewId, "setProgress", progress);
2783         }
2784     }
2785 
2786     /**
2787      * Equivalent to calling
2788      * {@link android.view.View#setOnClickListener(android.view.View.OnClickListener)}
2789      * to launch the provided {@link PendingIntent}. The source bounds
2790      * ({@link Intent#getSourceBounds()}) of the intent will be set to the bounds of the clicked
2791      * view in screen space.
2792      * Note that any activity options associated with the mPendingIntent may get overridden
2793      * before starting the intent.
2794      *
2795      * When setting the on-click action of items within collections (eg. {@link ListView},
2796      * {@link StackView} etc.), this method will not work. Instead, use {@link
2797      * RemoteViews#setPendingIntentTemplate(int, PendingIntent)} in conjunction with
2798      * {@link RemoteViews#setOnClickFillInIntent(int, Intent)}.
2799      *
2800      * @param viewId The id of the view that will trigger the {@link PendingIntent} when clicked
2801      * @param pendingIntent The {@link PendingIntent} to send when user clicks
2802      */
2803     public void setOnClickPendingIntent(int viewId, PendingIntent pendingIntent) {
2804         setOnClickResponse(viewId, RemoteResponse.fromPendingIntent(pendingIntent));
2805     }
2806 
2807     /**
2808      * Equivalent of calling
2809      * {@link android.view.View#setOnClickListener(android.view.View.OnClickListener)}
2810      * to launch the provided {@link RemoteResponse}.
2811      *
2812      * @param viewId The id of the view that will trigger the {@link RemoteResponse} when clicked
2813      * @param response The {@link RemoteResponse} to send when user clicks
2814      */
2815     public void setOnClickResponse(int viewId, @NonNull RemoteResponse response) {
2816         addAction(new SetOnClickResponse(viewId, response));
2817     }
2818 
2819     /**
2820      * When using collections (eg. {@link ListView}, {@link StackView} etc.) in widgets, it is very
2821      * costly to set PendingIntents on the individual items, and is hence not recommended. Instead
2822      * this method should be used to set a single PendingIntent template on the collection, and
2823      * individual items can differentiate their on-click behavior using
2824      * {@link RemoteViews#setOnClickFillInIntent(int, Intent)}.
2825      *
2826      * @param viewId The id of the collection who's children will use this PendingIntent template
2827      *          when clicked
2828      * @param pendingIntentTemplate The {@link PendingIntent} to be combined with extras specified
2829      *          by a child of viewId and executed when that child is clicked
2830      */
2831     public void setPendingIntentTemplate(int viewId, PendingIntent pendingIntentTemplate) {
2832         addAction(new SetPendingIntentTemplate(viewId, pendingIntentTemplate));
2833     }
2834 
2835     /**
2836      * When using collections (eg. {@link ListView}, {@link StackView} etc.) in widgets, it is very
2837      * costly to set PendingIntents on the individual items, and is hence not recommended. Instead
2838      * a single PendingIntent template can be set on the collection, see {@link
2839      * RemoteViews#setPendingIntentTemplate(int, PendingIntent)}, and the individual on-click
2840      * action of a given item can be distinguished by setting a fillInIntent on that item. The
2841      * fillInIntent is then combined with the PendingIntent template in order to determine the final
2842      * intent which will be executed when the item is clicked. This works as follows: any fields
2843      * which are left blank in the PendingIntent template, but are provided by the fillInIntent
2844      * will be overwritten, and the resulting PendingIntent will be used. The rest
2845      * of the PendingIntent template will then be filled in with the associated fields that are
2846      * set in fillInIntent. See {@link Intent#fillIn(Intent, int)} for more details.
2847      *
2848      * @param viewId The id of the view on which to set the fillInIntent
2849      * @param fillInIntent The intent which will be combined with the parent's PendingIntent
2850      *        in order to determine the on-click behavior of the view specified by viewId
2851      */
2852     public void setOnClickFillInIntent(int viewId, Intent fillInIntent) {
2853         setOnClickResponse(viewId, RemoteResponse.fromFillInIntent(fillInIntent));
2854     }
2855 
2856     /**
2857      * @hide
2858      * Equivalent to calling
2859      * {@link Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)},
2860      * on the {@link Drawable} of a given view.
2861      * <p>
2862      *
2863      * @param viewId The id of the view that contains the target
2864      *            {@link Drawable}
2865      * @param targetBackground If true, apply these parameters to the
2866      *            {@link Drawable} returned by
2867      *            {@link android.view.View#getBackground()}. Otherwise, assume
2868      *            the target view is an {@link ImageView} and apply them to
2869      *            {@link ImageView#getDrawable()}.
2870      * @param colorFilter Specify a color for a
2871      *            {@link android.graphics.ColorFilter} for this drawable. This will be ignored if
2872      *            {@code mode} is {@code null}.
2873      * @param mode Specify a PorterDuff mode for this drawable, or null to leave
2874      *            unchanged.
2875      */
2876     public void setDrawableTint(int viewId, boolean targetBackground,
2877             int colorFilter, @NonNull PorterDuff.Mode mode) {
2878         addAction(new SetDrawableTint(viewId, targetBackground, colorFilter, mode));
2879     }
2880 
2881     /**
2882      * @hide
2883      * Equivalent to calling
2884      * {@link RippleDrawable#setColor(ColorStateList)} on the {@link Drawable} of a given view,
2885      * assuming it's a {@link RippleDrawable}.
2886      * <p>
2887      *
2888      * @param viewId The id of the view that contains the target
2889      *            {@link RippleDrawable}
2890      * @param colorStateList Specify a color for a
2891      *            {@link ColorStateList} for this drawable.
2892      */
2893     public void setRippleDrawableColor(int viewId, ColorStateList colorStateList) {
2894         addAction(new SetRippleDrawableColor(viewId, colorStateList));
2895     }
2896 
2897     /**
2898      * @hide
2899      * Equivalent to calling {@link android.widget.ProgressBar#setProgressTintList}.
2900      *
2901      * @param viewId The id of the view whose tint should change
2902      * @param tint the tint to apply, may be {@code null} to clear tint
2903      */
2904     public void setProgressTintList(int viewId, ColorStateList tint) {
2905         addAction(new ReflectionAction(viewId, "setProgressTintList",
2906                 ReflectionAction.COLOR_STATE_LIST, tint));
2907     }
2908 
2909     /**
2910      * @hide
2911      * Equivalent to calling {@link android.widget.ProgressBar#setProgressBackgroundTintList}.
2912      *
2913      * @param viewId The id of the view whose tint should change
2914      * @param tint the tint to apply, may be {@code null} to clear tint
2915      */
2916     public void setProgressBackgroundTintList(int viewId, ColorStateList tint) {
2917         addAction(new ReflectionAction(viewId, "setProgressBackgroundTintList",
2918                 ReflectionAction.COLOR_STATE_LIST, tint));
2919     }
2920 
2921     /**
2922      * @hide
2923      * Equivalent to calling {@link android.widget.ProgressBar#setIndeterminateTintList}.
2924      *
2925      * @param viewId The id of the view whose tint should change
2926      * @param tint the tint to apply, may be {@code null} to clear tint
2927      */
2928     public void setProgressIndeterminateTintList(int viewId, ColorStateList tint) {
2929         addAction(new ReflectionAction(viewId, "setIndeterminateTintList",
2930                 ReflectionAction.COLOR_STATE_LIST, tint));
2931     }
2932 
2933     /**
2934      * Equivalent to calling {@link android.widget.TextView#setTextColor(int)}.
2935      *
2936      * @param viewId The id of the view whose text color should change
2937      * @param color Sets the text color for all the states (normal, selected,
2938      *            focused) to be this color.
2939      */
2940     public void setTextColor(int viewId, @ColorInt int color) {
2941         setInt(viewId, "setTextColor", color);
2942     }
2943 
2944     /**
2945      * @hide
2946      * Equivalent to calling {@link android.widget.TextView#setTextColor(ColorStateList)}.
2947      *
2948      * @param viewId The id of the view whose text color should change
2949      * @param colors the text colors to set
2950      */
2951     public void setTextColor(int viewId, @ColorInt ColorStateList colors) {
2952         addAction(new ReflectionAction(viewId, "setTextColor", ReflectionAction.COLOR_STATE_LIST,
2953                 colors));
2954     }
2955 
2956     /**
2957      * Equivalent to calling {@link android.widget.AbsListView#setRemoteViewsAdapter(Intent)}.
2958      *
2959      * @param appWidgetId The id of the app widget which contains the specified view. (This
2960      *      parameter is ignored in this deprecated method)
2961      * @param viewId The id of the {@link AdapterView}
2962      * @param intent The intent of the service which will be
2963      *            providing data to the RemoteViewsAdapter
2964      * @deprecated This method has been deprecated. See
2965      *      {@link android.widget.RemoteViews#setRemoteAdapter(int, Intent)}
2966      */
2967     @Deprecated
2968     public void setRemoteAdapter(int appWidgetId, int viewId, Intent intent) {
2969         setRemoteAdapter(viewId, intent);
2970     }
2971 
2972     /**
2973      * Equivalent to calling {@link android.widget.AbsListView#setRemoteViewsAdapter(Intent)}.
2974      * Can only be used for App Widgets.
2975      *
2976      * @param viewId The id of the {@link AdapterView}
2977      * @param intent The intent of the service which will be
2978      *            providing data to the RemoteViewsAdapter
2979      */
2980     public void setRemoteAdapter(int viewId, Intent intent) {
2981         addAction(new SetRemoteViewsAdapterIntent(viewId, intent));
2982     }
2983 
2984     /**
2985      * Creates a simple Adapter for the viewId specified. The viewId must point to an AdapterView,
2986      * ie. {@link ListView}, {@link GridView}, {@link StackView} or {@link AdapterViewAnimator}.
2987      * This is a simpler but less flexible approach to populating collection widgets. Its use is
2988      * encouraged for most scenarios, as long as the total memory within the list of RemoteViews
2989      * is relatively small (ie. doesn't contain large or numerous Bitmaps, see {@link
2990      * RemoteViews#setImageViewBitmap}). In the case of numerous images, the use of API is still
2991      * possible by setting image URIs instead of Bitmaps, see {@link RemoteViews#setImageViewUri}.
2992      *
2993      * This API is supported in the compatibility library for previous API levels, see
2994      * RemoteViewsCompat.
2995      *
2996      * @param viewId The id of the {@link AdapterView}
2997      * @param list The list of RemoteViews which will populate the view specified by viewId.
2998      * @param viewTypeCount The maximum number of unique layout id's used to construct the list of
2999      *      RemoteViews. This count cannot change during the life-cycle of a given widget, so this
3000      *      parameter should account for the maximum possible number of types that may appear in the
3001      *      See {@link Adapter#getViewTypeCount()}.
3002      *
3003      * @hide
3004      * @deprecated this appears to have no users outside of UnsupportedAppUsage?
3005      */
3006     @UnsupportedAppUsage
3007     @Deprecated
3008     public void setRemoteAdapter(int viewId, ArrayList<RemoteViews> list, int viewTypeCount) {
3009         addAction(new SetRemoteViewsAdapterList(viewId, list, viewTypeCount));
3010     }
3011 
3012     /**
3013      * Equivalent to calling {@link ListView#smoothScrollToPosition(int)}.
3014      *
3015      * @param viewId The id of the view to change
3016      * @param position Scroll to this adapter position
3017      */
3018     public void setScrollPosition(int viewId, int position) {
3019         setInt(viewId, "smoothScrollToPosition", position);
3020     }
3021 
3022     /**
3023      * Equivalent to calling {@link ListView#smoothScrollByOffset(int)}.
3024      *
3025      * @param viewId The id of the view to change
3026      * @param offset Scroll by this adapter position offset
3027      */
3028     public void setRelativeScrollPosition(int viewId, int offset) {
3029         setInt(viewId, "smoothScrollByOffset", offset);
3030     }
3031 
3032     /**
3033      * Equivalent to calling {@link android.view.View#setPadding(int, int, int, int)}.
3034      *
3035      * @param viewId The id of the view to change
3036      * @param left the left padding in pixels
3037      * @param top the top padding in pixels
3038      * @param right the right padding in pixels
3039      * @param bottom the bottom padding in pixels
3040      */
3041     public void setViewPadding(int viewId, int left, int top, int right, int bottom) {
3042         addAction(new ViewPaddingAction(viewId, left, top, right, bottom));
3043     }
3044 
3045     /**
3046      * @hide
3047      * Equivalent to calling {@link android.view.ViewGroup.MarginLayoutParams#setMarginEnd(int)}.
3048      * Only works if the {@link View#getLayoutParams()} supports margins.
3049      * Hidden for now since we don't want to support this for all different layout margins yet.
3050      *
3051      * @param viewId The id of the view to change
3052      * @param endMarginDimen a dimen resource to read the margin from or 0 to clear the margin.
3053      */
3054     public void setViewLayoutMarginEndDimen(int viewId, @DimenRes int endMarginDimen) {
3055         addAction(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_MARGIN_END_DIMEN,
3056                 endMarginDimen));
3057     }
3058 
3059     /**
3060      * Equivalent to calling {@link android.view.ViewGroup.MarginLayoutParams#setMarginEnd(int)}.
3061      * Only works if the {@link View#getLayoutParams()} supports margins.
3062      * Hidden for now since we don't want to support this for all different layout margins yet.
3063      *
3064      * @param viewId The id of the view to change
3065      * @param endMargin a value in pixels for the end margin.
3066      * @hide
3067      */
3068     public void setViewLayoutMarginEnd(int viewId, @DimenRes int endMargin) {
3069         addAction(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_MARGIN_END,
3070                 endMargin));
3071     }
3072 
3073     /**
3074      * Equivalent to setting {@link android.view.ViewGroup.MarginLayoutParams#bottomMargin}.
3075      *
3076      * @param bottomMarginDimen a dimen resource to read the margin from or 0 to clear the margin.
3077      * @hide
3078      */
3079     public void setViewLayoutMarginBottomDimen(int viewId, @DimenRes int bottomMarginDimen) {
3080         addAction(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_MARGIN_BOTTOM_DIMEN,
3081                 bottomMarginDimen));
3082     }
3083 
3084     /**
3085      * Equivalent to setting {@link android.view.ViewGroup.LayoutParams#width}.
3086      *
3087      * @param layoutWidth one of 0, MATCH_PARENT or WRAP_CONTENT. Other sizes are not allowed
3088      *                    because they behave poorly when the density changes.
3089      * @hide
3090      */
3091     public void setViewLayoutWidth(int viewId, int layoutWidth) {
3092         if (layoutWidth != 0 && layoutWidth != ViewGroup.LayoutParams.MATCH_PARENT
3093                 && layoutWidth != ViewGroup.LayoutParams.WRAP_CONTENT) {
3094             throw new IllegalArgumentException("Only supports 0, WRAP_CONTENT and MATCH_PARENT");
3095         }
3096         mActions.add(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_WIDTH, layoutWidth));
3097     }
3098 
3099     /**
3100      * Call a method taking one boolean on a view in the layout for this RemoteViews.
3101      *
3102      * @param viewId The id of the view on which to call the method.
3103      * @param methodName The name of the method to call.
3104      * @param value The value to pass to the method.
3105      */
3106     public void setBoolean(int viewId, String methodName, boolean value) {
3107         addAction(new ReflectionAction(viewId, methodName, ReflectionAction.BOOLEAN, value));
3108     }
3109 
3110     /**
3111      * Call a method taking one byte on a view in the layout for this RemoteViews.
3112      *
3113      * @param viewId The id of the view on which to call the method.
3114      * @param methodName The name of the method to call.
3115      * @param value The value to pass to the method.
3116      */
3117     public void setByte(int viewId, String methodName, byte value) {
3118         addAction(new ReflectionAction(viewId, methodName, ReflectionAction.BYTE, value));
3119     }
3120 
3121     /**
3122      * Call a method taking one short on a view in the layout for this RemoteViews.
3123      *
3124      * @param viewId The id of the view on which to call the method.
3125      * @param methodName The name of the method to call.
3126      * @param value The value to pass to the method.
3127      */
3128     public void setShort(int viewId, String methodName, short value) {
3129         addAction(new ReflectionAction(viewId, methodName, ReflectionAction.SHORT, value));
3130     }
3131 
3132     /**
3133      * Call a method taking one int on a view in the layout for this RemoteViews.
3134      *
3135      * @param viewId The id of the view on which to call the method.
3136      * @param methodName The name of the method to call.
3137      * @param value The value to pass to the method.
3138      */
3139     public void setInt(int viewId, String methodName, int value) {
3140         addAction(new ReflectionAction(viewId, methodName, ReflectionAction.INT, value));
3141     }
3142 
3143     /**
3144      * Call a method taking one ColorStateList on a view in the layout for this RemoteViews.
3145      *
3146      * @param viewId The id of the view on which to call the method.
3147      * @param methodName The name of the method to call.
3148      * @param value The value to pass to the method.
3149      *
3150      * @hide
3151      */
3152     public void setColorStateList(int viewId, String methodName, ColorStateList value) {
3153         addAction(new ReflectionAction(viewId, methodName, ReflectionAction.COLOR_STATE_LIST,
3154                 value));
3155     }
3156 
3157 
3158     /**
3159      * Call a method taking one long on a view in the layout for this RemoteViews.
3160      *
3161      * @param viewId The id of the view on which to call the method.
3162      * @param methodName The name of the method to call.
3163      * @param value The value to pass to the method.
3164      */
3165     public void setLong(int viewId, String methodName, long value) {
3166         addAction(new ReflectionAction(viewId, methodName, ReflectionAction.LONG, value));
3167     }
3168 
3169     /**
3170      * Call a method taking one float on a view in the layout for this RemoteViews.
3171      *
3172      * @param viewId The id of the view on which to call the method.
3173      * @param methodName The name of the method to call.
3174      * @param value The value to pass to the method.
3175      */
3176     public void setFloat(int viewId, String methodName, float value) {
3177         addAction(new ReflectionAction(viewId, methodName, ReflectionAction.FLOAT, value));
3178     }
3179 
3180     /**
3181      * Call a method taking one double on a view in the layout for this RemoteViews.
3182      *
3183      * @param viewId The id of the view on which to call the method.
3184      * @param methodName The name of the method to call.
3185      * @param value The value to pass to the method.
3186      */
3187     public void setDouble(int viewId, String methodName, double value) {
3188         addAction(new ReflectionAction(viewId, methodName, ReflectionAction.DOUBLE, value));
3189     }
3190 
3191     /**
3192      * Call a method taking one char on a view in the layout for this RemoteViews.
3193      *
3194      * @param viewId The id of the view on which to call the method.
3195      * @param methodName The name of the method to call.
3196      * @param value The value to pass to the method.
3197      */
3198     public void setChar(int viewId, String methodName, char value) {
3199         addAction(new ReflectionAction(viewId, methodName, ReflectionAction.CHAR, value));
3200     }
3201 
3202     /**
3203      * Call a method taking one String on a view in the layout for this RemoteViews.
3204      *
3205      * @param viewId The id of the view on which to call the method.
3206      * @param methodName The name of the method to call.
3207      * @param value The value to pass to the method.
3208      */
3209     public void setString(int viewId, String methodName, String value) {
3210         addAction(new ReflectionAction(viewId, methodName, ReflectionAction.STRING, value));
3211     }
3212 
3213     /**
3214      * Call a method taking one CharSequence on a view in the layout for this RemoteViews.
3215      *
3216      * @param viewId The id of the view on which to call the method.
3217      * @param methodName The name of the method to call.
3218      * @param value The value to pass to the method.
3219      */
3220     public void setCharSequence(int viewId, String methodName, CharSequence value) {
3221         addAction(new ReflectionAction(viewId, methodName, ReflectionAction.CHAR_SEQUENCE, value));
3222     }
3223 
3224     /**
3225      * Call a method taking one Uri on a view in the layout for this RemoteViews.
3226      *
3227      * @param viewId The id of the view on which to call the method.
3228      * @param methodName The name of the method to call.
3229      * @param value The value to pass to the method.
3230      */
3231     public void setUri(int viewId, String methodName, Uri value) {
3232         if (value != null) {
3233             // Resolve any filesystem path before sending remotely
3234             value = value.getCanonicalUri();
3235             if (StrictMode.vmFileUriExposureEnabled()) {
3236                 value.checkFileUriExposed("RemoteViews.setUri()");
3237             }
3238         }
3239         addAction(new ReflectionAction(viewId, methodName, ReflectionAction.URI, value));
3240     }
3241 
3242     /**
3243      * Call a method taking one Bitmap on a view in the layout for this RemoteViews.
3244      * @more
3245      * <p class="note">The bitmap will be flattened into the parcel if this object is
3246      * sent across processes, so it may end up using a lot of memory, and may be fairly slow.</p>
3247      *
3248      * @param viewId The id of the view on which to call the method.
3249      * @param methodName The name of the method to call.
3250      * @param value The value to pass to the method.
3251      */
3252     public void setBitmap(int viewId, String methodName, Bitmap value) {
3253         addAction(new BitmapReflectionAction(viewId, methodName, value));
3254     }
3255 
3256     /**
3257      * Call a method taking one Bundle on a view in the layout for this RemoteViews.
3258      *
3259      * @param viewId The id of the view on which to call the method.
3260      * @param methodName The name of the method to call.
3261      * @param value The value to pass to the method.
3262      */
3263     public void setBundle(int viewId, String methodName, Bundle value) {
3264         addAction(new ReflectionAction(viewId, methodName, ReflectionAction.BUNDLE, value));
3265     }
3266 
3267     /**
3268      * Call a method taking one Intent on a view in the layout for this RemoteViews.
3269      *
3270      * @param viewId The id of the view on which to call the method.
3271      * @param methodName The name of the method to call.
3272      * @param value The {@link android.content.Intent} to pass the method.
3273      */
3274     public void setIntent(int viewId, String methodName, Intent value) {
3275         addAction(new ReflectionAction(viewId, methodName, ReflectionAction.INTENT, value));
3276     }
3277 
3278     /**
3279      * Call a method taking one Icon on a view in the layout for this RemoteViews.
3280      *
3281      * @param viewId The id of the view on which to call the method.
3282      * @param methodName The name of the method to call.
3283      * @param value The {@link android.graphics.drawable.Icon} to pass the method.
3284      */
3285     public void setIcon(int viewId, String methodName, Icon value) {
3286         addAction(new ReflectionAction(viewId, methodName, ReflectionAction.ICON, value));
3287     }
3288 
3289     /**
3290      * Equivalent to calling View.setContentDescription(CharSequence).
3291      *
3292      * @param viewId The id of the view whose content description should change.
3293      * @param contentDescription The new content description for the view.
3294      */
3295     public void setContentDescription(int viewId, CharSequence contentDescription) {
3296         setCharSequence(viewId, "setContentDescription", contentDescription);
3297     }
3298 
3299     /**
3300      * Equivalent to calling {@link android.view.View#setAccessibilityTraversalBefore(int)}.
3301      *
3302      * @param viewId The id of the view whose before view in accessibility traversal to set.
3303      * @param nextId The id of the next in the accessibility traversal.
3304      **/
3305     public void setAccessibilityTraversalBefore(int viewId, int nextId) {
3306         setInt(viewId, "setAccessibilityTraversalBefore", nextId);
3307     }
3308 
3309     /**
3310      * Equivalent to calling {@link android.view.View#setAccessibilityTraversalAfter(int)}.
3311      *
3312      * @param viewId The id of the view whose after view in accessibility traversal to set.
3313      * @param nextId The id of the next in the accessibility traversal.
3314      **/
3315     public void setAccessibilityTraversalAfter(int viewId, int nextId) {
3316         setInt(viewId, "setAccessibilityTraversalAfter", nextId);
3317     }
3318 
3319     /**
3320      * Equivalent to calling {@link View#setLabelFor(int)}.
3321      *
3322      * @param viewId The id of the view whose property to set.
3323      * @param labeledId The id of a view for which this view serves as a label.
3324      */
3325     public void setLabelFor(int viewId, int labeledId) {
3326         setInt(viewId, "setLabelFor", labeledId);
3327     }
3328 
3329     /**
3330      * Provides an alternate layout ID, which can be used to inflate this view. This layout will be
3331      * used by the host when the widgets displayed on a light-background where foreground elements
3332      * and text can safely draw using a dark color without any additional background protection.
3333      */
3334     public void setLightBackgroundLayoutId(@LayoutRes int layoutId) {
3335         mLightBackgroundLayoutId = layoutId;
3336     }
3337 
3338     /**
3339      * If this view supports dark text versions, creates a copy representing that version,
3340      * otherwise returns itself.
3341      * @hide
3342      */
3343     public RemoteViews getDarkTextViews() {
3344         if (hasFlags(FLAG_USE_LIGHT_BACKGROUND_LAYOUT)) {
3345             return this;
3346         }
3347 
3348         try {
3349             addFlags(FLAG_USE_LIGHT_BACKGROUND_LAYOUT);
3350             return new RemoteViews(this);
3351         } finally {
3352             mApplyFlags &= ~FLAG_USE_LIGHT_BACKGROUND_LAYOUT;
3353         }
3354     }
3355 
3356     private RemoteViews getRemoteViewsToApply(Context context) {
3357         if (hasLandscapeAndPortraitLayouts()) {
3358             int orientation = context.getResources().getConfiguration().orientation;
3359             if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
3360                 return mLandscape;
3361             } else {
3362                 return mPortrait;
3363             }
3364         }
3365         return this;
3366     }
3367 
3368     /**
3369      * Inflates the view hierarchy represented by this object and applies
3370      * all of the actions.
3371      *
3372      * <p><strong>Caller beware: this may throw</strong>
3373      *
3374      * @param context Default context to use
3375      * @param parent Parent that the resulting view hierarchy will be attached to. This method
3376      * does <strong>not</strong> attach the hierarchy. The caller should do so when appropriate.
3377      * @return The inflated view hierarchy
3378      */
3379     public View apply(Context context, ViewGroup parent) {
3380         return apply(context, parent, null);
3381     }
3382 
3383     /** @hide */
3384     public View apply(Context context, ViewGroup parent, OnClickHandler handler) {
3385         RemoteViews rvToApply = getRemoteViewsToApply(context);
3386 
3387         View result = inflateView(context, rvToApply, parent);
3388         rvToApply.performApply(result, parent, handler);
3389         return result;
3390     }
3391 
3392     /** @hide */
3393     public View applyWithTheme(Context context, ViewGroup parent, OnClickHandler handler,
3394             @StyleRes int applyThemeResId) {
3395         RemoteViews rvToApply = getRemoteViewsToApply(context);
3396 
3397         View result = inflateView(context, rvToApply, parent, applyThemeResId);
3398         rvToApply.performApply(result, parent, handler);
3399         return result;
3400     }
3401 
3402     private View inflateView(Context context, RemoteViews rv, ViewGroup parent) {
3403         return inflateView(context, rv, parent, 0);
3404     }
3405 
3406     private View inflateView(Context context, RemoteViews rv, ViewGroup parent,
3407             @StyleRes int applyThemeResId) {
3408         // RemoteViews may be built by an application installed in another
3409         // user. So build a context that loads resources from that user but
3410         // still returns the current users userId so settings like data / time formats
3411         // are loaded without requiring cross user persmissions.
3412         final Context contextForResources = getContextForResources(context);
3413         Context inflationContext = new RemoteViewsContextWrapper(context, contextForResources);
3414 
3415         // If mApplyThemeResId is not given, Theme.DeviceDefault will be used.
3416         if (applyThemeResId != 0) {
3417             inflationContext = new ContextThemeWrapper(inflationContext, applyThemeResId);
3418         }
3419         LayoutInflater inflater = (LayoutInflater)
3420                 context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
3421 
3422         // Clone inflater so we load resources from correct context and
3423         // we don't add a filter to the static version returned by getSystemService.
3424         inflater = inflater.cloneInContext(inflationContext);
3425         inflater.setFilter(shouldUseStaticFilter() ? INFLATER_FILTER : this);
3426         View v = inflater.inflate(rv.getLayoutId(), parent, false);
3427         v.setTagInternal(R.id.widget_frame, rv.getLayoutId());
3428         return v;
3429     }
3430 
3431     /**
3432      * A static filter is much lighter than RemoteViews itself. It's optimized here only for
3433      * RemoteVies class. Subclasses should always override this and return true if not overriding
3434      * {@link this#onLoadClass(Class)}.
3435      *
3436      * @hide
3437      */
3438     protected boolean shouldUseStaticFilter() {
3439         return this.getClass().equals(RemoteViews.class);
3440     }
3441 
3442     /**
3443      * Implement this interface to receive a callback when
3444      * {@link #applyAsync} or {@link #reapplyAsync} is finished.
3445      * @hide
3446      */
3447     public interface OnViewAppliedListener {
3448         /**
3449          * Callback when the RemoteView has finished inflating,
3450          * but no actions have been applied yet.
3451          */
3452         default void onViewInflated(View v) {};
3453 
3454         void onViewApplied(View v);
3455 
3456         void onError(Exception e);
3457     }
3458 
3459     /**
3460      * Applies the views asynchronously, moving as much of the task on the background
3461      * thread as possible.
3462      *
3463      * @see #apply(Context, ViewGroup)
3464      * @param context Default context to use
3465      * @param parent Parent that the resulting view hierarchy will be attached to. This method
3466      * does <strong>not</strong> attach the hierarchy. The caller should do so when appropriate.
3467      * @param listener the callback to run when all actions have been applied. May be null.
3468      * @param executor The executor to use. If null {@link AsyncTask#THREAD_POOL_EXECUTOR} is used.
3469      * @return CancellationSignal
3470      * @hide
3471      */
3472     public CancellationSignal applyAsync(
3473             Context context, ViewGroup parent, Executor executor, OnViewAppliedListener listener) {
3474         return applyAsync(context, parent, executor, listener, null);
3475     }
3476 
3477     /** @hide */
3478     public CancellationSignal applyAsync(Context context, ViewGroup parent,
3479             Executor executor, OnViewAppliedListener listener, OnClickHandler handler) {
3480         return getAsyncApplyTask(context, parent, listener, handler).startTaskOnExecutor(executor);
3481     }
3482 
3483     private AsyncApplyTask getAsyncApplyTask(Context context, ViewGroup parent,
3484             OnViewAppliedListener listener, OnClickHandler handler) {
3485         return new AsyncApplyTask(getRemoteViewsToApply(context), parent, context, listener,
3486                 handler, null);
3487     }
3488 
3489     private class AsyncApplyTask extends AsyncTask<Void, Void, ViewTree>
3490             implements CancellationSignal.OnCancelListener {
3491         final CancellationSignal mCancelSignal = new CancellationSignal();
3492         final RemoteViews mRV;
3493         final ViewGroup mParent;
3494         final Context mContext;
3495         final OnViewAppliedListener mListener;
3496         final OnClickHandler mHandler;
3497 
3498         private View mResult;
3499         private ViewTree mTree;
3500         private Action[] mActions;
3501         private Exception mError;
3502 
3503         private AsyncApplyTask(
3504                 RemoteViews rv, ViewGroup parent, Context context, OnViewAppliedListener listener,
3505                 OnClickHandler handler, View result) {
3506             mRV = rv;
3507             mParent = parent;
3508             mContext = context;
3509             mListener = listener;
3510             mHandler = handler;
3511 
3512             mResult = result;
3513         }
3514 
3515         @Override
3516         protected ViewTree doInBackground(Void... params) {
3517             try {
3518                 if (mResult == null) {
3519                     mResult = inflateView(mContext, mRV, mParent);
3520                 }
3521 
3522                 mTree = new ViewTree(mResult);
3523                 if (mRV.mActions != null) {
3524                     int count = mRV.mActions.size();
3525                     mActions = new Action[count];
3526                     for (int i = 0; i < count && !isCancelled(); i++) {
3527                         // TODO: check if isCancelled in nested views.
3528                         mActions[i] = mRV.mActions.get(i).initActionAsync(mTree, mParent, mHandler);
3529                     }
3530                 } else {
3531                     mActions = null;
3532                 }
3533                 return mTree;
3534             } catch (Exception e) {
3535                 mError = e;
3536                 return null;
3537             }
3538         }
3539 
3540         @Override
3541         protected void onPostExecute(ViewTree viewTree) {
3542             mCancelSignal.setOnCancelListener(null);
3543             if (mError == null) {
3544                 if (mListener != null) {
3545                     mListener.onViewInflated(viewTree.mRoot);
3546                 }
3547 
3548                 try {
3549                     if (mActions != null) {
3550                         OnClickHandler handler = mHandler == null
3551                                 ? DEFAULT_ON_CLICK_HANDLER : mHandler;
3552                         for (Action a : mActions) {
3553                             a.apply(viewTree.mRoot, mParent, handler);
3554                         }
3555                     }
3556                 } catch (Exception e) {
3557                     mError = e;
3558                 }
3559             }
3560 
3561             if (mListener != null) {
3562                 if (mError != null) {
3563                     mListener.onError(mError);
3564                 } else {
3565                     mListener.onViewApplied(viewTree.mRoot);
3566                 }
3567             } else if (mError != null) {
3568                 if (mError instanceof ActionException) {
3569                     throw (ActionException) mError;
3570                 } else {
3571                     throw new ActionException(mError);
3572                 }
3573             }
3574         }
3575 
3576         @Override
3577         public void onCancel() {
3578             cancel(true);
3579         }
3580 
3581         private CancellationSignal startTaskOnExecutor(Executor executor) {
3582             mCancelSignal.setOnCancelListener(this);
3583             executeOnExecutor(executor == null ? AsyncTask.THREAD_POOL_EXECUTOR : executor);
3584             return mCancelSignal;
3585         }
3586     }
3587 
3588     /**
3589      * Applies all of the actions to the provided view.
3590      *
3591      * <p><strong>Caller beware: this may throw</strong>
3592      *
3593      * @param v The view to apply the actions to.  This should be the result of
3594      * the {@link #apply(Context,ViewGroup)} call.
3595      */
3596     public void reapply(Context context, View v) {
3597         reapply(context, v, null);
3598     }
3599 
3600     /** @hide */
3601     public void reapply(Context context, View v, OnClickHandler handler) {
3602         RemoteViews rvToApply = getRemoteViewsToApply(context);
3603 
3604         // In the case that a view has this RemoteViews applied in one orientation, is persisted
3605         // across orientation change, and has the RemoteViews re-applied in the new orientation,
3606         // we throw an exception, since the layouts may be completely unrelated.
3607         if (hasLandscapeAndPortraitLayouts()) {
3608             if ((Integer) v.getTag(R.id.widget_frame) != rvToApply.getLayoutId()) {
3609                 throw new RuntimeException("Attempting to re-apply RemoteViews to a view that" +
3610                         " that does not share the same root layout id.");
3611             }
3612         }
3613 
3614         rvToApply.performApply(v, (ViewGroup) v.getParent(), handler);
3615     }
3616 
3617     /**
3618      * Applies all the actions to the provided view, moving as much of the task on the background
3619      * thread as possible.
3620      *
3621      * @see #reapply(Context, View)
3622      * @param context Default context to use
3623      * @param v The view to apply the actions to.  This should be the result of
3624      * the {@link #apply(Context,ViewGroup)} call.
3625      * @param listener the callback to run when all actions have been applied. May be null.
3626      * @param executor The executor to use. If null {@link AsyncTask#THREAD_POOL_EXECUTOR} is used
3627      * @return CancellationSignal
3628      * @hide
3629      */
3630     public CancellationSignal reapplyAsync(
3631             Context context, View v, Executor executor, OnViewAppliedListener listener) {
3632         return reapplyAsync(context, v, executor, listener, null);
3633     }
3634 
3635     /** @hide */
3636     public CancellationSignal reapplyAsync(Context context, View v, Executor executor,
3637             OnViewAppliedListener listener, OnClickHandler handler) {
3638         RemoteViews rvToApply = getRemoteViewsToApply(context);
3639 
3640         // In the case that a view has this RemoteViews applied in one orientation, is persisted
3641         // across orientation change, and has the RemoteViews re-applied in the new orientation,
3642         // we throw an exception, since the layouts may be completely unrelated.
3643         if (hasLandscapeAndPortraitLayouts()) {
3644             if ((Integer) v.getTag(R.id.widget_frame) != rvToApply.getLayoutId()) {
3645                 throw new RuntimeException("Attempting to re-apply RemoteViews to a view that" +
3646                         " that does not share the same root layout id.");
3647             }
3648         }
3649 
3650         return new AsyncApplyTask(rvToApply, (ViewGroup) v.getParent(),
3651                 context, listener, handler, v).startTaskOnExecutor(executor);
3652     }
3653 
3654     private void performApply(View v, ViewGroup parent, OnClickHandler handler) {
3655         if (mActions != null) {
3656             handler = handler == null ? DEFAULT_ON_CLICK_HANDLER : handler;
3657             final int count = mActions.size();
3658             for (int i = 0; i < count; i++) {
3659                 Action a = mActions.get(i);
3660                 a.apply(v, parent, handler);
3661             }
3662         }
3663     }
3664 
3665     /**
3666      * Returns true if the RemoteViews contains potentially costly operations and should be
3667      * applied asynchronously.
3668      *
3669      * @hide
3670      */
3671     public boolean prefersAsyncApply() {
3672         if (mActions != null) {
3673             final int count = mActions.size();
3674             for (int i = 0; i < count; i++) {
3675                 if (mActions.get(i).prefersAsyncApply()) {
3676                     return true;
3677                 }
3678             }
3679         }
3680         return false;
3681     }
3682 
3683     private Context getContextForResources(Context context) {
3684         if (mApplication != null) {
3685             if (context.getUserId() == UserHandle.getUserId(mApplication.uid)
3686                     && context.getPackageName().equals(mApplication.packageName)) {
3687                 return context;
3688             }
3689             try {
3690                 return context.createApplicationContext(mApplication,
3691                         Context.CONTEXT_RESTRICTED);
3692             } catch (NameNotFoundException e) {
3693                 Log.e(LOG_TAG, "Package name " + mApplication.packageName + " not found");
3694             }
3695         }
3696 
3697         return context;
3698     }
3699 
3700     /**
3701      * Returns the number of actions in this RemoteViews. Can be used as a sequence number.
3702      *
3703      * @hide
3704      */
3705     public int getSequenceNumber() {
3706         return (mActions == null) ? 0 : mActions.size();
3707     }
3708 
3709     /**
3710      * Used to restrict the views which can be inflated
3711      *
3712      * @see android.view.LayoutInflater.Filter#onLoadClass(java.lang.Class)
3713      * @deprecated Used by system to enforce safe inflation of {@link RemoteViews}. Apps should not
3714      * override this method. Changing of this method will NOT affect the process where RemoteViews
3715      * is rendered.
3716      */
3717     @Deprecated
3718     public boolean onLoadClass(Class clazz) {
3719         return clazz.isAnnotationPresent(RemoteView.class);
3720     }
3721 
3722     public int describeContents() {
3723         return 0;
3724     }
3725 
3726     public void writeToParcel(Parcel dest, int flags) {
3727         if (!hasLandscapeAndPortraitLayouts()) {
3728             dest.writeInt(MODE_NORMAL);
3729             // We only write the bitmap cache if we are the root RemoteViews, as this cache
3730             // is shared by all children.
3731             if (mIsRoot) {
3732                 mBitmapCache.writeBitmapsToParcel(dest, flags);
3733             }
3734             if (!mIsRoot && (flags & PARCELABLE_ELIDE_DUPLICATES) != 0) {
3735                 dest.writeInt(0);
3736             } else {
3737                 dest.writeInt(1);
3738                 mApplication.writeToParcel(dest, flags);
3739             }
3740             dest.writeInt(mLayoutId);
3741             dest.writeInt(mLightBackgroundLayoutId);
3742             writeActionsToParcel(dest);
3743         } else {
3744             dest.writeInt(MODE_HAS_LANDSCAPE_AND_PORTRAIT);
3745             // We only write the bitmap cache if we are the root RemoteViews, as this cache
3746             // is shared by all children.
3747             if (mIsRoot) {
3748                 mBitmapCache.writeBitmapsToParcel(dest, flags);
3749             }
3750             mLandscape.writeToParcel(dest, flags);
3751             // Both RemoteViews already share the same package and user
3752             mPortrait.writeToParcel(dest, flags | PARCELABLE_ELIDE_DUPLICATES);
3753         }
3754         dest.writeInt(mApplyFlags);
3755     }
3756 
3757     private void writeActionsToParcel(Parcel parcel) {
3758         int count;
3759         if (mActions != null) {
3760             count = mActions.size();
3761         } else {
3762             count = 0;
3763         }
3764         parcel.writeInt(count);
3765         for (int i = 0; i < count; i++) {
3766             Action a = mActions.get(i);
3767             parcel.writeInt(a.getActionTag());
3768             a.writeToParcel(parcel, a.hasSameAppInfo(mApplication)
3769                     ? PARCELABLE_ELIDE_DUPLICATES : 0);
3770         }
3771     }
3772 
3773     private static ApplicationInfo getApplicationInfo(String packageName, int userId) {
3774         if (packageName == null) {
3775             return null;
3776         }
3777 
3778         // Get the application for the passed in package and user.
3779         Application application = ActivityThread.currentApplication();
3780         if (application == null) {
3781             throw new IllegalStateException("Cannot create remote views out of an aplication.");
3782         }
3783 
3784         ApplicationInfo applicationInfo = application.getApplicationInfo();
3785         if (UserHandle.getUserId(applicationInfo.uid) != userId
3786                 || !applicationInfo.packageName.equals(packageName)) {
3787             try {
3788                 Context context = application.getBaseContext().createPackageContextAsUser(
3789                         packageName, 0, new UserHandle(userId));
3790                 applicationInfo = context.getApplicationInfo();
3791             } catch (NameNotFoundException nnfe) {
3792                 throw new IllegalArgumentException("No such package " + packageName);
3793             }
3794         }
3795 
3796         return applicationInfo;
3797     }
3798 
3799     /**
3800      * Returns true if the {@link #mApplication} is same as the provided info.
3801      *
3802      * @hide
3803      */
3804     public boolean hasSameAppInfo(ApplicationInfo info) {
3805         return mApplication.packageName.equals(info.packageName) && mApplication.uid == info.uid;
3806     }
3807 
3808     /**
3809      * Parcelable.Creator that instantiates RemoteViews objects
3810      */
3811     public static final @android.annotation.NonNull Parcelable.Creator<RemoteViews> CREATOR = new Parcelable.Creator<RemoteViews>() {
3812         public RemoteViews createFromParcel(Parcel parcel) {
3813             return new RemoteViews(parcel);
3814         }
3815 
3816         public RemoteViews[] newArray(int size) {
3817             return new RemoteViews[size];
3818         }
3819     };
3820 
3821     /**
3822      * A representation of the view hierarchy. Only views which have a valid ID are added
3823      * and can be searched.
3824      */
3825     private static class ViewTree {
3826         private static final int INSERT_AT_END_INDEX = -1;
3827         private View mRoot;
3828         private ArrayList<ViewTree> mChildren;
3829 
3830         private ViewTree(View root) {
3831             mRoot = root;
3832         }
3833 
3834         public void createTree() {
3835             if (mChildren != null) {
3836                 return;
3837             }
3838 
3839             mChildren = new ArrayList<>();
3840             if (mRoot instanceof ViewGroup) {
3841                 ViewGroup vg = (ViewGroup) mRoot;
3842                 int count = vg.getChildCount();
3843                 for (int i = 0; i < count; i++) {
3844                     addViewChild(vg.getChildAt(i));
3845                 }
3846             }
3847         }
3848 
3849         public ViewTree findViewTreeById(int id) {
3850             if (mRoot.getId() == id) {
3851                 return this;
3852             }
3853             if (mChildren == null) {
3854                 return null;
3855             }
3856             for (ViewTree tree : mChildren) {
3857                 ViewTree result = tree.findViewTreeById(id);
3858                 if (result != null) {
3859                     return result;
3860                 }
3861             }
3862             return null;
3863         }
3864 
3865         public void replaceView(View v) {
3866             mRoot = v;
3867             mChildren = null;
3868             createTree();
3869         }
3870 
3871         public <T extends View> T findViewById(int id) {
3872             if (mChildren == null) {
3873                 return mRoot.findViewById(id);
3874             }
3875             ViewTree tree = findViewTreeById(id);
3876             return tree == null ? null : (T) tree.mRoot;
3877         }
3878 
3879         public void addChild(ViewTree child) {
3880             addChild(child, INSERT_AT_END_INDEX);
3881         }
3882 
3883         /**
3884          * Adds the given {@link ViewTree} as a child at the given index.
3885          *
3886          * @param index The position at which to add the child or -1 to add last.
3887          */
3888         public void addChild(ViewTree child, int index) {
3889             if (mChildren == null) {
3890                 mChildren = new ArrayList<>();
3891             }
3892             child.createTree();
3893 
3894             if (index == INSERT_AT_END_INDEX) {
3895                 mChildren.add(child);
3896                 return;
3897             }
3898 
3899             mChildren.add(index, child);
3900         }
3901 
3902         private void addViewChild(View v) {
3903             // ViewTree only contains Views which can be found using findViewById.
3904             // If isRootNamespace is true, this view is skipped.
3905             // @see ViewGroup#findViewTraversal(int)
3906             if (v.isRootNamespace()) {
3907                 return;
3908             }
3909             final ViewTree target;
3910 
3911             // If the view has a valid id, i.e., if can be found using findViewById, add it to the
3912             // tree, otherwise skip this view and add its children instead.
3913             if (v.getId() != 0) {
3914                 ViewTree tree = new ViewTree(v);
3915                 mChildren.add(tree);
3916                 target = tree;
3917             } else {
3918                 target = this;
3919             }
3920 
3921             if (v instanceof ViewGroup) {
3922                 if (target.mChildren == null) {
3923                     target.mChildren = new ArrayList<>();
3924                     ViewGroup vg = (ViewGroup) v;
3925                     int count = vg.getChildCount();
3926                     for (int i = 0; i < count; i++) {
3927                         target.addViewChild(vg.getChildAt(i));
3928                     }
3929                 }
3930             }
3931         }
3932     }
3933 
3934     /**
3935      * Class representing a response to an action performed on any element of a RemoteViews.
3936      */
3937     public static class RemoteResponse {
3938 
3939         private PendingIntent mPendingIntent;
3940         private Intent mFillIntent;
3941 
3942         private IntArray mViewIds;
3943         private ArrayList<String> mElementNames;
3944 
3945         /**
3946          * Creates a response which sends a pending intent as part of the response. The source
3947          * bounds ({@link Intent#getSourceBounds()}) of the intent will be set to the bounds of the
3948          * target view in screen space.
3949          * Note that any activity options associated with the mPendingIntent may get overridden
3950          * before starting the intent.
3951          *
3952          * @param pendingIntent The {@link PendingIntent} to send as part of the response
3953          */
3954         @NonNull
3955         public static RemoteResponse fromPendingIntent(@NonNull PendingIntent pendingIntent) {
3956             RemoteResponse response = new RemoteResponse();
3957             response.mPendingIntent = pendingIntent;
3958             return response;
3959         }
3960 
3961         /**
3962          * When using collections (eg. {@link ListView}, {@link StackView} etc.) in widgets, it is
3963          * very costly to set PendingIntents on the individual items, and is hence not recommended.
3964          * Instead a single PendingIntent template can be set on the collection, see {@link
3965          * RemoteViews#setPendingIntentTemplate(int, PendingIntent)}, and the individual on-click
3966          * action of a given item can be distinguished by setting a fillInIntent on that item. The
3967          * fillInIntent is then combined with the PendingIntent template in order to determine the
3968          * final intent which will be executed when the item is clicked. This works as follows: any
3969          * fields which are left blank in the PendingIntent template, but are provided by the
3970          * fillInIntent will be overwritten, and the resulting PendingIntent will be used. The rest
3971          * of the PendingIntent template will then be filled in with the associated fields that are
3972          * set in fillInIntent. See {@link Intent#fillIn(Intent, int)} for more details.
3973          * Creates a response which sends a pending intent as part of the response. The source
3974          * bounds ({@link Intent#getSourceBounds()}) of the intent will be set to the bounds of the
3975          * target view in screen space.
3976          * Note that any activity options associated with the mPendingIntent may get overridden
3977          * before starting the intent.
3978          *
3979          * @param fillIntent The intent which will be combined with the parent's PendingIntent in
3980          *                  order to determine the behavior of the response
3981          *
3982          * @see RemoteViews#setPendingIntentTemplate(int, PendingIntent)
3983          * @see RemoteViews#setOnClickFillInIntent(int, Intent)
3984          * @return
3985          */
3986         @NonNull
3987         public static RemoteResponse fromFillInIntent(@NonNull Intent fillIntent) {
3988             RemoteResponse response = new RemoteResponse();
3989             response.mFillIntent = fillIntent;
3990             return response;
3991         }
3992 
3993         /**
3994          * Adds a shared element to be transferred as part of the transition between Activities
3995          * using cross-Activity scene animations. The position of the first element will be used as
3996          * the epicenter for the exit Transition. The position of the associated shared element in
3997          * the launched Activity will be the epicenter of its entering Transition.
3998          *
3999          * @param viewId The id of the view to be shared as part of the transition
4000          * @param sharedElementName The shared element name for this view
4001          *
4002          * @see ActivityOptions#makeSceneTransitionAnimation(Activity, Pair[])
4003          */
4004         @NonNull
4005         public RemoteResponse addSharedElement(int viewId, @NonNull String sharedElementName) {
4006             if (mViewIds == null) {
4007                 mViewIds = new IntArray();
4008                 mElementNames = new ArrayList<>();
4009             }
4010             mViewIds.add(viewId);
4011             mElementNames.add(sharedElementName);
4012             return this;
4013         }
4014 
4015         private void writeToParcel(Parcel dest, int flags) {
4016             PendingIntent.writePendingIntentOrNullToParcel(mPendingIntent, dest);
4017             if (mPendingIntent == null) {
4018                 // Only write the intent if pending intent is null
4019                 dest.writeTypedObject(mFillIntent, flags);
4020             }
4021             dest.writeIntArray(mViewIds == null ? null : mViewIds.toArray());
4022             dest.writeStringList(mElementNames);
4023         }
4024 
4025         private void readFromParcel(Parcel parcel) {
4026             mPendingIntent = PendingIntent.readPendingIntentOrNullFromParcel(parcel);
4027             if (mPendingIntent == null) {
4028                 mFillIntent = parcel.readTypedObject(Intent.CREATOR);
4029             }
4030             int[] viewIds = parcel.createIntArray();
4031             mViewIds = viewIds == null ? null : IntArray.wrap(viewIds);
4032             mElementNames = parcel.createStringArrayList();
4033         }
4034 
4035         private void handleViewClick(View v, OnClickHandler handler) {
4036             final PendingIntent pi;
4037             if (mPendingIntent != null) {
4038                 pi = mPendingIntent;
4039             } else if (mFillIntent != null) {
4040                 // Insure that this view is a child of an AdapterView
4041                 View parent = (View) v.getParent();
4042                 // Break the for loop on the first encounter of:
4043                 //    1) an AdapterView,
4044                 //    2) an AppWidgetHostView that is not a RemoteViewsFrameLayout, or
4045                 //    3) a null parent.
4046                 // 2) and 3) are unexpected and catch the case where a child is not
4047                 // correctly parented in an AdapterView.
4048                 while (parent != null && !(parent instanceof AdapterView<?>)
4049                         && !((parent instanceof AppWidgetHostView)
4050                         && !(parent instanceof RemoteViewsAdapter.RemoteViewsFrameLayout))) {
4051                     parent = (View) parent.getParent();
4052                 }
4053 
4054                 if (!(parent instanceof AdapterView<?>)) {
4055                     // Somehow they've managed to get this far without having
4056                     // and AdapterView as a parent.
4057                     Log.e(LOG_TAG, "Collection item doesn't have AdapterView parent");
4058                     return;
4059                 }
4060                 // Insure that a template pending intent has been set on an ancestor
4061                 if (!(parent.getTag() instanceof PendingIntent)) {
4062                     Log.e(LOG_TAG, "Attempting setOnClickFillInIntent without"
4063                             + " calling setPendingIntentTemplate on parent.");
4064                     return;
4065                 }
4066 
4067                 pi = (PendingIntent) parent.getTag();
4068             } else {
4069                 Log.e(LOG_TAG, "Response has neither pendingIntent nor fillInIntent");
4070                 return;
4071             }
4072 
4073             handler.onClickHandler(v, pi, this);
4074         }
4075 
4076         /** @hide */
4077         public Pair<Intent, ActivityOptions> getLaunchOptions(View view) {
4078             Intent intent = mPendingIntent != null ? new Intent() : new Intent(mFillIntent);
4079             intent.setSourceBounds(getSourceBounds(view));
4080 
4081             ActivityOptions opts = null;
4082 
4083             Context context = view.getContext();
4084             if (context.getResources().getBoolean(
4085                     com.android.internal.R.bool.config_overrideRemoteViewsActivityTransition)) {
4086                 TypedArray windowStyle = context.getTheme().obtainStyledAttributes(
4087                         com.android.internal.R.styleable.Window);
4088                 int windowAnimations = windowStyle.getResourceId(
4089                         com.android.internal.R.styleable.Window_windowAnimationStyle, 0);
4090                 TypedArray windowAnimationStyle = context.obtainStyledAttributes(
4091                         windowAnimations, com.android.internal.R.styleable.WindowAnimation);
4092                 int enterAnimationId = windowAnimationStyle.getResourceId(com.android.internal.R
4093                         .styleable.WindowAnimation_activityOpenRemoteViewsEnterAnimation, 0);
4094                 windowStyle.recycle();
4095                 windowAnimationStyle.recycle();
4096 
4097                 if (enterAnimationId != 0) {
4098                     opts = ActivityOptions.makeCustomAnimation(context,
4099                             enterAnimationId, 0);
4100                     opts.setPendingIntentLaunchFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
4101                 }
4102             }
4103 
4104             if (opts == null && mViewIds != null && mElementNames != null) {
4105                 View parent = (View) view.getParent();
4106                 while (parent != null && !(parent instanceof AppWidgetHostView)) {
4107                     parent = (View) parent.getParent();
4108                 }
4109                 if (parent instanceof AppWidgetHostView) {
4110                     opts = ((AppWidgetHostView) parent).createSharedElementActivityOptions(
4111                             mViewIds.toArray(),
4112                             mElementNames.toArray(new String[mElementNames.size()]), intent);
4113                 }
4114             }
4115 
4116             if (opts == null) {
4117                 opts = ActivityOptions.makeBasic();
4118                 opts.setPendingIntentLaunchFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
4119             }
4120             return Pair.create(intent, opts);
4121         }
4122     }
4123 
4124     /** @hide */
4125     public static boolean startPendingIntent(View view, PendingIntent pendingIntent,
4126             Pair<Intent, ActivityOptions> options) {
4127         try {
4128             // TODO: Unregister this handler if PendingIntent.FLAG_ONE_SHOT?
4129             Context context = view.getContext();
4130             // The NEW_TASK flags are applied through the activity options and not as a part of
4131             // the call to startIntentSender() to ensure that they are consistently applied to
4132             // both mutable and immutable PendingIntents.
4133             context.startIntentSender(
4134                     pendingIntent.getIntentSender(), options.first,
4135                     0, 0, 0, options.second.toBundle());
4136         } catch (IntentSender.SendIntentException e) {
4137             Log.e(LOG_TAG, "Cannot send pending intent: ", e);
4138             return false;
4139         } catch (Exception e) {
4140             Log.e(LOG_TAG, "Cannot send pending intent due to unknown exception: ", e);
4141             return false;
4142         }
4143         return true;
4144     }
4145 }
4146