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