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