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.app.ActivityOptions;
     20 import android.app.ActivityThread;
     21 import android.app.Application;
     22 import android.app.PendingIntent;
     23 import android.appwidget.AppWidgetHostView;
     24 import android.content.Context;
     25 import android.content.ContextWrapper;
     26 import android.content.Intent;
     27 import android.content.IntentSender;
     28 import android.content.pm.ApplicationInfo;
     29 import android.content.pm.PackageManager.NameNotFoundException;
     30 import android.content.res.ColorStateList;
     31 import android.content.res.Configuration;
     32 import android.content.res.Resources;
     33 import android.graphics.Bitmap;
     34 import android.graphics.PorterDuff;
     35 import android.graphics.Rect;
     36 import android.graphics.drawable.Drawable;
     37 import android.net.Uri;
     38 import android.os.Build;
     39 import android.os.Bundle;
     40 import android.os.Parcel;
     41 import android.os.Parcelable;
     42 import android.os.StrictMode;
     43 import android.os.UserHandle;
     44 import android.text.TextUtils;
     45 import android.util.ArrayMap;
     46 import android.util.Log;
     47 import android.view.LayoutInflater;
     48 import android.view.LayoutInflater.Filter;
     49 import android.view.RemotableViewMethod;
     50 import android.view.View;
     51 import android.view.View.OnClickListener;
     52 import android.view.ViewGroup;
     53 import android.widget.AdapterView.OnItemClickListener;
     54 import libcore.util.Objects;
     55 
     56 import java.lang.annotation.ElementType;
     57 import java.lang.annotation.Retention;
     58 import java.lang.annotation.RetentionPolicy;
     59 import java.lang.annotation.Target;
     60 import java.lang.reflect.Method;
     61 import java.util.ArrayList;
     62 import java.util.HashMap;
     63 
     64 /**
     65  * A class that describes a view hierarchy that can be displayed in
     66  * another process. The hierarchy is inflated from a layout resource
     67  * file, and this class provides some basic operations for modifying
     68  * the content of the inflated hierarchy.
     69  */
     70 public class RemoteViews implements Parcelable, Filter {
     71 
     72     private static final String LOG_TAG = "RemoteViews";
     73 
     74     /**
     75      * The intent extra that contains the appWidgetId.
     76      * @hide
     77      */
     78     static final String EXTRA_REMOTEADAPTER_APPWIDGET_ID = "remoteAdapterAppWidgetId";
     79 
     80     /**
     81      * Application that hosts the remote views.
     82      *
     83      * @hide
     84      */
     85     private ApplicationInfo mApplication;
     86 
     87     /**
     88      * The resource ID of the layout file. (Added to the parcel)
     89      */
     90     private final int mLayoutId;
     91 
     92     /**
     93      * An array of actions to perform on the view tree once it has been
     94      * inflated
     95      */
     96     private ArrayList<Action> mActions;
     97 
     98     /**
     99      * A class to keep track of memory usage by this RemoteViews
    100      */
    101     private MemoryUsageCounter mMemoryUsageCounter;
    102 
    103     /**
    104      * Maps bitmaps to unique indicies to avoid Bitmap duplication.
    105      */
    106     private BitmapCache mBitmapCache;
    107 
    108     /**
    109      * Indicates whether or not this RemoteViews object is contained as a child of any other
    110      * RemoteViews.
    111      */
    112     private boolean mIsRoot = true;
    113 
    114     /**
    115      * Constants to whether or not this RemoteViews is composed of a landscape and portrait
    116      * RemoteViews.
    117      */
    118     private static final int MODE_NORMAL = 0;
    119     private static final int MODE_HAS_LANDSCAPE_AND_PORTRAIT = 1;
    120 
    121     /**
    122      * Used in conjunction with the special constructor
    123      * {@link #RemoteViews(RemoteViews, RemoteViews)} to keep track of the landscape and portrait
    124      * RemoteViews.
    125      */
    126     private RemoteViews mLandscape = null;
    127     private RemoteViews mPortrait = null;
    128 
    129     /**
    130      * This flag indicates whether this RemoteViews object is being created from a
    131      * RemoteViewsService for use as a child of a widget collection. This flag is used
    132      * to determine whether or not certain features are available, in particular,
    133      * setting on click extras and setting on click pending intents. The former is enabled,
    134      * and the latter disabled when this flag is true.
    135      */
    136     private boolean mIsWidgetCollectionChild = false;
    137 
    138     private static final OnClickHandler DEFAULT_ON_CLICK_HANDLER = new OnClickHandler();
    139 
    140     private static final Object[] sMethodsLock = new Object[0];
    141     private static final ArrayMap<Class<? extends View>, ArrayMap<MutablePair<String, Class<?>>, Method>> sMethods =
    142             new ArrayMap<Class<? extends View>, ArrayMap<MutablePair<String, Class<?>>, Method>>();
    143     private static final ThreadLocal<Object[]> sInvokeArgsTls = new ThreadLocal<Object[]>() {
    144         @Override
    145         protected Object[] initialValue() {
    146             return new Object[1];
    147         }
    148     };
    149 
    150     /**
    151      * Handle with care!
    152      */
    153     static class MutablePair<F, S> {
    154         F first;
    155         S second;
    156 
    157         MutablePair(F first, S second) {
    158             this.first = first;
    159             this.second = second;
    160         }
    161 
    162         @Override
    163         public boolean equals(Object o) {
    164             if (!(o instanceof MutablePair)) {
    165                 return false;
    166             }
    167             MutablePair<?, ?> p = (MutablePair<?, ?>) o;
    168             return Objects.equal(p.first, first) && Objects.equal(p.second, second);
    169         }
    170 
    171         @Override
    172         public int hashCode() {
    173             return (first == null ? 0 : first.hashCode()) ^ (second == null ? 0 : second.hashCode());
    174         }
    175     }
    176 
    177     /**
    178      * This pair is used to perform lookups in sMethods without causing allocations.
    179      */
    180     private final MutablePair<String, Class<?>> mPair =
    181             new MutablePair<String, Class<?>>(null, null);
    182 
    183     /**
    184      * This annotation indicates that a subclass of View is alllowed to be used
    185      * with the {@link RemoteViews} mechanism.
    186      */
    187     @Target({ ElementType.TYPE })
    188     @Retention(RetentionPolicy.RUNTIME)
    189     public @interface RemoteView {
    190     }
    191 
    192     /**
    193      * Exception to send when something goes wrong executing an action
    194      *
    195      */
    196     public static class ActionException extends RuntimeException {
    197         public ActionException(Exception ex) {
    198             super(ex);
    199         }
    200         public ActionException(String message) {
    201             super(message);
    202         }
    203     }
    204 
    205     /** @hide */
    206     public static class OnClickHandler {
    207         public boolean onClickHandler(View view, PendingIntent pendingIntent,
    208                 Intent fillInIntent) {
    209             try {
    210                 // TODO: Unregister this handler if PendingIntent.FLAG_ONE_SHOT?
    211                 Context context = view.getContext();
    212                 ActivityOptions opts = ActivityOptions.makeScaleUpAnimation(view,
    213                         0, 0,
    214                         view.getMeasuredWidth(), view.getMeasuredHeight());
    215                 context.startIntentSender(
    216                         pendingIntent.getIntentSender(), fillInIntent,
    217                         Intent.FLAG_ACTIVITY_NEW_TASK,
    218                         Intent.FLAG_ACTIVITY_NEW_TASK, 0, opts.toBundle());
    219             } catch (IntentSender.SendIntentException e) {
    220                 android.util.Log.e(LOG_TAG, "Cannot send pending intent: ", e);
    221                 return false;
    222             } catch (Exception e) {
    223                 android.util.Log.e(LOG_TAG, "Cannot send pending intent due to " +
    224                         "unknown exception: ", e);
    225                 return false;
    226             }
    227             return true;
    228         }
    229     }
    230 
    231     /**
    232      * Base class for all actions that can be performed on an
    233      * inflated view.
    234      *
    235      *  SUBCLASSES MUST BE IMMUTABLE SO CLONE WORKS!!!!!
    236      */
    237     private abstract static class Action implements Parcelable {
    238         public abstract void apply(View root, ViewGroup rootParent,
    239                 OnClickHandler handler) throws ActionException;
    240 
    241         public static final int MERGE_REPLACE = 0;
    242         public static final int MERGE_APPEND = 1;
    243         public static final int MERGE_IGNORE = 2;
    244 
    245         public int describeContents() {
    246             return 0;
    247         }
    248 
    249         /**
    250          * Overridden by each class to report on it's own memory usage
    251          */
    252         public void updateMemoryUsageEstimate(MemoryUsageCounter counter) {
    253             // We currently only calculate Bitmap memory usage, so by default,
    254             // don't do anything here
    255         }
    256 
    257         public void setBitmapCache(BitmapCache bitmapCache) {
    258             // Do nothing
    259         }
    260 
    261         public int mergeBehavior() {
    262             return MERGE_REPLACE;
    263         }
    264 
    265         public abstract String getActionName();
    266 
    267         public String getUniqueKey() {
    268             return (getActionName() + viewId);
    269         }
    270 
    271         int viewId;
    272     }
    273 
    274     /**
    275      * Merges the passed RemoteViews actions with this RemoteViews actions according to
    276      * action-specific merge rules.
    277      *
    278      * @param newRv
    279      *
    280      * @hide
    281      */
    282     public void mergeRemoteViews(RemoteViews newRv) {
    283         if (newRv == null) return;
    284         // We first copy the new RemoteViews, as the process of merging modifies the way the actions
    285         // reference the bitmap cache. We don't want to modify the object as it may need to
    286         // be merged and applied multiple times.
    287         RemoteViews copy = newRv.clone();
    288 
    289         HashMap<String, Action> map = new HashMap<String, Action>();
    290         if (mActions == null) {
    291             mActions = new ArrayList<Action>();
    292         }
    293 
    294         int count = mActions.size();
    295         for (int i = 0; i < count; i++) {
    296             Action a = mActions.get(i);
    297             map.put(a.getUniqueKey(), a);
    298         }
    299 
    300         ArrayList<Action> newActions = copy.mActions;
    301         if (newActions == null) return;
    302         count = newActions.size();
    303         for (int i = 0; i < count; i++) {
    304             Action a = newActions.get(i);
    305             String key = newActions.get(i).getUniqueKey();
    306             int mergeBehavior = newActions.get(i).mergeBehavior();
    307             if (map.containsKey(key) && mergeBehavior == Action.MERGE_REPLACE) {
    308                 mActions.remove(map.get(key));
    309                 map.remove(key);
    310             }
    311 
    312             // If the merge behavior is ignore, we don't bother keeping the extra action
    313             if (mergeBehavior == Action.MERGE_REPLACE || mergeBehavior == Action.MERGE_APPEND) {
    314                 mActions.add(a);
    315             }
    316         }
    317 
    318         // Because pruning can remove the need for bitmaps, we reconstruct the bitmap cache
    319         mBitmapCache = new BitmapCache();
    320         setBitmapCache(mBitmapCache);
    321     }
    322 
    323     private class SetEmptyView extends Action {
    324         int viewId;
    325         int emptyViewId;
    326 
    327         public final static int TAG = 6;
    328 
    329         SetEmptyView(int viewId, int emptyViewId) {
    330             this.viewId = viewId;
    331             this.emptyViewId = emptyViewId;
    332         }
    333 
    334         SetEmptyView(Parcel in) {
    335             this.viewId = in.readInt();
    336             this.emptyViewId = in.readInt();
    337         }
    338 
    339         public void writeToParcel(Parcel out, int flags) {
    340             out.writeInt(TAG);
    341             out.writeInt(this.viewId);
    342             out.writeInt(this.emptyViewId);
    343         }
    344 
    345         @Override
    346         public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
    347             final View view = root.findViewById(viewId);
    348             if (!(view instanceof AdapterView<?>)) return;
    349 
    350             AdapterView<?> adapterView = (AdapterView<?>) view;
    351 
    352             final View emptyView = root.findViewById(emptyViewId);
    353             if (emptyView == null) return;
    354 
    355             adapterView.setEmptyView(emptyView);
    356         }
    357 
    358         public String getActionName() {
    359             return "SetEmptyView";
    360         }
    361     }
    362 
    363     private class SetOnClickFillInIntent extends Action {
    364         public SetOnClickFillInIntent(int id, Intent fillInIntent) {
    365             this.viewId = id;
    366             this.fillInIntent = fillInIntent;
    367         }
    368 
    369         public SetOnClickFillInIntent(Parcel parcel) {
    370             viewId = parcel.readInt();
    371             fillInIntent = Intent.CREATOR.createFromParcel(parcel);
    372         }
    373 
    374         public void writeToParcel(Parcel dest, int flags) {
    375             dest.writeInt(TAG);
    376             dest.writeInt(viewId);
    377             fillInIntent.writeToParcel(dest, 0 /* no flags */);
    378         }
    379 
    380         @Override
    381         public void apply(View root, ViewGroup rootParent, final OnClickHandler handler) {
    382             final View target = root.findViewById(viewId);
    383             if (target == null) return;
    384 
    385             if (!mIsWidgetCollectionChild) {
    386                 Log.e(LOG_TAG, "The method setOnClickFillInIntent is available " +
    387                         "only from RemoteViewsFactory (ie. on collection items).");
    388                 return;
    389             }
    390             if (target == root) {
    391                 target.setTagInternal(com.android.internal.R.id.fillInIntent, fillInIntent);
    392             } else if (fillInIntent != null) {
    393                 OnClickListener listener = new OnClickListener() {
    394                     public void onClick(View v) {
    395                         // Insure that this view is a child of an AdapterView
    396                         View parent = (View) v.getParent();
    397                         while (parent != null && !(parent instanceof AdapterView<?>)
    398                                 && !(parent instanceof AppWidgetHostView)) {
    399                             parent = (View) parent.getParent();
    400                         }
    401 
    402                         if (parent instanceof AppWidgetHostView || parent == null) {
    403                             // Somehow they've managed to get this far without having
    404                             // and AdapterView as a parent.
    405                             Log.e(LOG_TAG, "Collection item doesn't have AdapterView parent");
    406                             return;
    407                         }
    408 
    409                         // Insure that a template pending intent has been set on an ancestor
    410                         if (!(parent.getTag() instanceof PendingIntent)) {
    411                             Log.e(LOG_TAG, "Attempting setOnClickFillInIntent without" +
    412                                     " calling setPendingIntentTemplate on parent.");
    413                             return;
    414                         }
    415 
    416                         PendingIntent pendingIntent = (PendingIntent) parent.getTag();
    417 
    418                         final Rect rect = getSourceBounds(v);
    419 
    420                         fillInIntent.setSourceBounds(rect);
    421                         handler.onClickHandler(v, pendingIntent, fillInIntent);
    422                     }
    423 
    424                 };
    425                 target.setOnClickListener(listener);
    426             }
    427         }
    428 
    429         public String getActionName() {
    430             return "SetOnClickFillInIntent";
    431         }
    432 
    433         Intent fillInIntent;
    434 
    435         public final static int TAG = 9;
    436     }
    437 
    438     private class SetPendingIntentTemplate extends Action {
    439         public SetPendingIntentTemplate(int id, PendingIntent pendingIntentTemplate) {
    440             this.viewId = id;
    441             this.pendingIntentTemplate = pendingIntentTemplate;
    442         }
    443 
    444         public SetPendingIntentTemplate(Parcel parcel) {
    445             viewId = parcel.readInt();
    446             pendingIntentTemplate = PendingIntent.readPendingIntentOrNullFromParcel(parcel);
    447         }
    448 
    449         public void writeToParcel(Parcel dest, int flags) {
    450             dest.writeInt(TAG);
    451             dest.writeInt(viewId);
    452             pendingIntentTemplate.writeToParcel(dest, 0 /* no flags */);
    453         }
    454 
    455         @Override
    456         public void apply(View root, ViewGroup rootParent, final OnClickHandler handler) {
    457             final View target = root.findViewById(viewId);
    458             if (target == null) return;
    459 
    460             // If the view isn't an AdapterView, setting a PendingIntent template doesn't make sense
    461             if (target instanceof AdapterView<?>) {
    462                 AdapterView<?> av = (AdapterView<?>) target;
    463                 // The PendingIntent template is stored in the view's tag.
    464                 OnItemClickListener listener = new OnItemClickListener() {
    465                     public void onItemClick(AdapterView<?> parent, View view,
    466                             int position, long id) {
    467                         // The view should be a frame layout
    468                         if (view instanceof ViewGroup) {
    469                             ViewGroup vg = (ViewGroup) view;
    470 
    471                             // AdapterViews contain their children in a frame
    472                             // so we need to go one layer deeper here.
    473                             if (parent instanceof AdapterViewAnimator) {
    474                                 vg = (ViewGroup) vg.getChildAt(0);
    475                             }
    476                             if (vg == null) return;
    477 
    478                             Intent fillInIntent = null;
    479                             int childCount = vg.getChildCount();
    480                             for (int i = 0; i < childCount; i++) {
    481                                 Object tag = vg.getChildAt(i).getTag(com.android.internal.R.id.fillInIntent);
    482                                 if (tag instanceof Intent) {
    483                                     fillInIntent = (Intent) tag;
    484                                     break;
    485                                 }
    486                             }
    487                             if (fillInIntent == null) return;
    488 
    489                             final Rect rect = getSourceBounds(view);
    490 
    491                             final Intent intent = new Intent();
    492                             intent.setSourceBounds(rect);
    493                             handler.onClickHandler(view, pendingIntentTemplate, fillInIntent);
    494                         }
    495                     }
    496                 };
    497                 av.setOnItemClickListener(listener);
    498                 av.setTag(pendingIntentTemplate);
    499             } else {
    500                 Log.e(LOG_TAG, "Cannot setPendingIntentTemplate on a view which is not" +
    501                         "an AdapterView (id: " + viewId + ")");
    502                 return;
    503             }
    504         }
    505 
    506         public String getActionName() {
    507             return "SetPendingIntentTemplate";
    508         }
    509 
    510         PendingIntent pendingIntentTemplate;
    511 
    512         public final static int TAG = 8;
    513     }
    514 
    515     private class SetRemoteViewsAdapterList extends Action {
    516         public SetRemoteViewsAdapterList(int id, ArrayList<RemoteViews> list, int viewTypeCount) {
    517             this.viewId = id;
    518             this.list = list;
    519             this.viewTypeCount = viewTypeCount;
    520         }
    521 
    522         public SetRemoteViewsAdapterList(Parcel parcel) {
    523             viewId = parcel.readInt();
    524             viewTypeCount = parcel.readInt();
    525             int count = parcel.readInt();
    526             list = new ArrayList<RemoteViews>();
    527 
    528             for (int i = 0; i < count; i++) {
    529                 RemoteViews rv = RemoteViews.CREATOR.createFromParcel(parcel);
    530                 list.add(rv);
    531             }
    532         }
    533 
    534         public void writeToParcel(Parcel dest, int flags) {
    535             dest.writeInt(TAG);
    536             dest.writeInt(viewId);
    537             dest.writeInt(viewTypeCount);
    538 
    539             if (list == null || list.size() == 0) {
    540                 dest.writeInt(0);
    541             } else {
    542                 int count = list.size();
    543                 dest.writeInt(count);
    544                 for (int i = 0; i < count; i++) {
    545                     RemoteViews rv = list.get(i);
    546                     rv.writeToParcel(dest, flags);
    547                 }
    548             }
    549         }
    550 
    551         @Override
    552         public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
    553             final View target = root.findViewById(viewId);
    554             if (target == null) return;
    555 
    556             // Ensure that we are applying to an AppWidget root
    557             if (!(rootParent instanceof AppWidgetHostView)) {
    558                 Log.e(LOG_TAG, "SetRemoteViewsAdapterIntent action can only be used for " +
    559                         "AppWidgets (root id: " + viewId + ")");
    560                 return;
    561             }
    562             // Ensure that we are calling setRemoteAdapter on an AdapterView that supports it
    563             if (!(target instanceof AbsListView) && !(target instanceof AdapterViewAnimator)) {
    564                 Log.e(LOG_TAG, "Cannot setRemoteViewsAdapter on a view which is not " +
    565                         "an AbsListView or AdapterViewAnimator (id: " + viewId + ")");
    566                 return;
    567             }
    568 
    569             if (target instanceof AbsListView) {
    570                 AbsListView v = (AbsListView) target;
    571                 Adapter a = v.getAdapter();
    572                 if (a instanceof RemoteViewsListAdapter && viewTypeCount <= a.getViewTypeCount()) {
    573                     ((RemoteViewsListAdapter) a).setViewsList(list);
    574                 } else {
    575                     v.setAdapter(new RemoteViewsListAdapter(v.getContext(), list, viewTypeCount));
    576                 }
    577             } else if (target instanceof AdapterViewAnimator) {
    578                 AdapterViewAnimator v = (AdapterViewAnimator) target;
    579                 Adapter a = v.getAdapter();
    580                 if (a instanceof RemoteViewsListAdapter && viewTypeCount <= a.getViewTypeCount()) {
    581                     ((RemoteViewsListAdapter) a).setViewsList(list);
    582                 } else {
    583                     v.setAdapter(new RemoteViewsListAdapter(v.getContext(), list, viewTypeCount));
    584                 }
    585             }
    586         }
    587 
    588         public String getActionName() {
    589             return "SetRemoteViewsAdapterList";
    590         }
    591 
    592         int viewTypeCount;
    593         ArrayList<RemoteViews> list;
    594         public final static int TAG = 15;
    595     }
    596 
    597     private class SetRemoteViewsAdapterIntent extends Action {
    598         public SetRemoteViewsAdapterIntent(int id, Intent intent) {
    599             this.viewId = id;
    600             this.intent = intent;
    601         }
    602 
    603         public SetRemoteViewsAdapterIntent(Parcel parcel) {
    604             viewId = parcel.readInt();
    605             intent = Intent.CREATOR.createFromParcel(parcel);
    606         }
    607 
    608         public void writeToParcel(Parcel dest, int flags) {
    609             dest.writeInt(TAG);
    610             dest.writeInt(viewId);
    611             intent.writeToParcel(dest, flags);
    612         }
    613 
    614         @Override
    615         public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
    616             final View target = root.findViewById(viewId);
    617             if (target == null) return;
    618 
    619             // Ensure that we are applying to an AppWidget root
    620             if (!(rootParent instanceof AppWidgetHostView)) {
    621                 Log.e(LOG_TAG, "SetRemoteViewsAdapterIntent action can only be used for " +
    622                         "AppWidgets (root id: " + viewId + ")");
    623                 return;
    624             }
    625             // Ensure that we are calling setRemoteAdapter on an AdapterView that supports it
    626             if (!(target instanceof AbsListView) && !(target instanceof AdapterViewAnimator)) {
    627                 Log.e(LOG_TAG, "Cannot setRemoteViewsAdapter on a view which is not " +
    628                         "an AbsListView or AdapterViewAnimator (id: " + viewId + ")");
    629                 return;
    630             }
    631 
    632             // Embed the AppWidget Id for use in RemoteViewsAdapter when connecting to the intent
    633             // RemoteViewsService
    634             AppWidgetHostView host = (AppWidgetHostView) rootParent;
    635             intent.putExtra(EXTRA_REMOTEADAPTER_APPWIDGET_ID, host.getAppWidgetId());
    636             if (target instanceof AbsListView) {
    637                 AbsListView v = (AbsListView) target;
    638                 v.setRemoteViewsAdapter(intent);
    639                 v.setRemoteViewsOnClickHandler(handler);
    640             } else if (target instanceof AdapterViewAnimator) {
    641                 AdapterViewAnimator v = (AdapterViewAnimator) target;
    642                 v.setRemoteViewsAdapter(intent);
    643                 v.setRemoteViewsOnClickHandler(handler);
    644             }
    645         }
    646 
    647         public String getActionName() {
    648             return "SetRemoteViewsAdapterIntent";
    649         }
    650 
    651         Intent intent;
    652 
    653         public final static int TAG = 10;
    654     }
    655 
    656     /**
    657      * Equivalent to calling
    658      * {@link android.view.View#setOnClickListener(android.view.View.OnClickListener)}
    659      * to launch the provided {@link PendingIntent}.
    660      */
    661     private class SetOnClickPendingIntent extends Action {
    662         public SetOnClickPendingIntent(int id, PendingIntent pendingIntent) {
    663             this.viewId = id;
    664             this.pendingIntent = pendingIntent;
    665         }
    666 
    667         public SetOnClickPendingIntent(Parcel parcel) {
    668             viewId = parcel.readInt();
    669 
    670             // We check a flag to determine if the parcel contains a PendingIntent.
    671             if (parcel.readInt() != 0) {
    672                 pendingIntent = PendingIntent.readPendingIntentOrNullFromParcel(parcel);
    673             }
    674         }
    675 
    676         public void writeToParcel(Parcel dest, int flags) {
    677             dest.writeInt(TAG);
    678             dest.writeInt(viewId);
    679 
    680             // We use a flag to indicate whether the parcel contains a valid object.
    681             dest.writeInt(pendingIntent != null ? 1 : 0);
    682             if (pendingIntent != null) {
    683                 pendingIntent.writeToParcel(dest, 0 /* no flags */);
    684             }
    685         }
    686 
    687         @Override
    688         public void apply(View root, ViewGroup rootParent, final OnClickHandler handler) {
    689             final View target = root.findViewById(viewId);
    690             if (target == null) return;
    691 
    692             // If the view is an AdapterView, setting a PendingIntent on click doesn't make much
    693             // sense, do they mean to set a PendingIntent template for the AdapterView's children?
    694             if (mIsWidgetCollectionChild) {
    695                 Log.w(LOG_TAG, "Cannot setOnClickPendingIntent for collection item " +
    696                         "(id: " + viewId + ")");
    697                 ApplicationInfo appInfo = root.getContext().getApplicationInfo();
    698 
    699                 // We let this slide for HC and ICS so as to not break compatibility. It should have
    700                 // been disabled from the outset, but was left open by accident.
    701                 if (appInfo != null &&
    702                         appInfo.targetSdkVersion >= Build.VERSION_CODES.JELLY_BEAN) {
    703                     return;
    704                 }
    705             }
    706 
    707             // If the pendingIntent is null, we clear the onClickListener
    708             OnClickListener listener = null;
    709             if (pendingIntent != null) {
    710                 listener = new OnClickListener() {
    711                     public void onClick(View v) {
    712                         // Find target view location in screen coordinates and
    713                         // fill into PendingIntent before sending.
    714                         final Rect rect = getSourceBounds(v);
    715 
    716                         final Intent intent = new Intent();
    717                         intent.setSourceBounds(rect);
    718                         handler.onClickHandler(v, pendingIntent, intent);
    719                     }
    720                 };
    721             }
    722             target.setOnClickListener(listener);
    723         }
    724 
    725         public String getActionName() {
    726             return "SetOnClickPendingIntent";
    727         }
    728 
    729         PendingIntent pendingIntent;
    730 
    731         public final static int TAG = 1;
    732     }
    733 
    734     private static Rect getSourceBounds(View v) {
    735         final float appScale = v.getContext().getResources()
    736                 .getCompatibilityInfo().applicationScale;
    737         final int[] pos = new int[2];
    738         v.getLocationOnScreen(pos);
    739 
    740         final Rect rect = new Rect();
    741         rect.left = (int) (pos[0] * appScale + 0.5f);
    742         rect.top = (int) (pos[1] * appScale + 0.5f);
    743         rect.right = (int) ((pos[0] + v.getWidth()) * appScale + 0.5f);
    744         rect.bottom = (int) ((pos[1] + v.getHeight()) * appScale + 0.5f);
    745         return rect;
    746     }
    747 
    748     private Method getMethod(View view, String methodName, Class<?> paramType) {
    749         Method method;
    750         Class<? extends View> klass = view.getClass();
    751 
    752         synchronized (sMethodsLock) {
    753             ArrayMap<MutablePair<String, Class<?>>, Method> methods = sMethods.get(klass);
    754             if (methods == null) {
    755                 methods = new ArrayMap<MutablePair<String, Class<?>>, Method>();
    756                 sMethods.put(klass, methods);
    757             }
    758 
    759             mPair.first = methodName;
    760             mPair.second = paramType;
    761 
    762             method = methods.get(mPair);
    763             if (method == null) {
    764                 try {
    765                     if (paramType == null) {
    766                         method = klass.getMethod(methodName);
    767                     } else {
    768                         method = klass.getMethod(methodName, paramType);
    769                     }
    770                 } catch (NoSuchMethodException ex) {
    771                     throw new ActionException("view: " + klass.getName() + " doesn't have method: "
    772                             + methodName + getParameters(paramType));
    773                 }
    774 
    775                 if (!method.isAnnotationPresent(RemotableViewMethod.class)) {
    776                     throw new ActionException("view: " + klass.getName()
    777                             + " can't use method with RemoteViews: "
    778                             + methodName + getParameters(paramType));
    779                 }
    780 
    781                 methods.put(new MutablePair<String, Class<?>>(methodName, paramType), method);
    782             }
    783         }
    784 
    785         return method;
    786     }
    787 
    788     private static String getParameters(Class<?> paramType) {
    789         if (paramType == null) return "()";
    790         return "(" + paramType + ")";
    791     }
    792 
    793     private static Object[] wrapArg(Object value) {
    794         Object[] args = sInvokeArgsTls.get();
    795         args[0] = value;
    796         return args;
    797     }
    798 
    799     /**
    800      * Equivalent to calling a combination of {@link Drawable#setAlpha(int)},
    801      * {@link Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)},
    802      * and/or {@link Drawable#setLevel(int)} on the {@link Drawable} of a given view.
    803      * <p>
    804      * These operations will be performed on the {@link Drawable} returned by the
    805      * target {@link View#getBackground()} by default.  If targetBackground is false,
    806      * we assume the target is an {@link ImageView} and try applying the operations
    807      * to {@link ImageView#getDrawable()}.
    808      * <p>
    809      * You can omit specific calls by marking their values with null or -1.
    810      */
    811     private class SetDrawableParameters extends Action {
    812         public SetDrawableParameters(int id, boolean targetBackground, int alpha,
    813                 int colorFilter, PorterDuff.Mode mode, int level) {
    814             this.viewId = id;
    815             this.targetBackground = targetBackground;
    816             this.alpha = alpha;
    817             this.colorFilter = colorFilter;
    818             this.filterMode = mode;
    819             this.level = level;
    820         }
    821 
    822         public SetDrawableParameters(Parcel parcel) {
    823             viewId = parcel.readInt();
    824             targetBackground = parcel.readInt() != 0;
    825             alpha = parcel.readInt();
    826             colorFilter = parcel.readInt();
    827             boolean hasMode = parcel.readInt() != 0;
    828             if (hasMode) {
    829                 filterMode = PorterDuff.Mode.valueOf(parcel.readString());
    830             } else {
    831                 filterMode = null;
    832             }
    833             level = parcel.readInt();
    834         }
    835 
    836         public void writeToParcel(Parcel dest, int flags) {
    837             dest.writeInt(TAG);
    838             dest.writeInt(viewId);
    839             dest.writeInt(targetBackground ? 1 : 0);
    840             dest.writeInt(alpha);
    841             dest.writeInt(colorFilter);
    842             if (filterMode != null) {
    843                 dest.writeInt(1);
    844                 dest.writeString(filterMode.toString());
    845             } else {
    846                 dest.writeInt(0);
    847             }
    848             dest.writeInt(level);
    849         }
    850 
    851         @Override
    852         public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
    853             final View target = root.findViewById(viewId);
    854             if (target == null) return;
    855 
    856             // Pick the correct drawable to modify for this view
    857             Drawable targetDrawable = null;
    858             if (targetBackground) {
    859                 targetDrawable = target.getBackground();
    860             } else if (target instanceof ImageView) {
    861                 ImageView imageView = (ImageView) target;
    862                 targetDrawable = imageView.getDrawable();
    863             }
    864 
    865             if (targetDrawable != null) {
    866                 // Perform modifications only if values are set correctly
    867                 if (alpha != -1) {
    868                     targetDrawable.setAlpha(alpha);
    869                 }
    870                 if (filterMode != null) {
    871                     targetDrawable.setColorFilter(colorFilter, filterMode);
    872                 }
    873                 if (level != -1) {
    874                     targetDrawable.setLevel(level);
    875                 }
    876             }
    877         }
    878 
    879         public String getActionName() {
    880             return "SetDrawableParameters";
    881         }
    882 
    883         boolean targetBackground;
    884         int alpha;
    885         int colorFilter;
    886         PorterDuff.Mode filterMode;
    887         int level;
    888 
    889         public final static int TAG = 3;
    890     }
    891 
    892     private final class ReflectionActionWithoutParams extends Action {
    893         final String methodName;
    894 
    895         public final static int TAG = 5;
    896 
    897         ReflectionActionWithoutParams(int viewId, String methodName) {
    898             this.viewId = viewId;
    899             this.methodName = methodName;
    900         }
    901 
    902         ReflectionActionWithoutParams(Parcel in) {
    903             this.viewId = in.readInt();
    904             this.methodName = in.readString();
    905         }
    906 
    907         public void writeToParcel(Parcel out, int flags) {
    908             out.writeInt(TAG);
    909             out.writeInt(this.viewId);
    910             out.writeString(this.methodName);
    911         }
    912 
    913         @Override
    914         public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
    915             final View view = root.findViewById(viewId);
    916             if (view == null) return;
    917 
    918             try {
    919                 getMethod(view, this.methodName, null).invoke(view);
    920             } catch (ActionException e) {
    921                 throw e;
    922             } catch (Exception ex) {
    923                 throw new ActionException(ex);
    924             }
    925         }
    926 
    927         public int mergeBehavior() {
    928             // we don't need to build up showNext or showPrevious calls
    929             if (methodName.equals("showNext") || methodName.equals("showPrevious")) {
    930                 return MERGE_IGNORE;
    931             } else {
    932                 return MERGE_REPLACE;
    933             }
    934         }
    935 
    936         public String getActionName() {
    937             return "ReflectionActionWithoutParams";
    938         }
    939     }
    940 
    941     private static class BitmapCache {
    942         ArrayList<Bitmap> mBitmaps;
    943 
    944         public BitmapCache() {
    945             mBitmaps = new ArrayList<Bitmap>();
    946         }
    947 
    948         public BitmapCache(Parcel source) {
    949             int count = source.readInt();
    950             mBitmaps = new ArrayList<Bitmap>();
    951             for (int i = 0; i < count; i++) {
    952                 Bitmap b = Bitmap.CREATOR.createFromParcel(source);
    953                 mBitmaps.add(b);
    954             }
    955         }
    956 
    957         public int getBitmapId(Bitmap b) {
    958             if (b == null) {
    959                 return -1;
    960             } else {
    961                 if (mBitmaps.contains(b)) {
    962                     return mBitmaps.indexOf(b);
    963                 } else {
    964                     mBitmaps.add(b);
    965                     return (mBitmaps.size() - 1);
    966                 }
    967             }
    968         }
    969 
    970         public Bitmap getBitmapForId(int id) {
    971             if (id == -1 || id >= mBitmaps.size()) {
    972                 return null;
    973             } else {
    974                 return mBitmaps.get(id);
    975             }
    976         }
    977 
    978         public void writeBitmapsToParcel(Parcel dest, int flags) {
    979             int count = mBitmaps.size();
    980             dest.writeInt(count);
    981             for (int i = 0; i < count; i++) {
    982                 mBitmaps.get(i).writeToParcel(dest, flags);
    983             }
    984         }
    985 
    986         public void assimilate(BitmapCache bitmapCache) {
    987             ArrayList<Bitmap> bitmapsToBeAdded = bitmapCache.mBitmaps;
    988             int count = bitmapsToBeAdded.size();
    989             for (int i = 0; i < count; i++) {
    990                 Bitmap b = bitmapsToBeAdded.get(i);
    991                 if (!mBitmaps.contains(b)) {
    992                     mBitmaps.add(b);
    993                 }
    994             }
    995         }
    996 
    997         public void addBitmapMemory(MemoryUsageCounter memoryCounter) {
    998             for (int i = 0; i < mBitmaps.size(); i++) {
    999                 memoryCounter.addBitmapMemory(mBitmaps.get(i));
   1000             }
   1001         }
   1002     }
   1003 
   1004     private class BitmapReflectionAction extends Action {
   1005         int bitmapId;
   1006         Bitmap bitmap;
   1007         String methodName;
   1008 
   1009         BitmapReflectionAction(int viewId, String methodName, Bitmap bitmap) {
   1010             this.bitmap = bitmap;
   1011             this.viewId = viewId;
   1012             this.methodName = methodName;
   1013             bitmapId = mBitmapCache.getBitmapId(bitmap);
   1014         }
   1015 
   1016         BitmapReflectionAction(Parcel in) {
   1017             viewId = in.readInt();
   1018             methodName = in.readString();
   1019             bitmapId = in.readInt();
   1020             bitmap = mBitmapCache.getBitmapForId(bitmapId);
   1021         }
   1022 
   1023         @Override
   1024         public void writeToParcel(Parcel dest, int flags) {
   1025             dest.writeInt(TAG);
   1026             dest.writeInt(viewId);
   1027             dest.writeString(methodName);
   1028             dest.writeInt(bitmapId);
   1029         }
   1030 
   1031         @Override
   1032         public void apply(View root, ViewGroup rootParent,
   1033                 OnClickHandler handler) throws ActionException {
   1034             ReflectionAction ra = new ReflectionAction(viewId, methodName, ReflectionAction.BITMAP,
   1035                     bitmap);
   1036             ra.apply(root, rootParent, handler);
   1037         }
   1038 
   1039         @Override
   1040         public void setBitmapCache(BitmapCache bitmapCache) {
   1041             bitmapId = bitmapCache.getBitmapId(bitmap);
   1042         }
   1043 
   1044         public String getActionName() {
   1045             return "BitmapReflectionAction";
   1046         }
   1047 
   1048         public final static int TAG = 12;
   1049     }
   1050 
   1051     /**
   1052      * Base class for the reflection actions.
   1053      */
   1054     private final class ReflectionAction extends Action {
   1055         static final int TAG = 2;
   1056 
   1057         static final int BOOLEAN = 1;
   1058         static final int BYTE = 2;
   1059         static final int SHORT = 3;
   1060         static final int INT = 4;
   1061         static final int LONG = 5;
   1062         static final int FLOAT = 6;
   1063         static final int DOUBLE = 7;
   1064         static final int CHAR = 8;
   1065         static final int STRING = 9;
   1066         static final int CHAR_SEQUENCE = 10;
   1067         static final int URI = 11;
   1068         // BITMAP actions are never stored in the list of actions. They are only used locally
   1069         // to implement BitmapReflectionAction, which eliminates duplicates using BitmapCache.
   1070         static final int BITMAP = 12;
   1071         static final int BUNDLE = 13;
   1072         static final int INTENT = 14;
   1073         static final int COLOR_STATE_LIST = 15;
   1074 
   1075         String methodName;
   1076         int type;
   1077         Object value;
   1078 
   1079         ReflectionAction(int viewId, String methodName, int type, Object value) {
   1080             this.viewId = viewId;
   1081             this.methodName = methodName;
   1082             this.type = type;
   1083             this.value = value;
   1084         }
   1085 
   1086         ReflectionAction(Parcel in) {
   1087             this.viewId = in.readInt();
   1088             this.methodName = in.readString();
   1089             this.type = in.readInt();
   1090             //noinspection ConstantIfStatement
   1091             if (false) {
   1092                 Log.d(LOG_TAG, "read viewId=0x" + Integer.toHexString(this.viewId)
   1093                         + " methodName=" + this.methodName + " type=" + this.type);
   1094             }
   1095 
   1096             // For some values that may have been null, we first check a flag to see if they were
   1097             // written to the parcel.
   1098             switch (this.type) {
   1099                 case BOOLEAN:
   1100                     this.value = in.readInt() != 0;
   1101                     break;
   1102                 case BYTE:
   1103                     this.value = in.readByte();
   1104                     break;
   1105                 case SHORT:
   1106                     this.value = (short)in.readInt();
   1107                     break;
   1108                 case INT:
   1109                     this.value = in.readInt();
   1110                     break;
   1111                 case LONG:
   1112                     this.value = in.readLong();
   1113                     break;
   1114                 case FLOAT:
   1115                     this.value = in.readFloat();
   1116                     break;
   1117                 case DOUBLE:
   1118                     this.value = in.readDouble();
   1119                     break;
   1120                 case CHAR:
   1121                     this.value = (char)in.readInt();
   1122                     break;
   1123                 case STRING:
   1124                     this.value = in.readString();
   1125                     break;
   1126                 case CHAR_SEQUENCE:
   1127                     this.value = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
   1128                     break;
   1129                 case URI:
   1130                     if (in.readInt() != 0) {
   1131                         this.value = Uri.CREATOR.createFromParcel(in);
   1132                     }
   1133                     break;
   1134                 case BITMAP:
   1135                     if (in.readInt() != 0) {
   1136                         this.value = Bitmap.CREATOR.createFromParcel(in);
   1137                     }
   1138                     break;
   1139                 case BUNDLE:
   1140                     this.value = in.readBundle();
   1141                     break;
   1142                 case INTENT:
   1143                     if (in.readInt() != 0) {
   1144                         this.value = Intent.CREATOR.createFromParcel(in);
   1145                     }
   1146                     break;
   1147                 case COLOR_STATE_LIST:
   1148                     if (in.readInt() != 0) {
   1149                         this.value = ColorStateList.CREATOR.createFromParcel(in);
   1150                     }
   1151                     break;
   1152                 default:
   1153                     break;
   1154             }
   1155         }
   1156 
   1157         public void writeToParcel(Parcel out, int flags) {
   1158             out.writeInt(TAG);
   1159             out.writeInt(this.viewId);
   1160             out.writeString(this.methodName);
   1161             out.writeInt(this.type);
   1162             //noinspection ConstantIfStatement
   1163             if (false) {
   1164                 Log.d(LOG_TAG, "write viewId=0x" + Integer.toHexString(this.viewId)
   1165                         + " methodName=" + this.methodName + " type=" + this.type);
   1166             }
   1167 
   1168             // For some values which are null, we record an integer flag to indicate whether
   1169             // we have written a valid value to the parcel.
   1170             switch (this.type) {
   1171                 case BOOLEAN:
   1172                     out.writeInt((Boolean) this.value ? 1 : 0);
   1173                     break;
   1174                 case BYTE:
   1175                     out.writeByte((Byte) this.value);
   1176                     break;
   1177                 case SHORT:
   1178                     out.writeInt((Short) this.value);
   1179                     break;
   1180                 case INT:
   1181                     out.writeInt((Integer) this.value);
   1182                     break;
   1183                 case LONG:
   1184                     out.writeLong((Long) this.value);
   1185                     break;
   1186                 case FLOAT:
   1187                     out.writeFloat((Float) this.value);
   1188                     break;
   1189                 case DOUBLE:
   1190                     out.writeDouble((Double) this.value);
   1191                     break;
   1192                 case CHAR:
   1193                     out.writeInt((int)((Character)this.value).charValue());
   1194                     break;
   1195                 case STRING:
   1196                     out.writeString((String)this.value);
   1197                     break;
   1198                 case CHAR_SEQUENCE:
   1199                     TextUtils.writeToParcel((CharSequence)this.value, out, flags);
   1200                     break;
   1201                 case URI:
   1202                     out.writeInt(this.value != null ? 1 : 0);
   1203                     if (this.value != null) {
   1204                         ((Uri)this.value).writeToParcel(out, flags);
   1205                     }
   1206                     break;
   1207                 case BITMAP:
   1208                     out.writeInt(this.value != null ? 1 : 0);
   1209                     if (this.value != null) {
   1210                         ((Bitmap)this.value).writeToParcel(out, flags);
   1211                     }
   1212                     break;
   1213                 case BUNDLE:
   1214                     out.writeBundle((Bundle) this.value);
   1215                     break;
   1216                 case INTENT:
   1217                     out.writeInt(this.value != null ? 1 : 0);
   1218                     if (this.value != null) {
   1219                         ((Intent)this.value).writeToParcel(out, flags);
   1220                     }
   1221                     break;
   1222                 case COLOR_STATE_LIST:
   1223                     out.writeInt(this.value != null ? 1 : 0);
   1224                     if (this.value != null) {
   1225                         ((ColorStateList)this.value).writeToParcel(out, flags);
   1226                     }
   1227                 default:
   1228                     break;
   1229             }
   1230         }
   1231 
   1232         private Class<?> getParameterType() {
   1233             switch (this.type) {
   1234                 case BOOLEAN:
   1235                     return boolean.class;
   1236                 case BYTE:
   1237                     return byte.class;
   1238                 case SHORT:
   1239                     return short.class;
   1240                 case INT:
   1241                     return int.class;
   1242                 case LONG:
   1243                     return long.class;
   1244                 case FLOAT:
   1245                     return float.class;
   1246                 case DOUBLE:
   1247                     return double.class;
   1248                 case CHAR:
   1249                     return char.class;
   1250                 case STRING:
   1251                     return String.class;
   1252                 case CHAR_SEQUENCE:
   1253                     return CharSequence.class;
   1254                 case URI:
   1255                     return Uri.class;
   1256                 case BITMAP:
   1257                     return Bitmap.class;
   1258                 case BUNDLE:
   1259                     return Bundle.class;
   1260                 case INTENT:
   1261                     return Intent.class;
   1262                 case COLOR_STATE_LIST:
   1263                     return ColorStateList.class;
   1264                 default:
   1265                     return null;
   1266             }
   1267         }
   1268 
   1269         @Override
   1270         public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
   1271             final View view = root.findViewById(viewId);
   1272             if (view == null) return;
   1273 
   1274             Class<?> param = getParameterType();
   1275             if (param == null) {
   1276                 throw new ActionException("bad type: " + this.type);
   1277             }
   1278 
   1279             try {
   1280                 getMethod(view, this.methodName, param).invoke(view, wrapArg(this.value));
   1281             } catch (ActionException e) {
   1282                 throw e;
   1283             } catch (Exception ex) {
   1284                 throw new ActionException(ex);
   1285             }
   1286         }
   1287 
   1288         public int mergeBehavior() {
   1289             // smoothScrollBy is cumulative, everything else overwites.
   1290             if (methodName.equals("smoothScrollBy")) {
   1291                 return MERGE_APPEND;
   1292             } else {
   1293                 return MERGE_REPLACE;
   1294             }
   1295         }
   1296 
   1297         public String getActionName() {
   1298             // Each type of reflection action corresponds to a setter, so each should be seen as
   1299             // unique from the standpoint of merging.
   1300             return "ReflectionAction" + this.methodName + this.type;
   1301         }
   1302     }
   1303 
   1304     private void configureRemoteViewsAsChild(RemoteViews rv) {
   1305         mBitmapCache.assimilate(rv.mBitmapCache);
   1306         rv.setBitmapCache(mBitmapCache);
   1307         rv.setNotRoot();
   1308     }
   1309 
   1310     void setNotRoot() {
   1311         mIsRoot = false;
   1312     }
   1313 
   1314     /**
   1315      * Equivalent to calling {@link ViewGroup#addView(View)} after inflating the
   1316      * given {@link RemoteViews}, or calling {@link ViewGroup#removeAllViews()}
   1317      * when null. This allows users to build "nested" {@link RemoteViews}.
   1318      */
   1319     private class ViewGroupAction extends Action {
   1320         public ViewGroupAction(int viewId, RemoteViews nestedViews) {
   1321             this.viewId = viewId;
   1322             this.nestedViews = nestedViews;
   1323             if (nestedViews != null) {
   1324                 configureRemoteViewsAsChild(nestedViews);
   1325             }
   1326         }
   1327 
   1328         public ViewGroupAction(Parcel parcel, BitmapCache bitmapCache) {
   1329             viewId = parcel.readInt();
   1330             boolean nestedViewsNull = parcel.readInt() == 0;
   1331             if (!nestedViewsNull) {
   1332                 nestedViews = new RemoteViews(parcel, bitmapCache);
   1333             } else {
   1334                 nestedViews = null;
   1335             }
   1336         }
   1337 
   1338         public void writeToParcel(Parcel dest, int flags) {
   1339             dest.writeInt(TAG);
   1340             dest.writeInt(viewId);
   1341             if (nestedViews != null) {
   1342                 dest.writeInt(1);
   1343                 nestedViews.writeToParcel(dest, flags);
   1344             } else {
   1345                 // signifies null
   1346                 dest.writeInt(0);
   1347             }
   1348         }
   1349 
   1350         @Override
   1351         public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
   1352             final Context context = root.getContext();
   1353             final ViewGroup target = (ViewGroup) root.findViewById(viewId);
   1354             if (target == null) return;
   1355             if (nestedViews != null) {
   1356                 // Inflate nested views and add as children
   1357                 target.addView(nestedViews.apply(context, target, handler));
   1358             } else {
   1359                 // Clear all children when nested views omitted
   1360                 target.removeAllViews();
   1361             }
   1362         }
   1363 
   1364         @Override
   1365         public void updateMemoryUsageEstimate(MemoryUsageCounter counter) {
   1366             if (nestedViews != null) {
   1367                 counter.increment(nestedViews.estimateMemoryUsage());
   1368             }
   1369         }
   1370 
   1371         @Override
   1372         public void setBitmapCache(BitmapCache bitmapCache) {
   1373             if (nestedViews != null) {
   1374                 nestedViews.setBitmapCache(bitmapCache);
   1375             }
   1376         }
   1377 
   1378         public String getActionName() {
   1379             return "ViewGroupAction" + (nestedViews == null ? "Remove" : "Add");
   1380         }
   1381 
   1382         public int mergeBehavior() {
   1383             return MERGE_APPEND;
   1384         }
   1385 
   1386         RemoteViews nestedViews;
   1387 
   1388         public final static int TAG = 4;
   1389     }
   1390 
   1391     /**
   1392      * Helper action to set compound drawables on a TextView. Supports relative
   1393      * (s/t/e/b) or cardinal (l/t/r/b) arrangement.
   1394      */
   1395     private class TextViewDrawableAction extends Action {
   1396         public TextViewDrawableAction(int viewId, boolean isRelative, int d1, int d2, int d3, int d4) {
   1397             this.viewId = viewId;
   1398             this.isRelative = isRelative;
   1399             this.d1 = d1;
   1400             this.d2 = d2;
   1401             this.d3 = d3;
   1402             this.d4 = d4;
   1403         }
   1404 
   1405         public TextViewDrawableAction(Parcel parcel) {
   1406             viewId = parcel.readInt();
   1407             isRelative = (parcel.readInt() != 0);
   1408             d1 = parcel.readInt();
   1409             d2 = parcel.readInt();
   1410             d3 = parcel.readInt();
   1411             d4 = parcel.readInt();
   1412         }
   1413 
   1414         public void writeToParcel(Parcel dest, int flags) {
   1415             dest.writeInt(TAG);
   1416             dest.writeInt(viewId);
   1417             dest.writeInt(isRelative ? 1 : 0);
   1418             dest.writeInt(d1);
   1419             dest.writeInt(d2);
   1420             dest.writeInt(d3);
   1421             dest.writeInt(d4);
   1422         }
   1423 
   1424         @Override
   1425         public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
   1426             final TextView target = (TextView) root.findViewById(viewId);
   1427             if (target == null) return;
   1428             if (isRelative) {
   1429                 target.setCompoundDrawablesRelativeWithIntrinsicBounds(d1, d2, d3, d4);
   1430             } else {
   1431                 target.setCompoundDrawablesWithIntrinsicBounds(d1, d2, d3, d4);
   1432             }
   1433         }
   1434 
   1435         public String getActionName() {
   1436             return "TextViewDrawableAction";
   1437         }
   1438 
   1439         boolean isRelative = false;
   1440         int d1, d2, d3, d4;
   1441 
   1442         public final static int TAG = 11;
   1443     }
   1444 
   1445     /**
   1446      * Helper action to set text size on a TextView in any supported units.
   1447      */
   1448     private class TextViewSizeAction extends Action {
   1449         public TextViewSizeAction(int viewId, int units, float size) {
   1450             this.viewId = viewId;
   1451             this.units = units;
   1452             this.size = size;
   1453         }
   1454 
   1455         public TextViewSizeAction(Parcel parcel) {
   1456             viewId = parcel.readInt();
   1457             units = parcel.readInt();
   1458             size  = parcel.readFloat();
   1459         }
   1460 
   1461         public void writeToParcel(Parcel dest, int flags) {
   1462             dest.writeInt(TAG);
   1463             dest.writeInt(viewId);
   1464             dest.writeInt(units);
   1465             dest.writeFloat(size);
   1466         }
   1467 
   1468         @Override
   1469         public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
   1470             final TextView target = (TextView) root.findViewById(viewId);
   1471             if (target == null) return;
   1472             target.setTextSize(units, size);
   1473         }
   1474 
   1475         public String getActionName() {
   1476             return "TextViewSizeAction";
   1477         }
   1478 
   1479         int units;
   1480         float size;
   1481 
   1482         public final static int TAG = 13;
   1483     }
   1484 
   1485     /**
   1486      * Helper action to set padding on a View.
   1487      */
   1488     private class ViewPaddingAction extends Action {
   1489         public ViewPaddingAction(int viewId, int left, int top, int right, int bottom) {
   1490             this.viewId = viewId;
   1491             this.left = left;
   1492             this.top = top;
   1493             this.right = right;
   1494             this.bottom = bottom;
   1495         }
   1496 
   1497         public ViewPaddingAction(Parcel parcel) {
   1498             viewId = parcel.readInt();
   1499             left = parcel.readInt();
   1500             top = parcel.readInt();
   1501             right = parcel.readInt();
   1502             bottom = parcel.readInt();
   1503         }
   1504 
   1505         public void writeToParcel(Parcel dest, int flags) {
   1506             dest.writeInt(TAG);
   1507             dest.writeInt(viewId);
   1508             dest.writeInt(left);
   1509             dest.writeInt(top);
   1510             dest.writeInt(right);
   1511             dest.writeInt(bottom);
   1512         }
   1513 
   1514         @Override
   1515         public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
   1516             final View target = root.findViewById(viewId);
   1517             if (target == null) return;
   1518             target.setPadding(left, top, right, bottom);
   1519         }
   1520 
   1521         public String getActionName() {
   1522             return "ViewPaddingAction";
   1523         }
   1524 
   1525         int left, top, right, bottom;
   1526 
   1527         public final static int TAG = 14;
   1528     }
   1529 
   1530     /**
   1531      * Helper action to set a color filter on a compound drawable on a TextView. Supports relative
   1532      * (s/t/e/b) or cardinal (l/t/r/b) arrangement.
   1533      */
   1534     private class TextViewDrawableColorFilterAction extends Action {
   1535         public TextViewDrawableColorFilterAction(int viewId, boolean isRelative, int index,
   1536                 int color, PorterDuff.Mode mode) {
   1537             this.viewId = viewId;
   1538             this.isRelative = isRelative;
   1539             this.index = index;
   1540             this.color = color;
   1541             this.mode = mode;
   1542         }
   1543 
   1544         public TextViewDrawableColorFilterAction(Parcel parcel) {
   1545             viewId = parcel.readInt();
   1546             isRelative = (parcel.readInt() != 0);
   1547             index = parcel.readInt();
   1548             color = parcel.readInt();
   1549             mode = readPorterDuffMode(parcel);
   1550         }
   1551 
   1552         private PorterDuff.Mode readPorterDuffMode(Parcel parcel) {
   1553             int mode = parcel.readInt();
   1554             if (mode >= 0 && mode < PorterDuff.Mode.values().length) {
   1555                 return PorterDuff.Mode.values()[mode];
   1556             } else {
   1557                 return PorterDuff.Mode.CLEAR;
   1558             }
   1559         }
   1560 
   1561         public void writeToParcel(Parcel dest, int flags) {
   1562             dest.writeInt(TAG);
   1563             dest.writeInt(viewId);
   1564             dest.writeInt(isRelative ? 1 : 0);
   1565             dest.writeInt(index);
   1566             dest.writeInt(color);
   1567             dest.writeInt(mode.ordinal());
   1568         }
   1569 
   1570         @Override
   1571         public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
   1572             final TextView target = (TextView) root.findViewById(viewId);
   1573             if (target == null) return;
   1574             Drawable[] drawables = isRelative
   1575                     ? target.getCompoundDrawablesRelative()
   1576                     : target.getCompoundDrawables();
   1577             if (index < 0 || index >= 4) {
   1578                 throw new IllegalStateException("index must be in range [0, 3].");
   1579             }
   1580             Drawable d = drawables[index];
   1581             if (d != null) {
   1582                 d.mutate();
   1583                 d.setColorFilter(color, mode);
   1584             }
   1585         }
   1586 
   1587         public String getActionName() {
   1588             return "TextViewDrawableColorFilterAction";
   1589         }
   1590 
   1591         final boolean isRelative;
   1592         final int index;
   1593         final int color;
   1594         final PorterDuff.Mode mode;
   1595 
   1596         public final static int TAG = 17;
   1597     }
   1598 
   1599     /**
   1600      * Simple class used to keep track of memory usage in a RemoteViews.
   1601      *
   1602      */
   1603     private class MemoryUsageCounter {
   1604         public void clear() {
   1605             mMemoryUsage = 0;
   1606         }
   1607 
   1608         public void increment(int numBytes) {
   1609             mMemoryUsage += numBytes;
   1610         }
   1611 
   1612         public int getMemoryUsage() {
   1613             return mMemoryUsage;
   1614         }
   1615 
   1616         @SuppressWarnings("deprecation")
   1617         public void addBitmapMemory(Bitmap b) {
   1618             final Bitmap.Config c = b.getConfig();
   1619             // If we don't know, be pessimistic and assume 4
   1620             int bpp = 4;
   1621             if (c != null) {
   1622                 switch (c) {
   1623                     case ALPHA_8:
   1624                         bpp = 1;
   1625                         break;
   1626                     case RGB_565:
   1627                     case ARGB_4444:
   1628                         bpp = 2;
   1629                         break;
   1630                     case ARGB_8888:
   1631                         bpp = 4;
   1632                         break;
   1633                 }
   1634             }
   1635             increment(b.getWidth() * b.getHeight() * bpp);
   1636         }
   1637 
   1638         int mMemoryUsage;
   1639     }
   1640 
   1641     /**
   1642      * Create a new RemoteViews object that will display the views contained
   1643      * in the specified layout file.
   1644      *
   1645      * @param packageName Name of the package that contains the layout resource
   1646      * @param layoutId The id of the layout resource
   1647      */
   1648     public RemoteViews(String packageName, int layoutId) {
   1649         this(getApplicationInfo(packageName, UserHandle.myUserId()), layoutId);
   1650     }
   1651 
   1652     /**
   1653      * Create a new RemoteViews object that will display the views contained
   1654      * in the specified layout file.
   1655      *
   1656      * @param packageName Name of the package that contains the layout resource.
   1657      * @param userId The user under which the package is running.
   1658      * @param layoutId The id of the layout resource.
   1659      *
   1660      * @hide
   1661      */
   1662     public RemoteViews(String packageName, int userId, int layoutId) {
   1663         this(getApplicationInfo(packageName, userId), layoutId);
   1664     }
   1665 
   1666     /**
   1667      * Create a new RemoteViews object that will display the views contained
   1668      * in the specified layout file.
   1669      *
   1670      * @param application The application whose content is shown by the views.
   1671      * @param layoutId The id of the layout resource.
   1672      *
   1673      * @hide
   1674      */
   1675     protected RemoteViews(ApplicationInfo application, int layoutId) {
   1676         mApplication = application;
   1677         mLayoutId = layoutId;
   1678         mBitmapCache = new BitmapCache();
   1679         // setup the memory usage statistics
   1680         mMemoryUsageCounter = new MemoryUsageCounter();
   1681         recalculateMemoryUsage();
   1682     }
   1683 
   1684     private boolean hasLandscapeAndPortraitLayouts() {
   1685         return (mLandscape != null) && (mPortrait != null);
   1686     }
   1687 
   1688     /**
   1689      * Create a new RemoteViews object that will inflate as the specified
   1690      * landspace or portrait RemoteViews, depending on the current configuration.
   1691      *
   1692      * @param landscape The RemoteViews to inflate in landscape configuration
   1693      * @param portrait The RemoteViews to inflate in portrait configuration
   1694      */
   1695     public RemoteViews(RemoteViews landscape, RemoteViews portrait) {
   1696         if (landscape == null || portrait == null) {
   1697             throw new RuntimeException("Both RemoteViews must be non-null");
   1698         }
   1699         if (landscape.mApplication.uid != portrait.mApplication.uid
   1700                 || !landscape.mApplication.packageName.equals(portrait.mApplication.packageName)) {
   1701             throw new RuntimeException("Both RemoteViews must share the same package and user");
   1702         }
   1703         mApplication = portrait.mApplication;
   1704         mLayoutId = portrait.getLayoutId();
   1705 
   1706         mLandscape = landscape;
   1707         mPortrait = portrait;
   1708 
   1709         // setup the memory usage statistics
   1710         mMemoryUsageCounter = new MemoryUsageCounter();
   1711 
   1712         mBitmapCache = new BitmapCache();
   1713         configureRemoteViewsAsChild(landscape);
   1714         configureRemoteViewsAsChild(portrait);
   1715 
   1716         recalculateMemoryUsage();
   1717     }
   1718 
   1719     /**
   1720      * Reads a RemoteViews object from a parcel.
   1721      *
   1722      * @param parcel
   1723      */
   1724     public RemoteViews(Parcel parcel) {
   1725         this(parcel, null);
   1726     }
   1727 
   1728     private RemoteViews(Parcel parcel, BitmapCache bitmapCache) {
   1729         int mode = parcel.readInt();
   1730 
   1731         // We only store a bitmap cache in the root of the RemoteViews.
   1732         if (bitmapCache == null) {
   1733             mBitmapCache = new BitmapCache(parcel);
   1734         } else {
   1735             setBitmapCache(bitmapCache);
   1736             setNotRoot();
   1737         }
   1738 
   1739         if (mode == MODE_NORMAL) {
   1740             mApplication = parcel.readParcelable(null);
   1741             mLayoutId = parcel.readInt();
   1742             mIsWidgetCollectionChild = parcel.readInt() == 1;
   1743 
   1744             int count = parcel.readInt();
   1745             if (count > 0) {
   1746                 mActions = new ArrayList<Action>(count);
   1747                 for (int i=0; i<count; i++) {
   1748                     int tag = parcel.readInt();
   1749                     switch (tag) {
   1750                         case SetOnClickPendingIntent.TAG:
   1751                             mActions.add(new SetOnClickPendingIntent(parcel));
   1752                             break;
   1753                         case SetDrawableParameters.TAG:
   1754                             mActions.add(new SetDrawableParameters(parcel));
   1755                             break;
   1756                         case ReflectionAction.TAG:
   1757                             mActions.add(new ReflectionAction(parcel));
   1758                             break;
   1759                         case ViewGroupAction.TAG:
   1760                             mActions.add(new ViewGroupAction(parcel, mBitmapCache));
   1761                             break;
   1762                         case ReflectionActionWithoutParams.TAG:
   1763                             mActions.add(new ReflectionActionWithoutParams(parcel));
   1764                             break;
   1765                         case SetEmptyView.TAG:
   1766                             mActions.add(new SetEmptyView(parcel));
   1767                             break;
   1768                         case SetPendingIntentTemplate.TAG:
   1769                             mActions.add(new SetPendingIntentTemplate(parcel));
   1770                             break;
   1771                         case SetOnClickFillInIntent.TAG:
   1772                             mActions.add(new SetOnClickFillInIntent(parcel));
   1773                             break;
   1774                         case SetRemoteViewsAdapterIntent.TAG:
   1775                             mActions.add(new SetRemoteViewsAdapterIntent(parcel));
   1776                             break;
   1777                         case TextViewDrawableAction.TAG:
   1778                             mActions.add(new TextViewDrawableAction(parcel));
   1779                             break;
   1780                         case TextViewSizeAction.TAG:
   1781                             mActions.add(new TextViewSizeAction(parcel));
   1782                             break;
   1783                         case ViewPaddingAction.TAG:
   1784                             mActions.add(new ViewPaddingAction(parcel));
   1785                             break;
   1786                         case BitmapReflectionAction.TAG:
   1787                             mActions.add(new BitmapReflectionAction(parcel));
   1788                             break;
   1789                         case SetRemoteViewsAdapterList.TAG:
   1790                             mActions.add(new SetRemoteViewsAdapterList(parcel));
   1791                             break;
   1792                         case TextViewDrawableColorFilterAction.TAG:
   1793                             mActions.add(new TextViewDrawableColorFilterAction(parcel));
   1794                             break;
   1795                         default:
   1796                             throw new ActionException("Tag " + tag + " not found");
   1797                     }
   1798                 }
   1799             }
   1800         } else {
   1801             // MODE_HAS_LANDSCAPE_AND_PORTRAIT
   1802             mLandscape = new RemoteViews(parcel, mBitmapCache);
   1803             mPortrait = new RemoteViews(parcel, mBitmapCache);
   1804             mApplication = mPortrait.mApplication;
   1805             mLayoutId = mPortrait.getLayoutId();
   1806         }
   1807 
   1808         // setup the memory usage statistics
   1809         mMemoryUsageCounter = new MemoryUsageCounter();
   1810         recalculateMemoryUsage();
   1811     }
   1812 
   1813 
   1814     public RemoteViews clone() {
   1815         Parcel p = Parcel.obtain();
   1816         writeToParcel(p, 0);
   1817         p.setDataPosition(0);
   1818         RemoteViews rv = new RemoteViews(p);
   1819         p.recycle();
   1820         return rv;
   1821     }
   1822 
   1823     public String getPackage() {
   1824         return (mApplication != null) ? mApplication.packageName : null;
   1825     }
   1826 
   1827     /**
   1828      * Reutrns the layout id of the root layout associated with this RemoteViews. In the case
   1829      * that the RemoteViews has both a landscape and portrait root, this will return the layout
   1830      * id associated with the portrait layout.
   1831      *
   1832      * @return the layout id.
   1833      */
   1834     public int getLayoutId() {
   1835         return mLayoutId;
   1836     }
   1837 
   1838     /*
   1839      * This flag indicates whether this RemoteViews object is being created from a
   1840      * RemoteViewsService for use as a child of a widget collection. This flag is used
   1841      * to determine whether or not certain features are available, in particular,
   1842      * setting on click extras and setting on click pending intents. The former is enabled,
   1843      * and the latter disabled when this flag is true.
   1844      */
   1845     void setIsWidgetCollectionChild(boolean isWidgetCollectionChild) {
   1846         mIsWidgetCollectionChild = isWidgetCollectionChild;
   1847     }
   1848 
   1849     /**
   1850      * Updates the memory usage statistics.
   1851      */
   1852     private void recalculateMemoryUsage() {
   1853         mMemoryUsageCounter.clear();
   1854 
   1855         if (!hasLandscapeAndPortraitLayouts()) {
   1856             // Accumulate the memory usage for each action
   1857             if (mActions != null) {
   1858                 final int count = mActions.size();
   1859                 for (int i= 0; i < count; ++i) {
   1860                     mActions.get(i).updateMemoryUsageEstimate(mMemoryUsageCounter);
   1861                 }
   1862             }
   1863             if (mIsRoot) {
   1864                 mBitmapCache.addBitmapMemory(mMemoryUsageCounter);
   1865             }
   1866         } else {
   1867             mMemoryUsageCounter.increment(mLandscape.estimateMemoryUsage());
   1868             mMemoryUsageCounter.increment(mPortrait.estimateMemoryUsage());
   1869             mBitmapCache.addBitmapMemory(mMemoryUsageCounter);
   1870         }
   1871     }
   1872 
   1873     /**
   1874      * Recursively sets BitmapCache in the hierarchy and update the bitmap ids.
   1875      */
   1876     private void setBitmapCache(BitmapCache bitmapCache) {
   1877         mBitmapCache = bitmapCache;
   1878         if (!hasLandscapeAndPortraitLayouts()) {
   1879             if (mActions != null) {
   1880                 final int count = mActions.size();
   1881                 for (int i= 0; i < count; ++i) {
   1882                     mActions.get(i).setBitmapCache(bitmapCache);
   1883                 }
   1884             }
   1885         } else {
   1886             mLandscape.setBitmapCache(bitmapCache);
   1887             mPortrait.setBitmapCache(bitmapCache);
   1888         }
   1889     }
   1890 
   1891     /**
   1892      * Returns an estimate of the bitmap heap memory usage for this RemoteViews.
   1893      */
   1894     /** @hide */
   1895     public int estimateMemoryUsage() {
   1896         return mMemoryUsageCounter.getMemoryUsage();
   1897     }
   1898 
   1899     /**
   1900      * Add an action to be executed on the remote side when apply is called.
   1901      *
   1902      * @param a The action to add
   1903      */
   1904     private void addAction(Action a) {
   1905         if (hasLandscapeAndPortraitLayouts()) {
   1906             throw new RuntimeException("RemoteViews specifying separate landscape and portrait" +
   1907                     " layouts cannot be modified. Instead, fully configure the landscape and" +
   1908                     " portrait layouts individually before constructing the combined layout.");
   1909         }
   1910         if (mActions == null) {
   1911             mActions = new ArrayList<Action>();
   1912         }
   1913         mActions.add(a);
   1914 
   1915         // update the memory usage stats
   1916         a.updateMemoryUsageEstimate(mMemoryUsageCounter);
   1917     }
   1918 
   1919     /**
   1920      * Equivalent to calling {@link ViewGroup#addView(View)} after inflating the
   1921      * given {@link RemoteViews}. This allows users to build "nested"
   1922      * {@link RemoteViews}. In cases where consumers of {@link RemoteViews} may
   1923      * recycle layouts, use {@link #removeAllViews(int)} to clear any existing
   1924      * children.
   1925      *
   1926      * @param viewId The id of the parent {@link ViewGroup} to add child into.
   1927      * @param nestedView {@link RemoteViews} that describes the child.
   1928      */
   1929     public void addView(int viewId, RemoteViews nestedView) {
   1930         addAction(new ViewGroupAction(viewId, nestedView));
   1931     }
   1932 
   1933     /**
   1934      * Equivalent to calling {@link ViewGroup#removeAllViews()}.
   1935      *
   1936      * @param viewId The id of the parent {@link ViewGroup} to remove all
   1937      *            children from.
   1938      */
   1939     public void removeAllViews(int viewId) {
   1940         addAction(new ViewGroupAction(viewId, null));
   1941     }
   1942 
   1943     /**
   1944      * Equivalent to calling {@link AdapterViewAnimator#showNext()}
   1945      *
   1946      * @param viewId The id of the view on which to call {@link AdapterViewAnimator#showNext()}
   1947      */
   1948     public void showNext(int viewId) {
   1949         addAction(new ReflectionActionWithoutParams(viewId, "showNext"));
   1950     }
   1951 
   1952     /**
   1953      * Equivalent to calling {@link AdapterViewAnimator#showPrevious()}
   1954      *
   1955      * @param viewId The id of the view on which to call {@link AdapterViewAnimator#showPrevious()}
   1956      */
   1957     public void showPrevious(int viewId) {
   1958         addAction(new ReflectionActionWithoutParams(viewId, "showPrevious"));
   1959     }
   1960 
   1961     /**
   1962      * Equivalent to calling {@link AdapterViewAnimator#setDisplayedChild(int)}
   1963      *
   1964      * @param viewId The id of the view on which to call
   1965      *               {@link AdapterViewAnimator#setDisplayedChild(int)}
   1966      */
   1967     public void setDisplayedChild(int viewId, int childIndex) {
   1968         setInt(viewId, "setDisplayedChild", childIndex);
   1969     }
   1970 
   1971     /**
   1972      * Equivalent to calling View.setVisibility
   1973      *
   1974      * @param viewId The id of the view whose visibility should change
   1975      * @param visibility The new visibility for the view
   1976      */
   1977     public void setViewVisibility(int viewId, int visibility) {
   1978         setInt(viewId, "setVisibility", visibility);
   1979     }
   1980 
   1981     /**
   1982      * Equivalent to calling TextView.setText
   1983      *
   1984      * @param viewId The id of the view whose text should change
   1985      * @param text The new text for the view
   1986      */
   1987     public void setTextViewText(int viewId, CharSequence text) {
   1988         setCharSequence(viewId, "setText", text);
   1989     }
   1990 
   1991     /**
   1992      * Equivalent to calling {@link TextView#setTextSize(int, float)}
   1993      *
   1994      * @param viewId The id of the view whose text size should change
   1995      * @param units The units of size (e.g. COMPLEX_UNIT_SP)
   1996      * @param size The size of the text
   1997      */
   1998     public void setTextViewTextSize(int viewId, int units, float size) {
   1999         addAction(new TextViewSizeAction(viewId, units, size));
   2000     }
   2001 
   2002     /**
   2003      * Equivalent to calling
   2004      * {@link TextView#setCompoundDrawablesWithIntrinsicBounds(int, int, int, int)}.
   2005      *
   2006      * @param viewId The id of the view whose text should change
   2007      * @param left The id of a drawable to place to the left of the text, or 0
   2008      * @param top The id of a drawable to place above the text, or 0
   2009      * @param right The id of a drawable to place to the right of the text, or 0
   2010      * @param bottom The id of a drawable to place below the text, or 0
   2011      */
   2012     public void setTextViewCompoundDrawables(int viewId, int left, int top, int right, int bottom) {
   2013         addAction(new TextViewDrawableAction(viewId, false, left, top, right, bottom));
   2014     }
   2015 
   2016     /**
   2017      * Equivalent to calling {@link
   2018      * TextView#setCompoundDrawablesRelativeWithIntrinsicBounds(int, int, int, int)}.
   2019      *
   2020      * @param viewId The id of the view whose text should change
   2021      * @param start The id of a drawable to place before the text (relative to the
   2022      * layout direction), or 0
   2023      * @param top The id of a drawable to place above the text, or 0
   2024      * @param end The id of a drawable to place after the text, or 0
   2025      * @param bottom The id of a drawable to place below the text, or 0
   2026      */
   2027     public void setTextViewCompoundDrawablesRelative(int viewId, int start, int top, int end, int bottom) {
   2028         addAction(new TextViewDrawableAction(viewId, true, start, top, end, bottom));
   2029     }
   2030 
   2031     /**
   2032      * Equivalent to applying a color filter on one of the drawables in
   2033      * {@link android.widget.TextView#getCompoundDrawablesRelative()}.
   2034      *
   2035      * @param viewId The id of the view whose text should change.
   2036      * @param index  The index of the drawable in the array of
   2037      *               {@link android.widget.TextView#getCompoundDrawablesRelative()} to set the color
   2038      *               filter on. Must be in [0, 3].
   2039      * @param color  The color of the color filter. See
   2040      *               {@link Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)}.
   2041      * @param mode   The mode of the color filter. See
   2042      *               {@link Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)}.
   2043      * @hide
   2044      */
   2045     public void setTextViewCompoundDrawablesRelativeColorFilter(int viewId,
   2046             int index, int color, PorterDuff.Mode mode) {
   2047         if (index < 0 || index >= 4) {
   2048             throw new IllegalArgumentException("index must be in range [0, 3].");
   2049         }
   2050         addAction(new TextViewDrawableColorFilterAction(viewId, true, index, color, mode));
   2051     }
   2052 
   2053     /**
   2054      * Equivalent to calling ImageView.setImageResource
   2055      *
   2056      * @param viewId The id of the view whose drawable should change
   2057      * @param srcId The new resource id for the drawable
   2058      */
   2059     public void setImageViewResource(int viewId, int srcId) {
   2060         setInt(viewId, "setImageResource", srcId);
   2061     }
   2062 
   2063     /**
   2064      * Equivalent to calling ImageView.setImageURI
   2065      *
   2066      * @param viewId The id of the view whose drawable should change
   2067      * @param uri The Uri for the image
   2068      */
   2069     public void setImageViewUri(int viewId, Uri uri) {
   2070         setUri(viewId, "setImageURI", uri);
   2071     }
   2072 
   2073     /**
   2074      * Equivalent to calling ImageView.setImageBitmap
   2075      *
   2076      * @param viewId The id of the view whose bitmap should change
   2077      * @param bitmap The new Bitmap for the drawable
   2078      */
   2079     public void setImageViewBitmap(int viewId, Bitmap bitmap) {
   2080         setBitmap(viewId, "setImageBitmap", bitmap);
   2081     }
   2082 
   2083     /**
   2084      * Equivalent to calling AdapterView.setEmptyView
   2085      *
   2086      * @param viewId The id of the view on which to set the empty view
   2087      * @param emptyViewId The view id of the empty view
   2088      */
   2089     public void setEmptyView(int viewId, int emptyViewId) {
   2090         addAction(new SetEmptyView(viewId, emptyViewId));
   2091     }
   2092 
   2093     /**
   2094      * Equivalent to calling {@link Chronometer#setBase Chronometer.setBase},
   2095      * {@link Chronometer#setFormat Chronometer.setFormat},
   2096      * and {@link Chronometer#start Chronometer.start()} or
   2097      * {@link Chronometer#stop Chronometer.stop()}.
   2098      *
   2099      * @param viewId The id of the {@link Chronometer} to change
   2100      * @param base The time at which the timer would have read 0:00.  This
   2101      *             time should be based off of
   2102      *             {@link android.os.SystemClock#elapsedRealtime SystemClock.elapsedRealtime()}.
   2103      * @param format The Chronometer format string, or null to
   2104      *               simply display the timer value.
   2105      * @param started True if you want the clock to be started, false if not.
   2106      */
   2107     public void setChronometer(int viewId, long base, String format, boolean started) {
   2108         setLong(viewId, "setBase", base);
   2109         setString(viewId, "setFormat", format);
   2110         setBoolean(viewId, "setStarted", started);
   2111     }
   2112 
   2113     /**
   2114      * Equivalent to calling {@link ProgressBar#setMax ProgressBar.setMax},
   2115      * {@link ProgressBar#setProgress ProgressBar.setProgress}, and
   2116      * {@link ProgressBar#setIndeterminate ProgressBar.setIndeterminate}
   2117      *
   2118      * If indeterminate is true, then the values for max and progress are ignored.
   2119      *
   2120      * @param viewId The id of the {@link ProgressBar} to change
   2121      * @param max The 100% value for the progress bar
   2122      * @param progress The current value of the progress bar.
   2123      * @param indeterminate True if the progress bar is indeterminate,
   2124      *                false if not.
   2125      */
   2126     public void setProgressBar(int viewId, int max, int progress,
   2127             boolean indeterminate) {
   2128         setBoolean(viewId, "setIndeterminate", indeterminate);
   2129         if (!indeterminate) {
   2130             setInt(viewId, "setMax", max);
   2131             setInt(viewId, "setProgress", progress);
   2132         }
   2133     }
   2134 
   2135     /**
   2136      * Equivalent to calling
   2137      * {@link android.view.View#setOnClickListener(android.view.View.OnClickListener)}
   2138      * to launch the provided {@link PendingIntent}.
   2139      *
   2140      * When setting the on-click action of items within collections (eg. {@link ListView},
   2141      * {@link StackView} etc.), this method will not work. Instead, use {@link
   2142      * RemoteViews#setPendingIntentTemplate(int, PendingIntent) in conjunction with
   2143      * RemoteViews#setOnClickFillInIntent(int, Intent).
   2144      *
   2145      * @param viewId The id of the view that will trigger the {@link PendingIntent} when clicked
   2146      * @param pendingIntent The {@link PendingIntent} to send when user clicks
   2147      */
   2148     public void setOnClickPendingIntent(int viewId, PendingIntent pendingIntent) {
   2149         addAction(new SetOnClickPendingIntent(viewId, pendingIntent));
   2150     }
   2151 
   2152     /**
   2153      * When using collections (eg. {@link ListView}, {@link StackView} etc.) in widgets, it is very
   2154      * costly to set PendingIntents on the individual items, and is hence not permitted. Instead
   2155      * this method should be used to set a single PendingIntent template on the collection, and
   2156      * individual items can differentiate their on-click behavior using
   2157      * {@link RemoteViews#setOnClickFillInIntent(int, Intent)}.
   2158      *
   2159      * @param viewId The id of the collection who's children will use this PendingIntent template
   2160      *          when clicked
   2161      * @param pendingIntentTemplate The {@link PendingIntent} to be combined with extras specified
   2162      *          by a child of viewId and executed when that child is clicked
   2163      */
   2164     public void setPendingIntentTemplate(int viewId, PendingIntent pendingIntentTemplate) {
   2165         addAction(new SetPendingIntentTemplate(viewId, pendingIntentTemplate));
   2166     }
   2167 
   2168     /**
   2169      * When using collections (eg. {@link ListView}, {@link StackView} etc.) in widgets, it is very
   2170      * costly to set PendingIntents on the individual items, and is hence not permitted. Instead
   2171      * a single PendingIntent template can be set on the collection, see {@link
   2172      * RemoteViews#setPendingIntentTemplate(int, PendingIntent)}, and the individual on-click
   2173      * action of a given item can be distinguished by setting a fillInIntent on that item. The
   2174      * fillInIntent is then combined with the PendingIntent template in order to determine the final
   2175      * intent which will be executed when the item is clicked. This works as follows: any fields
   2176      * which are left blank in the PendingIntent template, but are provided by the fillInIntent
   2177      * will be overwritten, and the resulting PendingIntent will be used.
   2178      *
   2179      *
   2180      * of the PendingIntent template will then be filled in with the associated fields that are
   2181      * set in fillInIntent. See {@link Intent#fillIn(Intent, int)} for more details.
   2182      *
   2183      * @param viewId The id of the view on which to set the fillInIntent
   2184      * @param fillInIntent The intent which will be combined with the parent's PendingIntent
   2185      *        in order to determine the on-click behavior of the view specified by viewId
   2186      */
   2187     public void setOnClickFillInIntent(int viewId, Intent fillInIntent) {
   2188         addAction(new SetOnClickFillInIntent(viewId, fillInIntent));
   2189     }
   2190 
   2191     /**
   2192      * @hide
   2193      * Equivalent to calling a combination of {@link Drawable#setAlpha(int)},
   2194      * {@link Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)},
   2195      * and/or {@link Drawable#setLevel(int)} on the {@link Drawable} of a given
   2196      * view.
   2197      * <p>
   2198      * You can omit specific calls by marking their values with null or -1.
   2199      *
   2200      * @param viewId The id of the view that contains the target
   2201      *            {@link Drawable}
   2202      * @param targetBackground If true, apply these parameters to the
   2203      *            {@link Drawable} returned by
   2204      *            {@link android.view.View#getBackground()}. Otherwise, assume
   2205      *            the target view is an {@link ImageView} and apply them to
   2206      *            {@link ImageView#getDrawable()}.
   2207      * @param alpha Specify an alpha value for the drawable, or -1 to leave
   2208      *            unchanged.
   2209      * @param colorFilter Specify a color for a
   2210      *            {@link android.graphics.ColorFilter} for this drawable. This will be ignored if
   2211      *            {@code mode} is {@code null}.
   2212      * @param mode Specify a PorterDuff mode for this drawable, or null to leave
   2213      *            unchanged.
   2214      * @param level Specify the level for the drawable, or -1 to leave
   2215      *            unchanged.
   2216      */
   2217     public void setDrawableParameters(int viewId, boolean targetBackground, int alpha,
   2218             int colorFilter, PorterDuff.Mode mode, int level) {
   2219         addAction(new SetDrawableParameters(viewId, targetBackground, alpha,
   2220                 colorFilter, mode, level));
   2221     }
   2222 
   2223     /**
   2224      * @hide
   2225      * Equivalent to calling {@link android.widget.ProgressBar#setProgressTintList}.
   2226      *
   2227      * @param viewId The id of the view whose tint should change
   2228      * @param tint the tint to apply, may be {@code null} to clear tint
   2229      */
   2230     public void setProgressTintList(int viewId, ColorStateList tint) {
   2231         addAction(new ReflectionAction(viewId, "setProgressTintList",
   2232                 ReflectionAction.COLOR_STATE_LIST, tint));
   2233     }
   2234 
   2235     /**
   2236      * @hide
   2237      * Equivalent to calling {@link android.widget.ProgressBar#setProgressBackgroundTintList}.
   2238      *
   2239      * @param viewId The id of the view whose tint should change
   2240      * @param tint the tint to apply, may be {@code null} to clear tint
   2241      */
   2242     public void setProgressBackgroundTintList(int viewId, ColorStateList tint) {
   2243         addAction(new ReflectionAction(viewId, "setProgressBackgroundTintList",
   2244                 ReflectionAction.COLOR_STATE_LIST, tint));
   2245     }
   2246 
   2247     /**
   2248      * @hide
   2249      * Equivalent to calling {@link android.widget.ProgressBar#setIndeterminateTintList}.
   2250      *
   2251      * @param viewId The id of the view whose tint should change
   2252      * @param tint the tint to apply, may be {@code null} to clear tint
   2253      */
   2254     public void setProgressIndeterminateTintList(int viewId, ColorStateList tint) {
   2255         addAction(new ReflectionAction(viewId, "setIndeterminateTintList",
   2256                 ReflectionAction.COLOR_STATE_LIST, tint));
   2257     }
   2258 
   2259     /**
   2260      * Equivalent to calling {@link android.widget.TextView#setTextColor(int)}.
   2261      *
   2262      * @param viewId The id of the view whose text color should change
   2263      * @param color Sets the text color for all the states (normal, selected,
   2264      *            focused) to be this color.
   2265      */
   2266     public void setTextColor(int viewId, int color) {
   2267         setInt(viewId, "setTextColor", color);
   2268     }
   2269 
   2270     /**
   2271      * Equivalent to calling {@link android.widget.AbsListView#setRemoteViewsAdapter(Intent)}.
   2272      *
   2273      * @param appWidgetId The id of the app widget which contains the specified view. (This
   2274      *      parameter is ignored in this deprecated method)
   2275      * @param viewId The id of the {@link AdapterView}
   2276      * @param intent The intent of the service which will be
   2277      *            providing data to the RemoteViewsAdapter
   2278      * @deprecated This method has been deprecated. See
   2279      *      {@link android.widget.RemoteViews#setRemoteAdapter(int, Intent)}
   2280      */
   2281     @Deprecated
   2282     public void setRemoteAdapter(int appWidgetId, int viewId, Intent intent) {
   2283         setRemoteAdapter(viewId, intent);
   2284     }
   2285 
   2286     /**
   2287      * Equivalent to calling {@link android.widget.AbsListView#setRemoteViewsAdapter(Intent)}.
   2288      * Can only be used for App Widgets.
   2289      *
   2290      * @param viewId The id of the {@link AdapterView}
   2291      * @param intent The intent of the service which will be
   2292      *            providing data to the RemoteViewsAdapter
   2293      */
   2294     public void setRemoteAdapter(int viewId, Intent intent) {
   2295         addAction(new SetRemoteViewsAdapterIntent(viewId, intent));
   2296     }
   2297 
   2298     /**
   2299      * Creates a simple Adapter for the viewId specified. The viewId must point to an AdapterView,
   2300      * ie. {@link ListView}, {@link GridView}, {@link StackView} or {@link AdapterViewAnimator}.
   2301      * This is a simpler but less flexible approach to populating collection widgets. Its use is
   2302      * encouraged for most scenarios, as long as the total memory within the list of RemoteViews
   2303      * is relatively small (ie. doesn't contain large or numerous Bitmaps, see {@link
   2304      * RemoteViews#setImageViewBitmap}). In the case of numerous images, the use of API is still
   2305      * possible by setting image URIs instead of Bitmaps, see {@link RemoteViews#setImageViewUri}.
   2306      *
   2307      * This API is supported in the compatibility library for previous API levels, see
   2308      * RemoteViewsCompat.
   2309      *
   2310      * @param viewId The id of the {@link AdapterView}
   2311      * @param list The list of RemoteViews which will populate the view specified by viewId.
   2312      * @param viewTypeCount The maximum number of unique layout id's used to construct the list of
   2313      *      RemoteViews. This count cannot change during the life-cycle of a given widget, so this
   2314      *      parameter should account for the maximum possible number of types that may appear in the
   2315      *      See {@link Adapter#getViewTypeCount()}.
   2316      *
   2317      * @hide
   2318      */
   2319     public void setRemoteAdapter(int viewId, ArrayList<RemoteViews> list, int viewTypeCount) {
   2320         addAction(new SetRemoteViewsAdapterList(viewId, list, viewTypeCount));
   2321     }
   2322 
   2323     /**
   2324      * Equivalent to calling {@link android.widget.AbsListView#smoothScrollToPosition(int, int)}.
   2325      *
   2326      * @param viewId The id of the view to change
   2327      * @param position Scroll to this adapter position
   2328      */
   2329     public void setScrollPosition(int viewId, int position) {
   2330         setInt(viewId, "smoothScrollToPosition", position);
   2331     }
   2332 
   2333     /**
   2334      * Equivalent to calling {@link android.widget.AbsListView#smoothScrollToPosition(int, int)}.
   2335      *
   2336      * @param viewId The id of the view to change
   2337      * @param offset Scroll by this adapter position offset
   2338      */
   2339     public void setRelativeScrollPosition(int viewId, int offset) {
   2340         setInt(viewId, "smoothScrollByOffset", offset);
   2341     }
   2342 
   2343     /**
   2344      * Equivalent to calling {@link android.view.View#setPadding(int, int, int, int)}.
   2345      *
   2346      * @param viewId The id of the view to change
   2347      * @param left the left padding in pixels
   2348      * @param top the top padding in pixels
   2349      * @param right the right padding in pixels
   2350      * @param bottom the bottom padding in pixels
   2351      */
   2352     public void setViewPadding(int viewId, int left, int top, int right, int bottom) {
   2353         addAction(new ViewPaddingAction(viewId, left, top, right, bottom));
   2354     }
   2355 
   2356     /**
   2357      * Call a method taking one boolean on a view in the layout for this RemoteViews.
   2358      *
   2359      * @param viewId The id of the view on which to call the method.
   2360      * @param methodName The name of the method to call.
   2361      * @param value The value to pass to the method.
   2362      */
   2363     public void setBoolean(int viewId, String methodName, boolean value) {
   2364         addAction(new ReflectionAction(viewId, methodName, ReflectionAction.BOOLEAN, value));
   2365     }
   2366 
   2367     /**
   2368      * Call a method taking one byte on a view in the layout for this RemoteViews.
   2369      *
   2370      * @param viewId The id of the view on which to call the method.
   2371      * @param methodName The name of the method to call.
   2372      * @param value The value to pass to the method.
   2373      */
   2374     public void setByte(int viewId, String methodName, byte value) {
   2375         addAction(new ReflectionAction(viewId, methodName, ReflectionAction.BYTE, value));
   2376     }
   2377 
   2378     /**
   2379      * Call a method taking one short on a view in the layout for this RemoteViews.
   2380      *
   2381      * @param viewId The id of the view on which to call the method.
   2382      * @param methodName The name of the method to call.
   2383      * @param value The value to pass to the method.
   2384      */
   2385     public void setShort(int viewId, String methodName, short value) {
   2386         addAction(new ReflectionAction(viewId, methodName, ReflectionAction.SHORT, value));
   2387     }
   2388 
   2389     /**
   2390      * Call a method taking one int on a view in the layout for this RemoteViews.
   2391      *
   2392      * @param viewId The id of the view on which to call the method.
   2393      * @param methodName The name of the method to call.
   2394      * @param value The value to pass to the method.
   2395      */
   2396     public void setInt(int viewId, String methodName, int value) {
   2397         addAction(new ReflectionAction(viewId, methodName, ReflectionAction.INT, value));
   2398     }
   2399 
   2400     /**
   2401      * Call a method taking one long on a view in the layout for this RemoteViews.
   2402      *
   2403      * @param viewId The id of the view on which to call the method.
   2404      * @param methodName The name of the method to call.
   2405      * @param value The value to pass to the method.
   2406      */
   2407     public void setLong(int viewId, String methodName, long value) {
   2408         addAction(new ReflectionAction(viewId, methodName, ReflectionAction.LONG, value));
   2409     }
   2410 
   2411     /**
   2412      * Call a method taking one float on a view in the layout for this RemoteViews.
   2413      *
   2414      * @param viewId The id of the view on which to call the method.
   2415      * @param methodName The name of the method to call.
   2416      * @param value The value to pass to the method.
   2417      */
   2418     public void setFloat(int viewId, String methodName, float value) {
   2419         addAction(new ReflectionAction(viewId, methodName, ReflectionAction.FLOAT, value));
   2420     }
   2421 
   2422     /**
   2423      * Call a method taking one double on a view in the layout for this RemoteViews.
   2424      *
   2425      * @param viewId The id of the view on which to call the method.
   2426      * @param methodName The name of the method to call.
   2427      * @param value The value to pass to the method.
   2428      */
   2429     public void setDouble(int viewId, String methodName, double value) {
   2430         addAction(new ReflectionAction(viewId, methodName, ReflectionAction.DOUBLE, value));
   2431     }
   2432 
   2433     /**
   2434      * Call a method taking one char on a view in the layout for this RemoteViews.
   2435      *
   2436      * @param viewId The id of the view on which to call the method.
   2437      * @param methodName The name of the method to call.
   2438      * @param value The value to pass to the method.
   2439      */
   2440     public void setChar(int viewId, String methodName, char value) {
   2441         addAction(new ReflectionAction(viewId, methodName, ReflectionAction.CHAR, value));
   2442     }
   2443 
   2444     /**
   2445      * Call a method taking one String on a view in the layout for this RemoteViews.
   2446      *
   2447      * @param viewId The id of the view on which to call the method.
   2448      * @param methodName The name of the method to call.
   2449      * @param value The value to pass to the method.
   2450      */
   2451     public void setString(int viewId, String methodName, String value) {
   2452         addAction(new ReflectionAction(viewId, methodName, ReflectionAction.STRING, value));
   2453     }
   2454 
   2455     /**
   2456      * Call a method taking one CharSequence on a view in the layout for this RemoteViews.
   2457      *
   2458      * @param viewId The id of the view on which to call the method.
   2459      * @param methodName The name of the method to call.
   2460      * @param value The value to pass to the method.
   2461      */
   2462     public void setCharSequence(int viewId, String methodName, CharSequence value) {
   2463         addAction(new ReflectionAction(viewId, methodName, ReflectionAction.CHAR_SEQUENCE, value));
   2464     }
   2465 
   2466     /**
   2467      * Call a method taking one Uri on a view in the layout for this RemoteViews.
   2468      *
   2469      * @param viewId The id of the view on which to call the method.
   2470      * @param methodName The name of the method to call.
   2471      * @param value The value to pass to the method.
   2472      */
   2473     public void setUri(int viewId, String methodName, Uri value) {
   2474         if (value != null) {
   2475             // Resolve any filesystem path before sending remotely
   2476             value = value.getCanonicalUri();
   2477             if (StrictMode.vmFileUriExposureEnabled()) {
   2478                 value.checkFileUriExposed("RemoteViews.setUri()");
   2479             }
   2480         }
   2481         addAction(new ReflectionAction(viewId, methodName, ReflectionAction.URI, value));
   2482     }
   2483 
   2484     /**
   2485      * Call a method taking one Bitmap on a view in the layout for this RemoteViews.
   2486      * @more
   2487      * <p class="note">The bitmap will be flattened into the parcel if this object is
   2488      * sent across processes, so it may end up using a lot of memory, and may be fairly slow.</p>
   2489      *
   2490      * @param viewId The id of the view on which to call the method.
   2491      * @param methodName The name of the method to call.
   2492      * @param value The value to pass to the method.
   2493      */
   2494     public void setBitmap(int viewId, String methodName, Bitmap value) {
   2495         addAction(new BitmapReflectionAction(viewId, methodName, value));
   2496     }
   2497 
   2498     /**
   2499      * Call a method taking one Bundle on a view in the layout for this RemoteViews.
   2500      *
   2501      * @param viewId The id of the view on which to call the method.
   2502      * @param methodName The name of the method to call.
   2503      * @param value The value to pass to the method.
   2504      */
   2505     public void setBundle(int viewId, String methodName, Bundle value) {
   2506         addAction(new ReflectionAction(viewId, methodName, ReflectionAction.BUNDLE, value));
   2507     }
   2508 
   2509     /**
   2510      * Call a method taking one Intent on a view in the layout for this RemoteViews.
   2511      *
   2512      * @param viewId The id of the view on which to call the method.
   2513      * @param methodName The name of the method to call.
   2514      * @param value The {@link android.content.Intent} to pass the method.
   2515      */
   2516     public void setIntent(int viewId, String methodName, Intent value) {
   2517         addAction(new ReflectionAction(viewId, methodName, ReflectionAction.INTENT, value));
   2518     }
   2519 
   2520     /**
   2521      * Equivalent to calling View.setContentDescription(CharSequence).
   2522      *
   2523      * @param viewId The id of the view whose content description should change.
   2524      * @param contentDescription The new content description for the view.
   2525      */
   2526     public void setContentDescription(int viewId, CharSequence contentDescription) {
   2527         setCharSequence(viewId, "setContentDescription", contentDescription);
   2528     }
   2529 
   2530     /**
   2531      * Equivalent to calling {@link android.view.View#setAccessibilityTraversalBefore(int)}.
   2532      *
   2533      * @param viewId The id of the view whose before view in accessibility traversal to set.
   2534      * @param nextId The id of the next in the accessibility traversal.
   2535      **/
   2536     public void setAccessibilityTraversalBefore(int viewId, int nextId) {
   2537         setInt(viewId, "setAccessibilityTraversalBefore", nextId);
   2538     }
   2539 
   2540     /**
   2541      * Equivalent to calling {@link android.view.View#setAccessibilityTraversalAfter(int)}.
   2542      *
   2543      * @param viewId The id of the view whose after view in accessibility traversal to set.
   2544      * @param nextId The id of the next in the accessibility traversal.
   2545      **/
   2546     public void setAccessibilityTraversalAfter(int viewId, int nextId) {
   2547         setInt(viewId, "setAccessibilityTraversalAfter", nextId);
   2548     }
   2549 
   2550     /**
   2551      * Equivalent to calling View.setLabelFor(int).
   2552      *
   2553      * @param viewId The id of the view whose property to set.
   2554      * @param labeledId The id of a view for which this view serves as a label.
   2555      */
   2556     public void setLabelFor(int viewId, int labeledId) {
   2557         setInt(viewId, "setLabelFor", labeledId);
   2558     }
   2559 
   2560     private RemoteViews getRemoteViewsToApply(Context context) {
   2561         if (hasLandscapeAndPortraitLayouts()) {
   2562             int orientation = context.getResources().getConfiguration().orientation;
   2563             if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
   2564                 return mLandscape;
   2565             } else {
   2566                 return mPortrait;
   2567             }
   2568         }
   2569         return this;
   2570     }
   2571 
   2572     /**
   2573      * Inflates the view hierarchy represented by this object and applies
   2574      * all of the actions.
   2575      *
   2576      * <p><strong>Caller beware: this may throw</strong>
   2577      *
   2578      * @param context Default context to use
   2579      * @param parent Parent that the resulting view hierarchy will be attached to. This method
   2580      * does <strong>not</strong> attach the hierarchy. The caller should do so when appropriate.
   2581      * @return The inflated view hierarchy
   2582      */
   2583     public View apply(Context context, ViewGroup parent) {
   2584         return apply(context, parent, null);
   2585     }
   2586 
   2587     /** @hide */
   2588     public View apply(Context context, ViewGroup parent, OnClickHandler handler) {
   2589         RemoteViews rvToApply = getRemoteViewsToApply(context);
   2590 
   2591         View result;
   2592         // RemoteViews may be built by an application installed in another
   2593         // user. So build a context that loads resources from that user but
   2594         // still returns the current users userId so settings like data / time formats
   2595         // are loaded without requiring cross user persmissions.
   2596         final Context contextForResources = getContextForResources(context);
   2597         Context inflationContext = new ContextWrapper(context) {
   2598             @Override
   2599             public Resources getResources() {
   2600                 return contextForResources.getResources();
   2601             }
   2602             @Override
   2603             public Resources.Theme getTheme() {
   2604                 return contextForResources.getTheme();
   2605             }
   2606         };
   2607 
   2608         LayoutInflater inflater = (LayoutInflater)
   2609                 context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
   2610 
   2611         // Clone inflater so we load resources from correct context and
   2612         // we don't add a filter to the static version returned by getSystemService.
   2613         inflater = inflater.cloneInContext(inflationContext);
   2614         inflater.setFilter(this);
   2615         result = inflater.inflate(rvToApply.getLayoutId(), parent, false);
   2616 
   2617         rvToApply.performApply(result, parent, handler);
   2618 
   2619         return result;
   2620     }
   2621 
   2622     /**
   2623      * Applies all of the actions to the provided view.
   2624      *
   2625      * <p><strong>Caller beware: this may throw</strong>
   2626      *
   2627      * @param v The view to apply the actions to.  This should be the result of
   2628      * the {@link #apply(Context,ViewGroup)} call.
   2629      */
   2630     public void reapply(Context context, View v) {
   2631         reapply(context, v, null);
   2632     }
   2633 
   2634     /** @hide */
   2635     public void reapply(Context context, View v, OnClickHandler handler) {
   2636         RemoteViews rvToApply = getRemoteViewsToApply(context);
   2637 
   2638         // In the case that a view has this RemoteViews applied in one orientation, is persisted
   2639         // across orientation change, and has the RemoteViews re-applied in the new orientation,
   2640         // we throw an exception, since the layouts may be completely unrelated.
   2641         if (hasLandscapeAndPortraitLayouts()) {
   2642             if (v.getId() != rvToApply.getLayoutId()) {
   2643                 throw new RuntimeException("Attempting to re-apply RemoteViews to a view that" +
   2644                         " that does not share the same root layout id.");
   2645             }
   2646         }
   2647 
   2648         rvToApply.performApply(v, (ViewGroup) v.getParent(), handler);
   2649     }
   2650 
   2651     private void performApply(View v, ViewGroup parent, OnClickHandler handler) {
   2652         if (mActions != null) {
   2653             handler = handler == null ? DEFAULT_ON_CLICK_HANDLER : handler;
   2654             final int count = mActions.size();
   2655             for (int i = 0; i < count; i++) {
   2656                 Action a = mActions.get(i);
   2657                 a.apply(v, parent, handler);
   2658             }
   2659         }
   2660     }
   2661 
   2662     private Context getContextForResources(Context context) {
   2663         if (mApplication != null) {
   2664             if (context.getUserId() == UserHandle.getUserId(mApplication.uid)
   2665                     && context.getPackageName().equals(mApplication.packageName)) {
   2666                 return context;
   2667             }
   2668             try {
   2669                 return context.createApplicationContext(mApplication,
   2670                         Context.CONTEXT_RESTRICTED);
   2671             } catch (NameNotFoundException e) {
   2672                 Log.e(LOG_TAG, "Package name " + mApplication.packageName + " not found");
   2673             }
   2674         }
   2675 
   2676         return context;
   2677     }
   2678 
   2679     /**
   2680      * Returns the number of actions in this RemoteViews. Can be used as a sequence number.
   2681      *
   2682      * @hide
   2683      */
   2684     public int getSequenceNumber() {
   2685         return (mActions == null) ? 0 : mActions.size();
   2686     }
   2687 
   2688     /* (non-Javadoc)
   2689      * Used to restrict the views which can be inflated
   2690      *
   2691      * @see android.view.LayoutInflater.Filter#onLoadClass(java.lang.Class)
   2692      */
   2693     public boolean onLoadClass(Class clazz) {
   2694         return clazz.isAnnotationPresent(RemoteView.class);
   2695     }
   2696 
   2697     public int describeContents() {
   2698         return 0;
   2699     }
   2700 
   2701     public void writeToParcel(Parcel dest, int flags) {
   2702         if (!hasLandscapeAndPortraitLayouts()) {
   2703             dest.writeInt(MODE_NORMAL);
   2704             // We only write the bitmap cache if we are the root RemoteViews, as this cache
   2705             // is shared by all children.
   2706             if (mIsRoot) {
   2707                 mBitmapCache.writeBitmapsToParcel(dest, flags);
   2708             }
   2709             dest.writeParcelable(mApplication, flags);
   2710             dest.writeInt(mLayoutId);
   2711             dest.writeInt(mIsWidgetCollectionChild ? 1 : 0);
   2712             int count;
   2713             if (mActions != null) {
   2714                 count = mActions.size();
   2715             } else {
   2716                 count = 0;
   2717             }
   2718             dest.writeInt(count);
   2719             for (int i=0; i<count; i++) {
   2720                 Action a = mActions.get(i);
   2721                 a.writeToParcel(dest, 0);
   2722             }
   2723         } else {
   2724             dest.writeInt(MODE_HAS_LANDSCAPE_AND_PORTRAIT);
   2725             // We only write the bitmap cache if we are the root RemoteViews, as this cache
   2726             // is shared by all children.
   2727             if (mIsRoot) {
   2728                 mBitmapCache.writeBitmapsToParcel(dest, flags);
   2729             }
   2730             mLandscape.writeToParcel(dest, flags);
   2731             mPortrait.writeToParcel(dest, flags);
   2732         }
   2733     }
   2734 
   2735     private static ApplicationInfo getApplicationInfo(String packageName, int userId) {
   2736         if (packageName == null) {
   2737             return null;
   2738         }
   2739 
   2740         // Get the application for the passed in package and user.
   2741         Application application = ActivityThread.currentApplication();
   2742         if (application == null) {
   2743             throw new IllegalStateException("Cannot create remote views out of an aplication.");
   2744         }
   2745 
   2746         ApplicationInfo applicationInfo = application.getApplicationInfo();
   2747         if (UserHandle.getUserId(applicationInfo.uid) != userId
   2748                 || !applicationInfo.packageName.equals(packageName)) {
   2749             try {
   2750                 Context context = application.getBaseContext().createPackageContextAsUser(
   2751                         packageName, 0, new UserHandle(userId));
   2752                 applicationInfo = context.getApplicationInfo();
   2753             } catch (NameNotFoundException nnfe) {
   2754                 throw new IllegalArgumentException("No such package " + packageName);
   2755             }
   2756         }
   2757 
   2758         return applicationInfo;
   2759     }
   2760 
   2761     /**
   2762      * Parcelable.Creator that instantiates RemoteViews objects
   2763      */
   2764     public static final Parcelable.Creator<RemoteViews> CREATOR = new Parcelable.Creator<RemoteViews>() {
   2765         public RemoteViews createFromParcel(Parcel parcel) {
   2766             return new RemoteViews(parcel);
   2767         }
   2768 
   2769         public RemoteViews[] newArray(int size) {
   2770             return new RemoteViews[size];
   2771         }
   2772     };
   2773 }
   2774