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