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