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