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