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