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
17package android.widget;
18
19import android.annotation.ColorInt;
20import android.annotation.DimenRes;
21import android.app.ActivityManager.StackId;
22import android.app.ActivityOptions;
23import android.app.ActivityThread;
24import android.app.Application;
25import android.app.PendingIntent;
26import android.app.RemoteInput;
27import android.appwidget.AppWidgetHostView;
28import android.content.Context;
29import android.content.ContextWrapper;
30import android.content.Intent;
31import android.content.IntentSender;
32import android.content.pm.ApplicationInfo;
33import android.content.pm.PackageManager.NameNotFoundException;
34import android.content.res.ColorStateList;
35import android.content.res.Configuration;
36import android.content.res.Resources;
37import android.content.res.TypedArray;
38import android.graphics.Bitmap;
39import android.graphics.PorterDuff;
40import android.graphics.Rect;
41import android.graphics.drawable.Drawable;
42import android.graphics.drawable.Icon;
43import android.net.Uri;
44import android.os.AsyncTask;
45import android.os.Build;
46import android.os.Bundle;
47import android.os.CancellationSignal;
48import android.os.Parcel;
49import android.os.Parcelable;
50import android.os.StrictMode;
51import android.os.UserHandle;
52import android.text.TextUtils;
53import android.util.ArrayMap;
54import android.util.Log;
55import android.view.LayoutInflater;
56import android.view.LayoutInflater.Filter;
57import android.view.RemotableViewMethod;
58import android.view.View;
59import android.view.View.OnClickListener;
60import android.view.ViewGroup;
61import android.widget.AdapterView.OnItemClickListener;
62import libcore.util.Objects;
63
64import com.android.internal.R;
65import com.android.internal.util.Preconditions;
66
67import java.lang.annotation.ElementType;
68import java.lang.annotation.Retention;
69import java.lang.annotation.RetentionPolicy;
70import java.lang.annotation.Target;
71import java.lang.reflect.Method;
72import java.util.ArrayList;
73import java.util.HashMap;
74import 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 */
82public 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     * @hide
2792     * Equivalent to calling {@link android.widget.TextView#setTextColor(ColorStateList)}.
2793     *
2794     * @param viewId The id of the view whose text color should change
2795     * @param colors the text colors to set
2796     */
2797    public void setTextColor(int viewId, @ColorInt ColorStateList colors) {
2798        addAction(new ReflectionAction(viewId, "setTextColor", ReflectionAction.COLOR_STATE_LIST,
2799                colors));
2800    }
2801
2802    /**
2803     * Equivalent to calling {@link android.widget.AbsListView#setRemoteViewsAdapter(Intent)}.
2804     *
2805     * @param appWidgetId The id of the app widget which contains the specified view. (This
2806     *      parameter is ignored in this deprecated method)
2807     * @param viewId The id of the {@link AdapterView}
2808     * @param intent The intent of the service which will be
2809     *            providing data to the RemoteViewsAdapter
2810     * @deprecated This method has been deprecated. See
2811     *      {@link android.widget.RemoteViews#setRemoteAdapter(int, Intent)}
2812     */
2813    @Deprecated
2814    public void setRemoteAdapter(int appWidgetId, int viewId, Intent intent) {
2815        setRemoteAdapter(viewId, intent);
2816    }
2817
2818    /**
2819     * Equivalent to calling {@link android.widget.AbsListView#setRemoteViewsAdapter(Intent)}.
2820     * Can only be used for App Widgets.
2821     *
2822     * @param viewId The id of the {@link AdapterView}
2823     * @param intent The intent of the service which will be
2824     *            providing data to the RemoteViewsAdapter
2825     */
2826    public void setRemoteAdapter(int viewId, Intent intent) {
2827        addAction(new SetRemoteViewsAdapterIntent(viewId, intent));
2828    }
2829
2830    /**
2831     * Creates a simple Adapter for the viewId specified. The viewId must point to an AdapterView,
2832     * ie. {@link ListView}, {@link GridView}, {@link StackView} or {@link AdapterViewAnimator}.
2833     * This is a simpler but less flexible approach to populating collection widgets. Its use is
2834     * encouraged for most scenarios, as long as the total memory within the list of RemoteViews
2835     * is relatively small (ie. doesn't contain large or numerous Bitmaps, see {@link
2836     * RemoteViews#setImageViewBitmap}). In the case of numerous images, the use of API is still
2837     * possible by setting image URIs instead of Bitmaps, see {@link RemoteViews#setImageViewUri}.
2838     *
2839     * This API is supported in the compatibility library for previous API levels, see
2840     * RemoteViewsCompat.
2841     *
2842     * @param viewId The id of the {@link AdapterView}
2843     * @param list The list of RemoteViews which will populate the view specified by viewId.
2844     * @param viewTypeCount The maximum number of unique layout id's used to construct the list of
2845     *      RemoteViews. This count cannot change during the life-cycle of a given widget, so this
2846     *      parameter should account for the maximum possible number of types that may appear in the
2847     *      See {@link Adapter#getViewTypeCount()}.
2848     *
2849     * @hide
2850     */
2851    public void setRemoteAdapter(int viewId, ArrayList<RemoteViews> list, int viewTypeCount) {
2852        addAction(new SetRemoteViewsAdapterList(viewId, list, viewTypeCount));
2853    }
2854
2855    /**
2856     * Equivalent to calling {@link android.widget.AbsListView#smoothScrollToPosition(int, int)}.
2857     *
2858     * @param viewId The id of the view to change
2859     * @param position Scroll to this adapter position
2860     */
2861    public void setScrollPosition(int viewId, int position) {
2862        setInt(viewId, "smoothScrollToPosition", position);
2863    }
2864
2865    /**
2866     * Equivalent to calling {@link android.widget.AbsListView#smoothScrollToPosition(int, int)}.
2867     *
2868     * @param viewId The id of the view to change
2869     * @param offset Scroll by this adapter position offset
2870     */
2871    public void setRelativeScrollPosition(int viewId, int offset) {
2872        setInt(viewId, "smoothScrollByOffset", offset);
2873    }
2874
2875    /**
2876     * Equivalent to calling {@link android.view.View#setPadding(int, int, int, int)}.
2877     *
2878     * @param viewId The id of the view to change
2879     * @param left the left padding in pixels
2880     * @param top the top padding in pixels
2881     * @param right the right padding in pixels
2882     * @param bottom the bottom padding in pixels
2883     */
2884    public void setViewPadding(int viewId, int left, int top, int right, int bottom) {
2885        addAction(new ViewPaddingAction(viewId, left, top, right, bottom));
2886    }
2887
2888    /**
2889     * @hide
2890     * Equivalent to calling {@link android.view.ViewGroup.MarginLayoutParams#setMarginEnd(int)}.
2891     * Only works if the {@link View#getLayoutParams()} supports margins.
2892     * Hidden for now since we don't want to support this for all different layout margins yet.
2893     *
2894     * @param viewId The id of the view to change
2895     * @param endMarginDimen a dimen resource to read the margin from or 0 to clear the margin.
2896     */
2897    public void setViewLayoutMarginEndDimen(int viewId, @DimenRes int endMarginDimen) {
2898        addAction(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_MARGIN_END_DIMEN,
2899                endMarginDimen));
2900    }
2901
2902    /**
2903     * Equivalent to setting {@link android.view.ViewGroup.MarginLayoutParams#bottomMargin}.
2904     *
2905     * @param bottomMarginDimen a dimen resource to read the margin from or 0 to clear the margin.
2906     * @hide
2907     */
2908    public void setViewLayoutMarginBottomDimen(int viewId, @DimenRes int bottomMarginDimen) {
2909        addAction(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_MARGIN_BOTTOM_DIMEN,
2910                bottomMarginDimen));
2911    }
2912
2913    /**
2914     * Equivalent to setting {@link android.view.ViewGroup.LayoutParams#width}.
2915     *
2916     * @param layoutWidth one of 0, MATCH_PARENT or WRAP_CONTENT. Other sizes are not allowed
2917     *                    because they behave poorly when the density changes.
2918     * @hide
2919     */
2920    public void setViewLayoutWidth(int viewId, int layoutWidth) {
2921        if (layoutWidth != 0 && layoutWidth != ViewGroup.LayoutParams.MATCH_PARENT
2922                && layoutWidth != ViewGroup.LayoutParams.WRAP_CONTENT) {
2923            throw new IllegalArgumentException("Only supports 0, WRAP_CONTENT and MATCH_PARENT");
2924        }
2925        mActions.add(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_WIDTH, layoutWidth));
2926    }
2927
2928    /**
2929     * Call a method taking one boolean on a view in the layout for this RemoteViews.
2930     *
2931     * @param viewId The id of the view on which to call the method.
2932     * @param methodName The name of the method to call.
2933     * @param value The value to pass to the method.
2934     */
2935    public void setBoolean(int viewId, String methodName, boolean value) {
2936        addAction(new ReflectionAction(viewId, methodName, ReflectionAction.BOOLEAN, value));
2937    }
2938
2939    /**
2940     * Call a method taking one byte on a view in the layout for this RemoteViews.
2941     *
2942     * @param viewId The id of the view on which to call the method.
2943     * @param methodName The name of the method to call.
2944     * @param value The value to pass to the method.
2945     */
2946    public void setByte(int viewId, String methodName, byte value) {
2947        addAction(new ReflectionAction(viewId, methodName, ReflectionAction.BYTE, value));
2948    }
2949
2950    /**
2951     * Call a method taking one short on a view in the layout for this RemoteViews.
2952     *
2953     * @param viewId The id of the view on which to call the method.
2954     * @param methodName The name of the method to call.
2955     * @param value The value to pass to the method.
2956     */
2957    public void setShort(int viewId, String methodName, short value) {
2958        addAction(new ReflectionAction(viewId, methodName, ReflectionAction.SHORT, value));
2959    }
2960
2961    /**
2962     * Call a method taking one int on a view in the layout for this RemoteViews.
2963     *
2964     * @param viewId The id of the view on which to call the method.
2965     * @param methodName The name of the method to call.
2966     * @param value The value to pass to the method.
2967     */
2968    public void setInt(int viewId, String methodName, int value) {
2969        addAction(new ReflectionAction(viewId, methodName, ReflectionAction.INT, value));
2970    }
2971
2972    /**
2973     * Call a method taking one long on a view in the layout for this RemoteViews.
2974     *
2975     * @param viewId The id of the view on which to call the method.
2976     * @param methodName The name of the method to call.
2977     * @param value The value to pass to the method.
2978     */
2979    public void setLong(int viewId, String methodName, long value) {
2980        addAction(new ReflectionAction(viewId, methodName, ReflectionAction.LONG, value));
2981    }
2982
2983    /**
2984     * Call a method taking one float on a view in the layout for this RemoteViews.
2985     *
2986     * @param viewId The id of the view on which to call the method.
2987     * @param methodName The name of the method to call.
2988     * @param value The value to pass to the method.
2989     */
2990    public void setFloat(int viewId, String methodName, float value) {
2991        addAction(new ReflectionAction(viewId, methodName, ReflectionAction.FLOAT, value));
2992    }
2993
2994    /**
2995     * Call a method taking one double on a view in the layout for this RemoteViews.
2996     *
2997     * @param viewId The id of the view on which to call the method.
2998     * @param methodName The name of the method to call.
2999     * @param value The value to pass to the method.
3000     */
3001    public void setDouble(int viewId, String methodName, double value) {
3002        addAction(new ReflectionAction(viewId, methodName, ReflectionAction.DOUBLE, value));
3003    }
3004
3005    /**
3006     * Call a method taking one char on a view in the layout for this RemoteViews.
3007     *
3008     * @param viewId The id of the view on which to call the method.
3009     * @param methodName The name of the method to call.
3010     * @param value The value to pass to the method.
3011     */
3012    public void setChar(int viewId, String methodName, char value) {
3013        addAction(new ReflectionAction(viewId, methodName, ReflectionAction.CHAR, value));
3014    }
3015
3016    /**
3017     * Call a method taking one String on a view in the layout for this RemoteViews.
3018     *
3019     * @param viewId The id of the view on which to call the method.
3020     * @param methodName The name of the method to call.
3021     * @param value The value to pass to the method.
3022     */
3023    public void setString(int viewId, String methodName, String value) {
3024        addAction(new ReflectionAction(viewId, methodName, ReflectionAction.STRING, value));
3025    }
3026
3027    /**
3028     * Call a method taking one CharSequence on a view in the layout for this RemoteViews.
3029     *
3030     * @param viewId The id of the view on which to call the method.
3031     * @param methodName The name of the method to call.
3032     * @param value The value to pass to the method.
3033     */
3034    public void setCharSequence(int viewId, String methodName, CharSequence value) {
3035        addAction(new ReflectionAction(viewId, methodName, ReflectionAction.CHAR_SEQUENCE, value));
3036    }
3037
3038    /**
3039     * Call a method taking one Uri on a view in the layout for this RemoteViews.
3040     *
3041     * @param viewId The id of the view on which to call the method.
3042     * @param methodName The name of the method to call.
3043     * @param value The value to pass to the method.
3044     */
3045    public void setUri(int viewId, String methodName, Uri value) {
3046        if (value != null) {
3047            // Resolve any filesystem path before sending remotely
3048            value = value.getCanonicalUri();
3049            if (StrictMode.vmFileUriExposureEnabled()) {
3050                value.checkFileUriExposed("RemoteViews.setUri()");
3051            }
3052        }
3053        addAction(new ReflectionAction(viewId, methodName, ReflectionAction.URI, value));
3054    }
3055
3056    /**
3057     * Call a method taking one Bitmap on a view in the layout for this RemoteViews.
3058     * @more
3059     * <p class="note">The bitmap will be flattened into the parcel if this object is
3060     * sent across processes, so it may end up using a lot of memory, and may be fairly slow.</p>
3061     *
3062     * @param viewId The id of the view on which to call the method.
3063     * @param methodName The name of the method to call.
3064     * @param value The value to pass to the method.
3065     */
3066    public void setBitmap(int viewId, String methodName, Bitmap value) {
3067        addAction(new BitmapReflectionAction(viewId, methodName, value));
3068    }
3069
3070    /**
3071     * Call a method taking one Bundle on a view in the layout for this RemoteViews.
3072     *
3073     * @param viewId The id of the view on which to call the method.
3074     * @param methodName The name of the method to call.
3075     * @param value The value to pass to the method.
3076     */
3077    public void setBundle(int viewId, String methodName, Bundle value) {
3078        addAction(new ReflectionAction(viewId, methodName, ReflectionAction.BUNDLE, value));
3079    }
3080
3081    /**
3082     * Call a method taking one Intent on a view in the layout for this RemoteViews.
3083     *
3084     * @param viewId The id of the view on which to call the method.
3085     * @param methodName The name of the method to call.
3086     * @param value The {@link android.content.Intent} to pass the method.
3087     */
3088    public void setIntent(int viewId, String methodName, Intent value) {
3089        addAction(new ReflectionAction(viewId, methodName, ReflectionAction.INTENT, value));
3090    }
3091
3092    /**
3093     * Call a method taking one Icon on a view in the layout for this RemoteViews.
3094     *
3095     * @param viewId The id of the view on which to call the method.
3096     * @param methodName The name of the method to call.
3097     * @param value The {@link android.graphics.drawable.Icon} to pass the method.
3098     */
3099    public void setIcon(int viewId, String methodName, Icon value) {
3100        addAction(new ReflectionAction(viewId, methodName, ReflectionAction.ICON, value));
3101    }
3102
3103    /**
3104     * Equivalent to calling View.setContentDescription(CharSequence).
3105     *
3106     * @param viewId The id of the view whose content description should change.
3107     * @param contentDescription The new content description for the view.
3108     */
3109    public void setContentDescription(int viewId, CharSequence contentDescription) {
3110        setCharSequence(viewId, "setContentDescription", contentDescription);
3111    }
3112
3113    /**
3114     * Equivalent to calling {@link android.view.View#setAccessibilityTraversalBefore(int)}.
3115     *
3116     * @param viewId The id of the view whose before view in accessibility traversal to set.
3117     * @param nextId The id of the next in the accessibility traversal.
3118     **/
3119    public void setAccessibilityTraversalBefore(int viewId, int nextId) {
3120        setInt(viewId, "setAccessibilityTraversalBefore", nextId);
3121    }
3122
3123    /**
3124     * Equivalent to calling {@link android.view.View#setAccessibilityTraversalAfter(int)}.
3125     *
3126     * @param viewId The id of the view whose after view in accessibility traversal to set.
3127     * @param nextId The id of the next in the accessibility traversal.
3128     **/
3129    public void setAccessibilityTraversalAfter(int viewId, int nextId) {
3130        setInt(viewId, "setAccessibilityTraversalAfter", nextId);
3131    }
3132
3133    /**
3134     * Equivalent to calling View.setLabelFor(int).
3135     *
3136     * @param viewId The id of the view whose property to set.
3137     * @param labeledId The id of a view for which this view serves as a label.
3138     */
3139    public void setLabelFor(int viewId, int labeledId) {
3140        setInt(viewId, "setLabelFor", labeledId);
3141    }
3142
3143    private RemoteViews getRemoteViewsToApply(Context context) {
3144        if (hasLandscapeAndPortraitLayouts()) {
3145            int orientation = context.getResources().getConfiguration().orientation;
3146            if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
3147                return mLandscape;
3148            } else {
3149                return mPortrait;
3150            }
3151        }
3152        return this;
3153    }
3154
3155    /**
3156     * Inflates the view hierarchy represented by this object and applies
3157     * all of the actions.
3158     *
3159     * <p><strong>Caller beware: this may throw</strong>
3160     *
3161     * @param context Default context to use
3162     * @param parent Parent that the resulting view hierarchy will be attached to. This method
3163     * does <strong>not</strong> attach the hierarchy. The caller should do so when appropriate.
3164     * @return The inflated view hierarchy
3165     */
3166    public View apply(Context context, ViewGroup parent) {
3167        return apply(context, parent, null);
3168    }
3169
3170    /** @hide */
3171    public View apply(Context context, ViewGroup parent, OnClickHandler handler) {
3172        RemoteViews rvToApply = getRemoteViewsToApply(context);
3173
3174        View result = inflateView(context, rvToApply, parent);
3175        loadTransitionOverride(context, handler);
3176
3177        rvToApply.performApply(result, parent, handler);
3178
3179        return result;
3180    }
3181
3182    private View inflateView(Context context, RemoteViews rv, ViewGroup parent) {
3183        // RemoteViews may be built by an application installed in another
3184        // user. So build a context that loads resources from that user but
3185        // still returns the current users userId so settings like data / time formats
3186        // are loaded without requiring cross user persmissions.
3187        final Context contextForResources = getContextForResources(context);
3188        Context inflationContext = new ContextWrapper(context) {
3189            @Override
3190            public Resources getResources() {
3191                return contextForResources.getResources();
3192            }
3193            @Override
3194            public Resources.Theme getTheme() {
3195                return contextForResources.getTheme();
3196            }
3197            @Override
3198            public String getPackageName() {
3199                return contextForResources.getPackageName();
3200            }
3201        };
3202
3203        LayoutInflater inflater = (LayoutInflater)
3204                context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
3205
3206        // Clone inflater so we load resources from correct context and
3207        // we don't add a filter to the static version returned by getSystemService.
3208        inflater = inflater.cloneInContext(inflationContext);
3209        inflater.setFilter(this);
3210        View v = inflater.inflate(rv.getLayoutId(), parent, false);
3211        v.setTagInternal(R.id.widget_frame, rv.getLayoutId());
3212        return v;
3213    }
3214
3215    private static void loadTransitionOverride(Context context,
3216            RemoteViews.OnClickHandler handler) {
3217        if (handler != null && context.getResources().getBoolean(
3218                com.android.internal.R.bool.config_overrideRemoteViewsActivityTransition)) {
3219            TypedArray windowStyle = context.getTheme().obtainStyledAttributes(
3220                    com.android.internal.R.styleable.Window);
3221            int windowAnimations = windowStyle.getResourceId(
3222                    com.android.internal.R.styleable.Window_windowAnimationStyle, 0);
3223            TypedArray windowAnimationStyle = context.obtainStyledAttributes(
3224                    windowAnimations, com.android.internal.R.styleable.WindowAnimation);
3225            handler.setEnterAnimationId(windowAnimationStyle.getResourceId(
3226                    com.android.internal.R.styleable.
3227                            WindowAnimation_activityOpenRemoteViewsEnterAnimation, 0));
3228            windowStyle.recycle();
3229            windowAnimationStyle.recycle();
3230        }
3231    }
3232
3233    /**
3234     * Implement this interface to receive a callback when
3235     * {@link #applyAsync} or {@link #reapplyAsync} is finished.
3236     * @hide
3237     */
3238    public interface OnViewAppliedListener {
3239        void onViewApplied(View v);
3240
3241        void onError(Exception e);
3242    }
3243
3244    /**
3245     * Applies the views asynchronously, moving as much of the task on the background
3246     * thread as possible.
3247     *
3248     * @see {@link #apply(Context, ViewGroup)}
3249     * @param context Default context to use
3250     * @param parent Parent that the resulting view hierarchy will be attached to. This method
3251     * does <strong>not</strong> attach the hierarchy. The caller should do so when appropriate.
3252     * @param listener the callback to run when all actions have been applied. May be null.
3253     * @param executor The executor to use. If null {@link AsyncTask#THREAD_POOL_EXECUTOR} is used.
3254     * @return CancellationSignal
3255     * @hide
3256     */
3257    public CancellationSignal applyAsync(
3258            Context context, ViewGroup parent, Executor executor, OnViewAppliedListener listener) {
3259        return applyAsync(context, parent, executor, listener, null);
3260    }
3261
3262    private CancellationSignal startTaskOnExecutor(AsyncApplyTask task, Executor executor) {
3263        CancellationSignal cancelSignal = new CancellationSignal();
3264        cancelSignal.setOnCancelListener(task);
3265
3266        task.executeOnExecutor(executor == null ? AsyncTask.THREAD_POOL_EXECUTOR : executor);
3267        return cancelSignal;
3268    }
3269
3270    /** @hide */
3271    public CancellationSignal applyAsync(Context context, ViewGroup parent,
3272            Executor executor, OnViewAppliedListener listener, OnClickHandler handler) {
3273        return startTaskOnExecutor(getAsyncApplyTask(context, parent, listener, handler), executor);
3274    }
3275
3276    private AsyncApplyTask getAsyncApplyTask(Context context, ViewGroup parent,
3277            OnViewAppliedListener listener, OnClickHandler handler) {
3278        return new AsyncApplyTask(getRemoteViewsToApply(context), parent, context, listener,
3279                handler, null);
3280    }
3281
3282    private class AsyncApplyTask extends AsyncTask<Void, Void, ViewTree>
3283            implements CancellationSignal.OnCancelListener {
3284        final RemoteViews mRV;
3285        final ViewGroup mParent;
3286        final Context mContext;
3287        final OnViewAppliedListener mListener;
3288        final OnClickHandler mHandler;
3289
3290        private View mResult;
3291        private ViewTree mTree;
3292        private Action[] mActions;
3293        private Exception mError;
3294
3295        private AsyncApplyTask(
3296                RemoteViews rv, ViewGroup parent, Context context, OnViewAppliedListener listener,
3297                OnClickHandler handler, View result) {
3298            mRV = rv;
3299            mParent = parent;
3300            mContext = context;
3301            mListener = listener;
3302            mHandler = handler;
3303
3304            mResult = result;
3305            loadTransitionOverride(context, handler);
3306        }
3307
3308        @Override
3309        protected ViewTree doInBackground(Void... params) {
3310            try {
3311                if (mResult == null) {
3312                    mResult = inflateView(mContext, mRV, mParent);
3313                }
3314
3315                mTree = new ViewTree(mResult);
3316                if (mRV.mActions != null) {
3317                    int count = mRV.mActions.size();
3318                    mActions = new Action[count];
3319                    for (int i = 0; i < count && !isCancelled(); i++) {
3320                        // TODO: check if isCanclled in nested views.
3321                        mActions[i] = mRV.mActions.get(i).initActionAsync(mTree, mParent, mHandler);
3322                    }
3323                } else {
3324                    mActions = null;
3325                }
3326                return mTree;
3327            } catch (Exception e) {
3328                mError = e;
3329                return null;
3330            }
3331        }
3332
3333        @Override
3334        protected void onPostExecute(ViewTree viewTree) {
3335            if (mError == null) {
3336                try {
3337                    if (mActions != null) {
3338                        OnClickHandler handler = mHandler == null
3339                                ? DEFAULT_ON_CLICK_HANDLER : mHandler;
3340                        for (Action a : mActions) {
3341                            a.apply(viewTree.mRoot, mParent, handler);
3342                        }
3343                    }
3344                } catch (Exception e) {
3345                    mError = e;
3346                }
3347            }
3348
3349            if (mListener != null) {
3350                if (mError != null) {
3351                    mListener.onError(mError);
3352                } else {
3353                    mListener.onViewApplied(viewTree.mRoot);
3354                }
3355            } else if (mError != null) {
3356                if (mError instanceof ActionException) {
3357                    throw (ActionException) mError;
3358                } else {
3359                    throw new ActionException(mError);
3360                }
3361            }
3362        }
3363
3364        @Override
3365        public void onCancel() {
3366            cancel(true);
3367        }
3368    }
3369
3370    /**
3371     * Applies all of the actions to the provided view.
3372     *
3373     * <p><strong>Caller beware: this may throw</strong>
3374     *
3375     * @param v The view to apply the actions to.  This should be the result of
3376     * the {@link #apply(Context,ViewGroup)} call.
3377     */
3378    public void reapply(Context context, View v) {
3379        reapply(context, v, null);
3380    }
3381
3382    /** @hide */
3383    public void reapply(Context context, View v, OnClickHandler handler) {
3384        RemoteViews rvToApply = getRemoteViewsToApply(context);
3385
3386        // In the case that a view has this RemoteViews applied in one orientation, is persisted
3387        // across orientation change, and has the RemoteViews re-applied in the new orientation,
3388        // we throw an exception, since the layouts may be completely unrelated.
3389        if (hasLandscapeAndPortraitLayouts()) {
3390            if ((Integer) v.getTag(R.id.widget_frame) != rvToApply.getLayoutId()) {
3391                throw new RuntimeException("Attempting to re-apply RemoteViews to a view that" +
3392                        " that does not share the same root layout id.");
3393            }
3394        }
3395
3396        rvToApply.performApply(v, (ViewGroup) v.getParent(), handler);
3397    }
3398
3399    /**
3400     * Applies all the actions to the provided view, moving as much of the task on the background
3401     * thread as possible.
3402     *
3403     * @see {@link #reapply(Context, View)}
3404     * @param context Default context to use
3405     * @param v The view to apply the actions to.  This should be the result of
3406     * the {@link #apply(Context,ViewGroup)} call.
3407     * @param listener the callback to run when all actions have been applied. May be null.
3408     * @param executor The executor to use. If null {@link AsyncTask#THREAD_POOL_EXECUTOR} is used
3409     * @return CancellationSignal
3410     * @hide
3411     */
3412    public CancellationSignal reapplyAsync(
3413            Context context, View v, Executor executor, OnViewAppliedListener listener) {
3414        return reapplyAsync(context, v, executor, listener, null);
3415    }
3416
3417    /** @hide */
3418    public CancellationSignal reapplyAsync(Context context, View v, Executor executor,
3419            OnViewAppliedListener listener, OnClickHandler handler) {
3420        RemoteViews rvToApply = getRemoteViewsToApply(context);
3421
3422        // In the case that a view has this RemoteViews applied in one orientation, is persisted
3423        // across orientation change, and has the RemoteViews re-applied in the new orientation,
3424        // we throw an exception, since the layouts may be completely unrelated.
3425        if (hasLandscapeAndPortraitLayouts()) {
3426            if ((Integer) v.getTag(R.id.widget_frame) != rvToApply.getLayoutId()) {
3427                throw new RuntimeException("Attempting to re-apply RemoteViews to a view that" +
3428                        " that does not share the same root layout id.");
3429            }
3430        }
3431
3432        return startTaskOnExecutor(new AsyncApplyTask(rvToApply, (ViewGroup) v.getParent(),
3433                context, listener, handler, v), executor);
3434    }
3435
3436    private void performApply(View v, ViewGroup parent, OnClickHandler handler) {
3437        if (mActions != null) {
3438            handler = handler == null ? DEFAULT_ON_CLICK_HANDLER : handler;
3439            final int count = mActions.size();
3440            for (int i = 0; i < count; i++) {
3441                Action a = mActions.get(i);
3442                a.apply(v, parent, handler);
3443            }
3444        }
3445    }
3446
3447    private Context getContextForResources(Context context) {
3448        if (mApplication != null) {
3449            if (context.getUserId() == UserHandle.getUserId(mApplication.uid)
3450                    && context.getPackageName().equals(mApplication.packageName)) {
3451                return context;
3452            }
3453            try {
3454                return context.createApplicationContext(mApplication,
3455                        Context.CONTEXT_RESTRICTED);
3456            } catch (NameNotFoundException e) {
3457                Log.e(LOG_TAG, "Package name " + mApplication.packageName + " not found");
3458            }
3459        }
3460
3461        return context;
3462    }
3463
3464    /**
3465     * Returns the number of actions in this RemoteViews. Can be used as a sequence number.
3466     *
3467     * @hide
3468     */
3469    public int getSequenceNumber() {
3470        return (mActions == null) ? 0 : mActions.size();
3471    }
3472
3473    /* (non-Javadoc)
3474     * Used to restrict the views which can be inflated
3475     *
3476     * @see android.view.LayoutInflater.Filter#onLoadClass(java.lang.Class)
3477     */
3478    public boolean onLoadClass(Class clazz) {
3479        return clazz.isAnnotationPresent(RemoteView.class);
3480    }
3481
3482    public int describeContents() {
3483        return 0;
3484    }
3485
3486    public void writeToParcel(Parcel dest, int flags) {
3487        if (!hasLandscapeAndPortraitLayouts()) {
3488            dest.writeInt(MODE_NORMAL);
3489            // We only write the bitmap cache if we are the root RemoteViews, as this cache
3490            // is shared by all children.
3491            if (mIsRoot) {
3492                mBitmapCache.writeBitmapsToParcel(dest, flags);
3493            }
3494            dest.writeParcelable(mApplication, flags);
3495            dest.writeInt(mLayoutId);
3496            dest.writeInt(mIsWidgetCollectionChild ? 1 : 0);
3497            int count;
3498            if (mActions != null) {
3499                count = mActions.size();
3500            } else {
3501                count = 0;
3502            }
3503            dest.writeInt(count);
3504            for (int i=0; i<count; i++) {
3505                Action a = mActions.get(i);
3506                a.writeToParcel(dest, 0);
3507            }
3508        } else {
3509            dest.writeInt(MODE_HAS_LANDSCAPE_AND_PORTRAIT);
3510            // We only write the bitmap cache if we are the root RemoteViews, as this cache
3511            // is shared by all children.
3512            if (mIsRoot) {
3513                mBitmapCache.writeBitmapsToParcel(dest, flags);
3514            }
3515            mLandscape.writeToParcel(dest, flags);
3516            mPortrait.writeToParcel(dest, flags);
3517        }
3518    }
3519
3520    private static ApplicationInfo getApplicationInfo(String packageName, int userId) {
3521        if (packageName == null) {
3522            return null;
3523        }
3524
3525        // Get the application for the passed in package and user.
3526        Application application = ActivityThread.currentApplication();
3527        if (application == null) {
3528            throw new IllegalStateException("Cannot create remote views out of an aplication.");
3529        }
3530
3531        ApplicationInfo applicationInfo = application.getApplicationInfo();
3532        if (UserHandle.getUserId(applicationInfo.uid) != userId
3533                || !applicationInfo.packageName.equals(packageName)) {
3534            try {
3535                Context context = application.getBaseContext().createPackageContextAsUser(
3536                        packageName, 0, new UserHandle(userId));
3537                applicationInfo = context.getApplicationInfo();
3538            } catch (NameNotFoundException nnfe) {
3539                throw new IllegalArgumentException("No such package " + packageName);
3540            }
3541        }
3542
3543        return applicationInfo;
3544    }
3545
3546    /**
3547     * Parcelable.Creator that instantiates RemoteViews objects
3548     */
3549    public static final Parcelable.Creator<RemoteViews> CREATOR = new Parcelable.Creator<RemoteViews>() {
3550        public RemoteViews createFromParcel(Parcel parcel) {
3551            return new RemoteViews(parcel);
3552        }
3553
3554        public RemoteViews[] newArray(int size) {
3555            return new RemoteViews[size];
3556        }
3557    };
3558
3559    /**
3560     * A representation of the view hierarchy. Only views which have a valid ID are added
3561     * and can be searched.
3562     */
3563    private static class ViewTree {
3564        private final View mRoot;
3565
3566        private ArrayList<ViewTree> mChildren;
3567
3568        private ViewTree(View root) {
3569            mRoot = root;
3570        }
3571
3572        public void createTree() {
3573            if (mChildren != null) {
3574                return;
3575            }
3576
3577            mChildren = new ArrayList<>();
3578            if (mRoot instanceof ViewGroup && mRoot.isRootNamespace()) {
3579                ViewGroup vg = (ViewGroup) mRoot;
3580                int count = vg.getChildCount();
3581                for (int i = 0; i < count; i++) {
3582                    addViewChild(vg.getChildAt(i));
3583                }
3584            }
3585        }
3586
3587        public ViewTree findViewTreeById(int id) {
3588            if (mRoot.getId() == id) {
3589                return this;
3590            }
3591            if (mChildren == null) {
3592                return null;
3593            }
3594            for (ViewTree tree : mChildren) {
3595                ViewTree result = tree.findViewTreeById(id);
3596                if (result != null) {
3597                    return result;
3598                }
3599            }
3600            return null;
3601        }
3602
3603        public View findViewById(int id) {
3604            if (mChildren == null) {
3605                return mRoot.findViewById(id);
3606            }
3607            ViewTree tree = findViewTreeById(id);
3608            return tree == null ? null : tree.mRoot;
3609        }
3610
3611        public void addChild(ViewTree child) {
3612            if (mChildren == null) {
3613                mChildren = new ArrayList<>();
3614            }
3615            child.createTree();
3616            mChildren.add(child);
3617        }
3618
3619        private void addViewChild(View v) {
3620            final ViewTree target;
3621
3622            // If the view has a valid id, i.e., if can be found using findViewById, add it to the
3623            // tree, otherwise skip this view and add its children instead.
3624            if (v.getId() != 0) {
3625                ViewTree tree = new ViewTree(v);
3626                mChildren.add(tree);
3627                target = tree;
3628            } else {
3629                target = this;
3630            }
3631
3632            if (v instanceof ViewGroup && v.isRootNamespace()) {
3633                if (target.mChildren == null) {
3634                    target.mChildren = new ArrayList<>();
3635                    ViewGroup vg = (ViewGroup) v;
3636                    int count = vg.getChildCount();
3637                    for (int i = 0; i < count; i++) {
3638                        target.addViewChild(vg.getChildAt(i));
3639                    }
3640                }
3641            }
3642        }
3643    }
3644}
3645