OSDN Git Service

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