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