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