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 java.lang.annotation.ElementType;
     20 import java.lang.annotation.Retention;
     21 import java.lang.annotation.RetentionPolicy;
     22 import java.lang.annotation.Target;
     23 import java.lang.reflect.Method;
     24 import java.util.ArrayList;
     25 
     26 import android.app.PendingIntent;
     27 import android.appwidget.AppWidgetHostView;
     28 import android.content.Context;
     29 import android.content.Intent;
     30 import android.content.IntentSender;
     31 import android.content.pm.PackageManager.NameNotFoundException;
     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.Bundle;
     38 import android.os.Parcel;
     39 import android.os.Parcelable;
     40 import android.text.TextUtils;
     41 import android.util.Log;
     42 import android.view.LayoutInflater;
     43 import android.view.RemotableViewMethod;
     44 import android.view.View;
     45 import android.view.ViewGroup;
     46 import android.view.LayoutInflater.Filter;
     47 import android.view.View.OnClickListener;
     48 import android.widget.AdapterView.OnItemClickListener;
     49 
     50 
     51 /**
     52  * A class that describes a view hierarchy that can be displayed in
     53  * another process. The hierarchy is inflated from a layout resource
     54  * file, and this class provides some basic operations for modifying
     55  * the content of the inflated hierarchy.
     56  */
     57 public class RemoteViews implements Parcelable, Filter {
     58 
     59     private static final String LOG_TAG = "RemoteViews";
     60 
     61     /**
     62      * The intent extra that contains the appWidgetId.
     63      * @hide
     64      */
     65     static final String EXTRA_REMOTEADAPTER_APPWIDGET_ID = "remoteAdapterAppWidgetId";
     66 
     67     /**
     68      * The package name of the package containing the layout
     69      * resource. (Added to the parcel)
     70      */
     71     private final String mPackage;
     72 
     73     /**
     74      * The resource ID of the layout file. (Added to the parcel)
     75      */
     76     private final int mLayoutId;
     77 
     78     /**
     79      * An array of actions to perform on the view tree once it has been
     80      * inflated
     81      */
     82     private ArrayList<Action> mActions;
     83 
     84     /**
     85      * A class to keep track of memory usage by this RemoteViews
     86      */
     87     private MemoryUsageCounter mMemoryUsageCounter;
     88 
     89 
     90     /**
     91      * This flag indicates whether this RemoteViews object is being created from a
     92      * RemoteViewsService for use as a child of a widget collection. This flag is used
     93      * to determine whether or not certain features are available, in particular,
     94      * setting on click extras and setting on click pending intents. The former is enabled,
     95      * and the latter disabled when this flag is true.
     96      */
     97      private boolean mIsWidgetCollectionChild = false;
     98 
     99     /**
    100      * This annotation indicates that a subclass of View is alllowed to be used
    101      * with the {@link RemoteViews} mechanism.
    102      */
    103     @Target({ ElementType.TYPE })
    104     @Retention(RetentionPolicy.RUNTIME)
    105     public @interface RemoteView {
    106     }
    107 
    108     /**
    109      * Exception to send when something goes wrong executing an action
    110      *
    111      */
    112     public static class ActionException extends RuntimeException {
    113         public ActionException(Exception ex) {
    114             super(ex);
    115         }
    116         public ActionException(String message) {
    117             super(message);
    118         }
    119     }
    120 
    121     /**
    122      * Base class for all actions that can be performed on an
    123      * inflated view.
    124      *
    125      *  SUBCLASSES MUST BE IMMUTABLE SO CLONE WORKS!!!!!
    126      */
    127     private abstract static class Action implements Parcelable {
    128         public abstract void apply(View root, ViewGroup rootParent) throws ActionException;
    129 
    130         public int describeContents() {
    131             return 0;
    132         }
    133 
    134         /**
    135          * Overridden by each class to report on it's own memory usage
    136          */
    137         public void updateMemoryUsageEstimate(MemoryUsageCounter counter) {
    138             // We currently only calculate Bitmap memory usage, so by default, don't do anything
    139             // here
    140             return;
    141         }
    142 
    143         protected boolean startIntentSafely(Context context, PendingIntent pendingIntent,
    144                 Intent fillInIntent) {
    145             try {
    146                 // TODO: Unregister this handler if PendingIntent.FLAG_ONE_SHOT?
    147                 context.startIntentSender(
    148                         pendingIntent.getIntentSender(), fillInIntent,
    149                         Intent.FLAG_ACTIVITY_NEW_TASK,
    150                         Intent.FLAG_ACTIVITY_NEW_TASK, 0);
    151             } catch (IntentSender.SendIntentException e) {
    152                 android.util.Log.e(LOG_TAG, "Cannot send pending intent: ", e);
    153                 return false;
    154             } catch (Exception e) {
    155                 android.util.Log.e(LOG_TAG, "Cannot send pending intent due to " +
    156                         "unknown exception: ", e);
    157                 return false;
    158             }
    159             return true;
    160         }
    161     }
    162 
    163     private class SetEmptyView extends Action {
    164         int viewId;
    165         int emptyViewId;
    166 
    167         public final static int TAG = 6;
    168 
    169         SetEmptyView(int viewId, int emptyViewId) {
    170             this.viewId = viewId;
    171             this.emptyViewId = emptyViewId;
    172         }
    173 
    174         SetEmptyView(Parcel in) {
    175             this.viewId = in.readInt();
    176             this.emptyViewId = in.readInt();
    177         }
    178 
    179         public void writeToParcel(Parcel out, int flags) {
    180             out.writeInt(TAG);
    181             out.writeInt(this.viewId);
    182             out.writeInt(this.emptyViewId);
    183         }
    184 
    185         @Override
    186         public void apply(View root, ViewGroup rootParent) {
    187             final View view = root.findViewById(viewId);
    188             if (!(view instanceof AdapterView<?>)) return;
    189 
    190             AdapterView<?> adapterView = (AdapterView<?>) view;
    191 
    192             final View emptyView = root.findViewById(emptyViewId);
    193             if (emptyView == null) return;
    194 
    195             adapterView.setEmptyView(emptyView);
    196         }
    197     }
    198 
    199     private class SetOnClickFillInIntent extends Action {
    200         public SetOnClickFillInIntent(int id, Intent fillInIntent) {
    201             this.viewId = id;
    202             this.fillInIntent = fillInIntent;
    203         }
    204 
    205         public SetOnClickFillInIntent(Parcel parcel) {
    206             viewId = parcel.readInt();
    207             fillInIntent = Intent.CREATOR.createFromParcel(parcel);
    208         }
    209 
    210         public void writeToParcel(Parcel dest, int flags) {
    211             dest.writeInt(TAG);
    212             dest.writeInt(viewId);
    213             fillInIntent.writeToParcel(dest, 0 /* no flags */);
    214         }
    215 
    216         @Override
    217         public void apply(View root, ViewGroup rootParent) {
    218             final View target = root.findViewById(viewId);
    219             if (target == null) return;
    220 
    221             if (!mIsWidgetCollectionChild) {
    222                 Log.e("RemoteViews", "The method setOnClickFillInIntent is available " +
    223                         "only from RemoteViewsFactory (ie. on collection items).");
    224                 return;
    225             }
    226             if (target == root) {
    227                 target.setTagInternal(com.android.internal.R.id.fillInIntent, fillInIntent);
    228             } else if (target != null && fillInIntent != null) {
    229                 OnClickListener listener = new OnClickListener() {
    230                     public void onClick(View v) {
    231                         // Insure that this view is a child of an AdapterView
    232                         View parent = (View) v.getParent();
    233                         while (!(parent instanceof AdapterView<?>)
    234                                 && !(parent instanceof AppWidgetHostView)) {
    235                             parent = (View) parent.getParent();
    236                         }
    237 
    238                         if (parent instanceof AppWidgetHostView) {
    239                             // Somehow they've managed to get this far without having
    240                             // and AdapterView as a parent.
    241                             Log.e("RemoteViews", "Collection item doesn't have AdapterView parent");
    242                             return;
    243                         }
    244 
    245                         // Insure that a template pending intent has been set on an ancestor
    246                         if (!(parent.getTag() instanceof PendingIntent)) {
    247                             Log.e("RemoteViews", "Attempting setOnClickFillInIntent without" +
    248                                     " calling setPendingIntentTemplate on parent.");
    249                             return;
    250                         }
    251 
    252                         PendingIntent pendingIntent = (PendingIntent) parent.getTag();
    253 
    254                         final float appScale = v.getContext().getResources()
    255                                 .getCompatibilityInfo().applicationScale;
    256                         final int[] pos = new int[2];
    257                         v.getLocationOnScreen(pos);
    258 
    259                         final Rect rect = new Rect();
    260                         rect.left = (int) (pos[0] * appScale + 0.5f);
    261                         rect.top = (int) (pos[1] * appScale + 0.5f);
    262                         rect.right = (int) ((pos[0] + v.getWidth()) * appScale + 0.5f);
    263                         rect.bottom = (int) ((pos[1] + v.getHeight()) * appScale + 0.5f);
    264 
    265                         fillInIntent.setSourceBounds(rect);
    266                         startIntentSafely(v.getContext(), pendingIntent, fillInIntent);
    267                     }
    268 
    269                 };
    270                 target.setOnClickListener(listener);
    271             }
    272         }
    273 
    274         int viewId;
    275         Intent fillInIntent;
    276 
    277         public final static int TAG = 9;
    278     }
    279 
    280     private class SetPendingIntentTemplate extends Action {
    281         public SetPendingIntentTemplate(int id, PendingIntent pendingIntentTemplate) {
    282             this.viewId = id;
    283             this.pendingIntentTemplate = pendingIntentTemplate;
    284         }
    285 
    286         public SetPendingIntentTemplate(Parcel parcel) {
    287             viewId = parcel.readInt();
    288             pendingIntentTemplate = PendingIntent.readPendingIntentOrNullFromParcel(parcel);
    289         }
    290 
    291         public void writeToParcel(Parcel dest, int flags) {
    292             dest.writeInt(TAG);
    293             dest.writeInt(viewId);
    294             pendingIntentTemplate.writeToParcel(dest, 0 /* no flags */);
    295         }
    296 
    297         @Override
    298         public void apply(View root, ViewGroup rootParent) {
    299             final View target = root.findViewById(viewId);
    300             if (target == null) return;
    301 
    302             // If the view isn't an AdapterView, setting a PendingIntent template doesn't make sense
    303             if (target instanceof AdapterView<?>) {
    304                 AdapterView<?> av = (AdapterView<?>) target;
    305                 // The PendingIntent template is stored in the view's tag.
    306                 OnItemClickListener listener = new OnItemClickListener() {
    307                     public void onItemClick(AdapterView<?> parent, View view,
    308                             int position, long id) {
    309                         // The view should be a frame layout
    310                         if (view instanceof ViewGroup) {
    311                             ViewGroup vg = (ViewGroup) view;
    312 
    313                             // AdapterViews contain their children in a frame
    314                             // so we need to go one layer deeper here.
    315                             if (parent instanceof AdapterViewAnimator) {
    316                                 vg = (ViewGroup) vg.getChildAt(0);
    317                             }
    318                             if (vg == null) return;
    319 
    320                             Intent fillInIntent = null;
    321                             int childCount = vg.getChildCount();
    322                             for (int i = 0; i < childCount; i++) {
    323                                 Object tag = vg.getChildAt(i).getTag(com.android.internal.R.id.fillInIntent);
    324                                 if (tag instanceof Intent) {
    325                                     fillInIntent = (Intent) tag;
    326                                     break;
    327                                 }
    328                             }
    329                             if (fillInIntent == null) return;
    330 
    331                             final float appScale = view.getContext().getResources()
    332                                     .getCompatibilityInfo().applicationScale;
    333                             final int[] pos = new int[2];
    334                             view.getLocationOnScreen(pos);
    335 
    336                             final Rect rect = new Rect();
    337                             rect.left = (int) (pos[0] * appScale + 0.5f);
    338                             rect.top = (int) (pos[1] * appScale + 0.5f);
    339                             rect.right = (int) ((pos[0] + view.getWidth()) * appScale + 0.5f);
    340                             rect.bottom = (int) ((pos[1] + view.getHeight()) * appScale + 0.5f);
    341 
    342                             final Intent intent = new Intent();
    343                             intent.setSourceBounds(rect);
    344                             startIntentSafely(view.getContext(), pendingIntentTemplate, fillInIntent);
    345                         }
    346                     }
    347                 };
    348                 av.setOnItemClickListener(listener);
    349                 av.setTag(pendingIntentTemplate);
    350             } else {
    351                 Log.e("RemoteViews", "Cannot setPendingIntentTemplate on a view which is not" +
    352                         "an AdapterView (id: " + viewId + ")");
    353                 return;
    354             }
    355         }
    356 
    357         int viewId;
    358         PendingIntent pendingIntentTemplate;
    359 
    360         public final static int TAG = 8;
    361     }
    362 
    363     private class SetRemoteViewsAdapterIntent extends Action {
    364         public SetRemoteViewsAdapterIntent(int id, Intent intent) {
    365             this.viewId = id;
    366             this.intent = intent;
    367         }
    368 
    369         public SetRemoteViewsAdapterIntent(Parcel parcel) {
    370             viewId = parcel.readInt();
    371             intent = Intent.CREATOR.createFromParcel(parcel);
    372         }
    373 
    374         public void writeToParcel(Parcel dest, int flags) {
    375             dest.writeInt(TAG);
    376             dest.writeInt(viewId);
    377             intent.writeToParcel(dest, flags);
    378         }
    379 
    380         @Override
    381         public void apply(View root, ViewGroup rootParent) {
    382             final View target = root.findViewById(viewId);
    383             if (target == null) return;
    384 
    385             // Ensure that we are applying to an AppWidget root
    386             if (!(rootParent instanceof AppWidgetHostView)) {
    387                 Log.e("RemoteViews", "SetRemoteViewsAdapterIntent action can only be used for " +
    388                         "AppWidgets (root id: " + viewId + ")");
    389                 return;
    390             }
    391             // Ensure that we are calling setRemoteAdapter on an AdapterView that supports it
    392             if (!(target instanceof AbsListView) && !(target instanceof AdapterViewAnimator)) {
    393                 Log.e("RemoteViews", "Cannot setRemoteViewsAdapter on a view which is not " +
    394                         "an AbsListView or AdapterViewAnimator (id: " + viewId + ")");
    395                 return;
    396             }
    397 
    398             // Embed the AppWidget Id for use in RemoteViewsAdapter when connecting to the intent
    399             // RemoteViewsService
    400             AppWidgetHostView host = (AppWidgetHostView) rootParent;
    401             intent.putExtra(EXTRA_REMOTEADAPTER_APPWIDGET_ID, host.getAppWidgetId());
    402             if (target instanceof AbsListView) {
    403                 AbsListView v = (AbsListView) target;
    404                 v.setRemoteViewsAdapter(intent);
    405             } else if (target instanceof AdapterViewAnimator) {
    406                 AdapterViewAnimator v = (AdapterViewAnimator) target;
    407                 v.setRemoteViewsAdapter(intent);
    408             }
    409         }
    410 
    411         int viewId;
    412         Intent intent;
    413 
    414         public final static int TAG = 10;
    415     }
    416 
    417     /**
    418      * Equivalent to calling
    419      * {@link android.view.View#setOnClickListener(android.view.View.OnClickListener)}
    420      * to launch the provided {@link PendingIntent}.
    421      */
    422     private class SetOnClickPendingIntent extends Action {
    423         public SetOnClickPendingIntent(int id, PendingIntent pendingIntent) {
    424             this.viewId = id;
    425             this.pendingIntent = pendingIntent;
    426         }
    427 
    428         public SetOnClickPendingIntent(Parcel parcel) {
    429             viewId = parcel.readInt();
    430             pendingIntent = PendingIntent.readPendingIntentOrNullFromParcel(parcel);
    431         }
    432 
    433         public void writeToParcel(Parcel dest, int flags) {
    434             dest.writeInt(TAG);
    435             dest.writeInt(viewId);
    436             pendingIntent.writeToParcel(dest, 0 /* no flags */);
    437         }
    438 
    439         @Override
    440         public void apply(View root, ViewGroup rootParent) {
    441             final View target = root.findViewById(viewId);
    442             if (target == null) return;
    443 
    444             // If the view is an AdapterView, setting a PendingIntent on click doesn't make much
    445             // sense, do they mean to set a PendingIntent template for the AdapterView's children?
    446             if (mIsWidgetCollectionChild) {
    447                 Log.e("RemoteViews", "Cannot setOnClickPendingIntent for collection item " +
    448 				"(id: " + viewId + ")");
    449                 // TODO: return; We'll let this slide until apps are up to date.
    450             }
    451 
    452             if (target != null && pendingIntent != null) {
    453                 OnClickListener listener = new OnClickListener() {
    454                     public void onClick(View v) {
    455                         // Find target view location in screen coordinates and
    456                         // fill into PendingIntent before sending.
    457                         final float appScale = v.getContext().getResources()
    458                                 .getCompatibilityInfo().applicationScale;
    459                         final int[] pos = new int[2];
    460                         v.getLocationOnScreen(pos);
    461 
    462                         final Rect rect = new Rect();
    463                         rect.left = (int) (pos[0] * appScale + 0.5f);
    464                         rect.top = (int) (pos[1] * appScale + 0.5f);
    465                         rect.right = (int) ((pos[0] + v.getWidth()) * appScale + 0.5f);
    466                         rect.bottom = (int) ((pos[1] + v.getHeight()) * appScale + 0.5f);
    467 
    468                         final Intent intent = new Intent();
    469                         intent.setSourceBounds(rect);
    470                         startIntentSafely(v.getContext(), pendingIntent, intent);
    471                     }
    472                 };
    473                 target.setOnClickListener(listener);
    474             }
    475         }
    476 
    477         int viewId;
    478         PendingIntent pendingIntent;
    479 
    480         public final static int TAG = 1;
    481     }
    482 
    483     /**
    484      * Equivalent to calling a combination of {@link Drawable#setAlpha(int)},
    485      * {@link Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)},
    486      * and/or {@link Drawable#setLevel(int)} on the {@link Drawable} of a given view.
    487      * <p>
    488      * These operations will be performed on the {@link Drawable} returned by the
    489      * target {@link View#getBackground()} by default.  If targetBackground is false,
    490      * we assume the target is an {@link ImageView} and try applying the operations
    491      * to {@link ImageView#getDrawable()}.
    492      * <p>
    493      * You can omit specific calls by marking their values with null or -1.
    494      */
    495     private class SetDrawableParameters extends Action {
    496         public SetDrawableParameters(int id, boolean targetBackground, int alpha,
    497                 int colorFilter, PorterDuff.Mode mode, int level) {
    498             this.viewId = id;
    499             this.targetBackground = targetBackground;
    500             this.alpha = alpha;
    501             this.colorFilter = colorFilter;
    502             this.filterMode = mode;
    503             this.level = level;
    504         }
    505 
    506         public SetDrawableParameters(Parcel parcel) {
    507             viewId = parcel.readInt();
    508             targetBackground = parcel.readInt() != 0;
    509             alpha = parcel.readInt();
    510             colorFilter = parcel.readInt();
    511             boolean hasMode = parcel.readInt() != 0;
    512             if (hasMode) {
    513                 filterMode = PorterDuff.Mode.valueOf(parcel.readString());
    514             } else {
    515                 filterMode = null;
    516             }
    517             level = parcel.readInt();
    518         }
    519 
    520         public void writeToParcel(Parcel dest, int flags) {
    521             dest.writeInt(TAG);
    522             dest.writeInt(viewId);
    523             dest.writeInt(targetBackground ? 1 : 0);
    524             dest.writeInt(alpha);
    525             dest.writeInt(colorFilter);
    526             if (filterMode != null) {
    527                 dest.writeInt(1);
    528                 dest.writeString(filterMode.toString());
    529             } else {
    530                 dest.writeInt(0);
    531             }
    532             dest.writeInt(level);
    533         }
    534 
    535         @Override
    536         public void apply(View root, ViewGroup rootParent) {
    537             final View target = root.findViewById(viewId);
    538             if (target == null) return;
    539 
    540             // Pick the correct drawable to modify for this view
    541             Drawable targetDrawable = null;
    542             if (targetBackground) {
    543                 targetDrawable = target.getBackground();
    544             } else if (target instanceof ImageView) {
    545                 ImageView imageView = (ImageView) target;
    546                 targetDrawable = imageView.getDrawable();
    547             }
    548 
    549             if (targetDrawable != null) {
    550                 // Perform modifications only if values are set correctly
    551                 if (alpha != -1) {
    552                     targetDrawable.setAlpha(alpha);
    553                 }
    554                 if (colorFilter != -1 && filterMode != null) {
    555                     targetDrawable.setColorFilter(colorFilter, filterMode);
    556                 }
    557                 if (level != -1) {
    558                     targetDrawable.setLevel(level);
    559                 }
    560             }
    561         }
    562 
    563         int viewId;
    564         boolean targetBackground;
    565         int alpha;
    566         int colorFilter;
    567         PorterDuff.Mode filterMode;
    568         int level;
    569 
    570         public final static int TAG = 3;
    571     }
    572 
    573     private class ReflectionActionWithoutParams extends Action {
    574         int viewId;
    575         String methodName;
    576 
    577         public final static int TAG = 5;
    578 
    579         ReflectionActionWithoutParams(int viewId, String methodName) {
    580             this.viewId = viewId;
    581             this.methodName = methodName;
    582         }
    583 
    584         ReflectionActionWithoutParams(Parcel in) {
    585             this.viewId = in.readInt();
    586             this.methodName = in.readString();
    587         }
    588 
    589         public void writeToParcel(Parcel out, int flags) {
    590             out.writeInt(TAG);
    591             out.writeInt(this.viewId);
    592             out.writeString(this.methodName);
    593         }
    594 
    595         @Override
    596         public void apply(View root, ViewGroup rootParent) {
    597             final View view = root.findViewById(viewId);
    598             if (view == null) return;
    599 
    600             Class klass = view.getClass();
    601             Method method;
    602             try {
    603                 method = klass.getMethod(this.methodName);
    604             } catch (NoSuchMethodException ex) {
    605                 throw new ActionException("view: " + klass.getName() + " doesn't have method: "
    606                         + this.methodName + "()");
    607             }
    608 
    609             if (!method.isAnnotationPresent(RemotableViewMethod.class)) {
    610                 throw new ActionException("view: " + klass.getName()
    611                         + " can't use method with RemoteViews: "
    612                         + this.methodName + "()");
    613             }
    614 
    615             try {
    616                 //noinspection ConstantIfStatement
    617                 if (false) {
    618                     Log.d("RemoteViews", "view: " + klass.getName() + " calling method: "
    619                         + this.methodName + "()");
    620                 }
    621                 method.invoke(view);
    622             } catch (Exception ex) {
    623                 throw new ActionException(ex);
    624             }
    625         }
    626     }
    627 
    628     /**
    629      * Base class for the reflection actions.
    630      */
    631     private class ReflectionAction extends Action {
    632         static final int TAG = 2;
    633 
    634         static final int BOOLEAN = 1;
    635         static final int BYTE = 2;
    636         static final int SHORT = 3;
    637         static final int INT = 4;
    638         static final int LONG = 5;
    639         static final int FLOAT = 6;
    640         static final int DOUBLE = 7;
    641         static final int CHAR = 8;
    642         static final int STRING = 9;
    643         static final int CHAR_SEQUENCE = 10;
    644         static final int URI = 11;
    645         static final int BITMAP = 12;
    646         static final int BUNDLE = 13;
    647         static final int INTENT = 14;
    648 
    649         int viewId;
    650         String methodName;
    651         int type;
    652         Object value;
    653 
    654         ReflectionAction(int viewId, String methodName, int type, Object value) {
    655             this.viewId = viewId;
    656             this.methodName = methodName;
    657             this.type = type;
    658             this.value = value;
    659         }
    660 
    661         ReflectionAction(Parcel in) {
    662             this.viewId = in.readInt();
    663             this.methodName = in.readString();
    664             this.type = in.readInt();
    665             //noinspection ConstantIfStatement
    666             if (false) {
    667                 Log.d("RemoteViews", "read viewId=0x" + Integer.toHexString(this.viewId)
    668                         + " methodName=" + this.methodName + " type=" + this.type);
    669             }
    670             switch (this.type) {
    671                 case BOOLEAN:
    672                     this.value = in.readInt() != 0;
    673                     break;
    674                 case BYTE:
    675                     this.value = in.readByte();
    676                     break;
    677                 case SHORT:
    678                     this.value = (short)in.readInt();
    679                     break;
    680                 case INT:
    681                     this.value = in.readInt();
    682                     break;
    683                 case LONG:
    684                     this.value = in.readLong();
    685                     break;
    686                 case FLOAT:
    687                     this.value = in.readFloat();
    688                     break;
    689                 case DOUBLE:
    690                     this.value = in.readDouble();
    691                     break;
    692                 case CHAR:
    693                     this.value = (char)in.readInt();
    694                     break;
    695                 case STRING:
    696                     this.value = in.readString();
    697                     break;
    698                 case CHAR_SEQUENCE:
    699                     this.value = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
    700                     break;
    701                 case URI:
    702                     this.value = Uri.CREATOR.createFromParcel(in);
    703                     break;
    704                 case BITMAP:
    705                     this.value = Bitmap.CREATOR.createFromParcel(in);
    706                     break;
    707                 case BUNDLE:
    708                     this.value = in.readBundle();
    709                     break;
    710                 case INTENT:
    711                     this.value = Intent.CREATOR.createFromParcel(in);
    712                     break;
    713                 default:
    714                     break;
    715             }
    716         }
    717 
    718         public void writeToParcel(Parcel out, int flags) {
    719             out.writeInt(TAG);
    720             out.writeInt(this.viewId);
    721             out.writeString(this.methodName);
    722             out.writeInt(this.type);
    723             //noinspection ConstantIfStatement
    724             if (false) {
    725                 Log.d("RemoteViews", "write viewId=0x" + Integer.toHexString(this.viewId)
    726                         + " methodName=" + this.methodName + " type=" + this.type);
    727             }
    728             switch (this.type) {
    729                 case BOOLEAN:
    730                     out.writeInt((Boolean) this.value ? 1 : 0);
    731                     break;
    732                 case BYTE:
    733                     out.writeByte((Byte) this.value);
    734                     break;
    735                 case SHORT:
    736                     out.writeInt((Short) this.value);
    737                     break;
    738                 case INT:
    739                     out.writeInt((Integer) this.value);
    740                     break;
    741                 case LONG:
    742                     out.writeLong((Long) this.value);
    743                     break;
    744                 case FLOAT:
    745                     out.writeFloat((Float) this.value);
    746                     break;
    747                 case DOUBLE:
    748                     out.writeDouble((Double) this.value);
    749                     break;
    750                 case CHAR:
    751                     out.writeInt((int)((Character)this.value).charValue());
    752                     break;
    753                 case STRING:
    754                     out.writeString((String)this.value);
    755                     break;
    756                 case CHAR_SEQUENCE:
    757                     TextUtils.writeToParcel((CharSequence)this.value, out, flags);
    758                     break;
    759                 case URI:
    760                     ((Uri)this.value).writeToParcel(out, flags);
    761                     break;
    762                 case BITMAP:
    763                     ((Bitmap)this.value).writeToParcel(out, flags);
    764                     break;
    765                 case BUNDLE:
    766                     out.writeBundle((Bundle) this.value);
    767                     break;
    768                 case INTENT:
    769                     ((Intent)this.value).writeToParcel(out, flags);
    770                     break;
    771                 default:
    772                     break;
    773             }
    774         }
    775 
    776         private Class getParameterType() {
    777             switch (this.type) {
    778                 case BOOLEAN:
    779                     return boolean.class;
    780                 case BYTE:
    781                     return byte.class;
    782                 case SHORT:
    783                     return short.class;
    784                 case INT:
    785                     return int.class;
    786                 case LONG:
    787                     return long.class;
    788                 case FLOAT:
    789                     return float.class;
    790                 case DOUBLE:
    791                     return double.class;
    792                 case CHAR:
    793                     return char.class;
    794                 case STRING:
    795                     return String.class;
    796                 case CHAR_SEQUENCE:
    797                     return CharSequence.class;
    798                 case URI:
    799                     return Uri.class;
    800                 case BITMAP:
    801                     return Bitmap.class;
    802                 case BUNDLE:
    803                     return Bundle.class;
    804                 case INTENT:
    805                     return Intent.class;
    806                 default:
    807                     return null;
    808             }
    809         }
    810 
    811         @Override
    812         public void apply(View root, ViewGroup rootParent) {
    813             final View view = root.findViewById(viewId);
    814             if (view == null) return;
    815 
    816             Class param = getParameterType();
    817             if (param == null) {
    818                 throw new ActionException("bad type: " + this.type);
    819             }
    820 
    821             Class klass = view.getClass();
    822             Method method;
    823             try {
    824                 method = klass.getMethod(this.methodName, getParameterType());
    825             }
    826             catch (NoSuchMethodException ex) {
    827                 throw new ActionException("view: " + klass.getName() + " doesn't have method: "
    828                         + this.methodName + "(" + param.getName() + ")");
    829             }
    830 
    831             if (!method.isAnnotationPresent(RemotableViewMethod.class)) {
    832                 throw new ActionException("view: " + klass.getName()
    833                         + " can't use method with RemoteViews: "
    834                         + this.methodName + "(" + param.getName() + ")");
    835             }
    836 
    837             try {
    838                 //noinspection ConstantIfStatement
    839                 if (false) {
    840                     Log.d("RemoteViews", "view: " + klass.getName() + " calling method: "
    841                         + this.methodName + "(" + param.getName() + ") with "
    842                         + (this.value == null ? "null" : this.value.getClass().getName()));
    843                 }
    844                 method.invoke(view, this.value);
    845             }
    846             catch (Exception ex) {
    847                 throw new ActionException(ex);
    848             }
    849         }
    850 
    851         @Override
    852         public void updateMemoryUsageEstimate(MemoryUsageCounter counter) {
    853             // We currently only calculate Bitmap memory usage
    854             switch (this.type) {
    855                 case BITMAP:
    856                     if (this.value != null) {
    857                         final Bitmap b = (Bitmap) this.value;
    858                         final Bitmap.Config c = b.getConfig();
    859                         // If we don't know, be pessimistic and assume 4
    860                         int bpp = 4;
    861                         if (c != null) {
    862                             switch (c) {
    863                             case ALPHA_8:
    864                                 bpp = 1;
    865                                 break;
    866                             case RGB_565:
    867                             case ARGB_4444:
    868                                 bpp = 2;
    869                                 break;
    870                             case ARGB_8888:
    871                                 bpp = 4;
    872                                 break;
    873                             }
    874                         }
    875                         counter.bitmapIncrement(b.getWidth() * b.getHeight() * bpp);
    876                     }
    877                     break;
    878                 default:
    879                     break;
    880             }
    881         }
    882     }
    883 
    884     /**
    885      * Equivalent to calling {@link ViewGroup#addView(View)} after inflating the
    886      * given {@link RemoteViews}, or calling {@link ViewGroup#removeAllViews()}
    887      * when null. This allows users to build "nested" {@link RemoteViews}.
    888      */
    889     private class ViewGroupAction extends Action {
    890         public ViewGroupAction(int viewId, RemoteViews nestedViews) {
    891             this.viewId = viewId;
    892             this.nestedViews = nestedViews;
    893         }
    894 
    895         public ViewGroupAction(Parcel parcel) {
    896             viewId = parcel.readInt();
    897             nestedViews = parcel.readParcelable(null);
    898         }
    899 
    900         public void writeToParcel(Parcel dest, int flags) {
    901             dest.writeInt(TAG);
    902             dest.writeInt(viewId);
    903             dest.writeParcelable(nestedViews, 0 /* no flags */);
    904         }
    905 
    906         @Override
    907         public void apply(View root, ViewGroup rootParent) {
    908             final Context context = root.getContext();
    909             final ViewGroup target = (ViewGroup) root.findViewById(viewId);
    910             if (target == null) return;
    911             if (nestedViews != null) {
    912                 // Inflate nested views and add as children
    913                 target.addView(nestedViews.apply(context, target));
    914             } else {
    915                 // Clear all children when nested views omitted
    916                 target.removeAllViews();
    917             }
    918         }
    919 
    920         @Override
    921         public void updateMemoryUsageEstimate(MemoryUsageCounter counter) {
    922             if (nestedViews != null) {
    923                 counter.bitmapIncrement(nestedViews.estimateBitmapMemoryUsage());
    924             }
    925         }
    926 
    927         int viewId;
    928         RemoteViews nestedViews;
    929 
    930         public final static int TAG = 4;
    931     }
    932 
    933     /**
    934      * Simple class used to keep track of memory usage in a RemoteViews.
    935      *
    936      */
    937     private class MemoryUsageCounter {
    938         public void clear() {
    939             mBitmapHeapMemoryUsage = 0;
    940         }
    941 
    942         public void bitmapIncrement(int numBytes) {
    943             mBitmapHeapMemoryUsage += numBytes;
    944         }
    945 
    946         public int getBitmapHeapMemoryUsage() {
    947             return mBitmapHeapMemoryUsage;
    948         }
    949 
    950         int mBitmapHeapMemoryUsage;
    951     }
    952 
    953     /**
    954      * Create a new RemoteViews object that will display the views contained
    955      * in the specified layout file.
    956      *
    957      * @param packageName Name of the package that contains the layout resource
    958      * @param layoutId The id of the layout resource
    959      */
    960     public RemoteViews(String packageName, int layoutId) {
    961         mPackage = packageName;
    962         mLayoutId = layoutId;
    963 
    964         // setup the memory usage statistics
    965         mMemoryUsageCounter = new MemoryUsageCounter();
    966         recalculateMemoryUsage();
    967     }
    968 
    969     /**
    970      * Reads a RemoteViews object from a parcel.
    971      *
    972      * @param parcel
    973      */
    974     public RemoteViews(Parcel parcel) {
    975         mPackage = parcel.readString();
    976         mLayoutId = parcel.readInt();
    977         mIsWidgetCollectionChild = parcel.readInt() == 1 ? true : false;
    978 
    979         int count = parcel.readInt();
    980         if (count > 0) {
    981             mActions = new ArrayList<Action>(count);
    982             for (int i=0; i<count; i++) {
    983                 int tag = parcel.readInt();
    984                 switch (tag) {
    985                 case SetOnClickPendingIntent.TAG:
    986                     mActions.add(new SetOnClickPendingIntent(parcel));
    987                     break;
    988                 case SetDrawableParameters.TAG:
    989                     mActions.add(new SetDrawableParameters(parcel));
    990                     break;
    991                 case ReflectionAction.TAG:
    992                     mActions.add(new ReflectionAction(parcel));
    993                     break;
    994                 case ViewGroupAction.TAG:
    995                     mActions.add(new ViewGroupAction(parcel));
    996                     break;
    997                 case ReflectionActionWithoutParams.TAG:
    998                     mActions.add(new ReflectionActionWithoutParams(parcel));
    999                     break;
   1000                 case SetEmptyView.TAG:
   1001                     mActions.add(new SetEmptyView(parcel));
   1002                     break;
   1003                 case SetPendingIntentTemplate.TAG:
   1004                     mActions.add(new SetPendingIntentTemplate(parcel));
   1005                     break;
   1006                 case SetOnClickFillInIntent.TAG:
   1007                     mActions.add(new SetOnClickFillInIntent(parcel));
   1008                     break;
   1009                 case SetRemoteViewsAdapterIntent.TAG:
   1010                     mActions.add(new SetRemoteViewsAdapterIntent(parcel));
   1011                     break;
   1012                 default:
   1013                     throw new ActionException("Tag " + tag + " not found");
   1014                 }
   1015             }
   1016         }
   1017 
   1018         // setup the memory usage statistics
   1019         mMemoryUsageCounter = new MemoryUsageCounter();
   1020         recalculateMemoryUsage();
   1021     }
   1022 
   1023     @Override
   1024     public RemoteViews clone() {
   1025         final RemoteViews that = new RemoteViews(mPackage, mLayoutId);
   1026         if (mActions != null) {
   1027             that.mActions = (ArrayList<Action>)mActions.clone();
   1028         }
   1029 
   1030         // update the memory usage stats of the cloned RemoteViews
   1031         that.recalculateMemoryUsage();
   1032         return that;
   1033     }
   1034 
   1035     public String getPackage() {
   1036         return mPackage;
   1037     }
   1038 
   1039     public int getLayoutId() {
   1040         return mLayoutId;
   1041     }
   1042 
   1043     /*
   1044      * This flag indicates whether this RemoteViews object is being created from a
   1045      * RemoteViewsService for use as a child of a widget collection. This flag is used
   1046      * to determine whether or not certain features are available, in particular,
   1047      * setting on click extras and setting on click pending intents. The former is enabled,
   1048      * and the latter disabled when this flag is true.
   1049      */
   1050     void setIsWidgetCollectionChild(boolean isWidgetCollectionChild) {
   1051         mIsWidgetCollectionChild = isWidgetCollectionChild;
   1052     }
   1053 
   1054     /**
   1055      * Updates the memory usage statistics.
   1056      */
   1057     private void recalculateMemoryUsage() {
   1058         mMemoryUsageCounter.clear();
   1059 
   1060         // Accumulate the memory usage for each action
   1061         if (mActions != null) {
   1062             final int count = mActions.size();
   1063             for (int i= 0; i < count; ++i) {
   1064                 mActions.get(i).updateMemoryUsageEstimate(mMemoryUsageCounter);
   1065             }
   1066         }
   1067     }
   1068 
   1069     /**
   1070      * Returns an estimate of the bitmap heap memory usage for this RemoteViews.
   1071      */
   1072     int estimateBitmapMemoryUsage() {
   1073         return mMemoryUsageCounter.getBitmapHeapMemoryUsage();
   1074     }
   1075 
   1076     /**
   1077      * Add an action to be executed on the remote side when apply is called.
   1078      *
   1079      * @param a The action to add
   1080      */
   1081     private void addAction(Action a) {
   1082         if (mActions == null) {
   1083             mActions = new ArrayList<Action>();
   1084         }
   1085         mActions.add(a);
   1086 
   1087         // update the memory usage stats
   1088         a.updateMemoryUsageEstimate(mMemoryUsageCounter);
   1089     }
   1090 
   1091     /**
   1092      * Equivalent to calling {@link ViewGroup#addView(View)} after inflating the
   1093      * given {@link RemoteViews}. This allows users to build "nested"
   1094      * {@link RemoteViews}. In cases where consumers of {@link RemoteViews} may
   1095      * recycle layouts, use {@link #removeAllViews(int)} to clear any existing
   1096      * children.
   1097      *
   1098      * @param viewId The id of the parent {@link ViewGroup} to add child into.
   1099      * @param nestedView {@link RemoteViews} that describes the child.
   1100      */
   1101     public void addView(int viewId, RemoteViews nestedView) {
   1102         addAction(new ViewGroupAction(viewId, nestedView));
   1103     }
   1104 
   1105     /**
   1106      * Equivalent to calling {@link ViewGroup#removeAllViews()}.
   1107      *
   1108      * @param viewId The id of the parent {@link ViewGroup} to remove all
   1109      *            children from.
   1110      */
   1111     public void removeAllViews(int viewId) {
   1112         addAction(new ViewGroupAction(viewId, null));
   1113     }
   1114 
   1115     /**
   1116      * Equivalent to calling {@link AdapterViewAnimator#showNext()}
   1117      *
   1118      * @param viewId The id of the view on which to call {@link AdapterViewAnimator#showNext()}
   1119      */
   1120     public void showNext(int viewId) {
   1121         addAction(new ReflectionActionWithoutParams(viewId, "showNext"));
   1122     }
   1123 
   1124     /**
   1125      * Equivalent to calling {@link AdapterViewAnimator#showPrevious()}
   1126      *
   1127      * @param viewId The id of the view on which to call {@link AdapterViewAnimator#showPrevious()}
   1128      */
   1129     public void showPrevious(int viewId) {
   1130         addAction(new ReflectionActionWithoutParams(viewId, "showPrevious"));
   1131     }
   1132 
   1133     /**
   1134      * Equivalent to calling {@link AdapterViewAnimator#setDisplayedChild(int)}
   1135      *
   1136      * @param viewId The id of the view on which to call
   1137      *               {@link AdapterViewAnimator#setDisplayedChild(int)}
   1138      */
   1139     public void setDisplayedChild(int viewId, int childIndex) {
   1140         setInt(viewId, "setDisplayedChild", childIndex);
   1141     }
   1142 
   1143     /**
   1144      * Equivalent to calling View.setVisibility
   1145      *
   1146      * @param viewId The id of the view whose visibility should change
   1147      * @param visibility The new visibility for the view
   1148      */
   1149     public void setViewVisibility(int viewId, int visibility) {
   1150         setInt(viewId, "setVisibility", visibility);
   1151     }
   1152 
   1153     /**
   1154      * Equivalent to calling TextView.setText
   1155      *
   1156      * @param viewId The id of the view whose text should change
   1157      * @param text The new text for the view
   1158      */
   1159     public void setTextViewText(int viewId, CharSequence text) {
   1160         setCharSequence(viewId, "setText", text);
   1161     }
   1162 
   1163     /**
   1164      * Equivalent to calling ImageView.setImageResource
   1165      *
   1166      * @param viewId The id of the view whose drawable should change
   1167      * @param srcId The new resource id for the drawable
   1168      */
   1169     public void setImageViewResource(int viewId, int srcId) {
   1170         setInt(viewId, "setImageResource", srcId);
   1171     }
   1172 
   1173     /**
   1174      * Equivalent to calling ImageView.setImageURI
   1175      *
   1176      * @param viewId The id of the view whose drawable should change
   1177      * @param uri The Uri for the image
   1178      */
   1179     public void setImageViewUri(int viewId, Uri uri) {
   1180         setUri(viewId, "setImageURI", uri);
   1181     }
   1182 
   1183     /**
   1184      * Equivalent to calling ImageView.setImageBitmap
   1185      *
   1186      * @param viewId The id of the view whose drawable should change
   1187      * @param bitmap The new Bitmap for the drawable
   1188      */
   1189     public void setImageViewBitmap(int viewId, Bitmap bitmap) {
   1190         setBitmap(viewId, "setImageBitmap", bitmap);
   1191     }
   1192 
   1193     /**
   1194      * Equivalent to calling AdapterView.setEmptyView
   1195      *
   1196      * @param viewId The id of the view on which to set the empty view
   1197      * @param emptyViewId The view id of the empty view
   1198      */
   1199     public void setEmptyView(int viewId, int emptyViewId) {
   1200         addAction(new SetEmptyView(viewId, emptyViewId));
   1201     }
   1202 
   1203     /**
   1204      * Equivalent to calling {@link Chronometer#setBase Chronometer.setBase},
   1205      * {@link Chronometer#setFormat Chronometer.setFormat},
   1206      * and {@link Chronometer#start Chronometer.start()} or
   1207      * {@link Chronometer#stop Chronometer.stop()}.
   1208      *
   1209      * @param viewId The id of the view whose text should change
   1210      * @param base The time at which the timer would have read 0:00.  This
   1211      *             time should be based off of
   1212      *             {@link android.os.SystemClock#elapsedRealtime SystemClock.elapsedRealtime()}.
   1213      * @param format The Chronometer format string, or null to
   1214      *               simply display the timer value.
   1215      * @param started True if you want the clock to be started, false if not.
   1216      */
   1217     public void setChronometer(int viewId, long base, String format, boolean started) {
   1218         setLong(viewId, "setBase", base);
   1219         setString(viewId, "setFormat", format);
   1220         setBoolean(viewId, "setStarted", started);
   1221     }
   1222 
   1223     /**
   1224      * Equivalent to calling {@link ProgressBar#setMax ProgressBar.setMax},
   1225      * {@link ProgressBar#setProgress ProgressBar.setProgress}, and
   1226      * {@link ProgressBar#setIndeterminate ProgressBar.setIndeterminate}
   1227      *
   1228      * If indeterminate is true, then the values for max and progress are ignored.
   1229      *
   1230      * @param viewId The id of the view whose text should change
   1231      * @param max The 100% value for the progress bar
   1232      * @param progress The current value of the progress bar.
   1233      * @param indeterminate True if the progress bar is indeterminate,
   1234      *                false if not.
   1235      */
   1236     public void setProgressBar(int viewId, int max, int progress,
   1237             boolean indeterminate) {
   1238         setBoolean(viewId, "setIndeterminate", indeterminate);
   1239         if (!indeterminate) {
   1240             setInt(viewId, "setMax", max);
   1241             setInt(viewId, "setProgress", progress);
   1242         }
   1243     }
   1244 
   1245     /**
   1246      * Equivalent to calling
   1247      * {@link android.view.View#setOnClickListener(android.view.View.OnClickListener)}
   1248      * to launch the provided {@link PendingIntent}.
   1249      *
   1250      * When setting the on-click action of items within collections (eg. {@link ListView},
   1251      * {@link StackView} etc.), this method will not work. Instead, use {@link
   1252      * RemoteViews#setPendingIntentTemplate(int, PendingIntent) in conjunction with
   1253      * RemoteViews#setOnClickFillInIntent(int, Intent).
   1254      *
   1255      * @param viewId The id of the view that will trigger the {@link PendingIntent} when clicked
   1256      * @param pendingIntent The {@link PendingIntent} to send when user clicks
   1257      */
   1258     public void setOnClickPendingIntent(int viewId, PendingIntent pendingIntent) {
   1259         addAction(new SetOnClickPendingIntent(viewId, pendingIntent));
   1260     }
   1261 
   1262     /**
   1263      * When using collections (eg. {@link ListView}, {@link StackView} etc.) in widgets, it is very
   1264      * costly to set PendingIntents on the individual items, and is hence not permitted. Instead
   1265      * this method should be used to set a single PendingIntent template on the collection, and
   1266      * individual items can differentiate their on-click behavior using
   1267      * {@link RemoteViews#setOnClickFillInIntent(int, Intent)}.
   1268      *
   1269      * @param viewId The id of the collection who's children will use this PendingIntent template
   1270      *          when clicked
   1271      * @param pendingIntentTemplate The {@link PendingIntent} to be combined with extras specified
   1272      *          by a child of viewId and executed when that child is clicked
   1273      */
   1274     public void setPendingIntentTemplate(int viewId, PendingIntent pendingIntentTemplate) {
   1275         addAction(new SetPendingIntentTemplate(viewId, pendingIntentTemplate));
   1276     }
   1277 
   1278     /**
   1279      * When using collections (eg. {@link ListView}, {@link StackView} etc.) in widgets, it is very
   1280      * costly to set PendingIntents on the individual items, and is hence not permitted. Instead
   1281      * a single PendingIntent template can be set on the collection, see {@link
   1282      * RemoteViews#setPendingIntentTemplate(int, PendingIntent)}, and the individual on-click
   1283      * action of a given item can be distinguished by setting a fillInIntent on that item. The
   1284      * fillInIntent is then combined with the PendingIntent template in order to determine the final
   1285      * intent which will be executed when the item is clicked. This works as follows: any fields
   1286      * which are left blank in the PendingIntent template, but are provided by the fillInIntent
   1287      * will be overwritten, and the resulting PendingIntent will be used.
   1288      *
   1289      *
   1290      * of the PendingIntent template will then be filled in with the associated fields that are
   1291      * set in fillInIntent. See {@link Intent#fillIn(Intent, int)} for more details.
   1292      *
   1293      * @param viewId The id of the view on which to set the fillInIntent
   1294      * @param fillInIntent The intent which will be combined with the parent's PendingIntent
   1295      *        in order to determine the on-click behavior of the view specified by viewId
   1296      */
   1297     public void setOnClickFillInIntent(int viewId, Intent fillInIntent) {
   1298         addAction(new SetOnClickFillInIntent(viewId, fillInIntent));
   1299     }
   1300 
   1301     /**
   1302      * @hide
   1303      * Equivalent to calling a combination of {@link Drawable#setAlpha(int)},
   1304      * {@link Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)},
   1305      * and/or {@link Drawable#setLevel(int)} on the {@link Drawable} of a given
   1306      * view.
   1307      * <p>
   1308      * You can omit specific calls by marking their values with null or -1.
   1309      *
   1310      * @param viewId The id of the view that contains the target
   1311      *            {@link Drawable}
   1312      * @param targetBackground If true, apply these parameters to the
   1313      *            {@link Drawable} returned by
   1314      *            {@link android.view.View#getBackground()}. Otherwise, assume
   1315      *            the target view is an {@link ImageView} and apply them to
   1316      *            {@link ImageView#getDrawable()}.
   1317      * @param alpha Specify an alpha value for the drawable, or -1 to leave
   1318      *            unchanged.
   1319      * @param colorFilter Specify a color for a
   1320      *            {@link android.graphics.ColorFilter} for this drawable, or -1
   1321      *            to leave unchanged.
   1322      * @param mode Specify a PorterDuff mode for this drawable, or null to leave
   1323      *            unchanged.
   1324      * @param level Specify the level for the drawable, or -1 to leave
   1325      *            unchanged.
   1326      */
   1327     public void setDrawableParameters(int viewId, boolean targetBackground, int alpha,
   1328             int colorFilter, PorterDuff.Mode mode, int level) {
   1329         addAction(new SetDrawableParameters(viewId, targetBackground, alpha,
   1330                 colorFilter, mode, level));
   1331     }
   1332 
   1333     /**
   1334      * Equivalent to calling {@link android.widget.TextView#setTextColor(int)}.
   1335      *
   1336      * @param viewId The id of the view whose text should change
   1337      * @param color Sets the text color for all the states (normal, selected,
   1338      *            focused) to be this color.
   1339      */
   1340     public void setTextColor(int viewId, int color) {
   1341         setInt(viewId, "setTextColor", color);
   1342     }
   1343 
   1344     /**
   1345      * Equivalent to calling {@link android.widget.AbsListView#setRemoteViewsAdapter(Intent)}.
   1346      *
   1347      * @param appWidgetId The id of the app widget which contains the specified view. (This
   1348      *      parameter is ignored in this deprecated method)
   1349      * @param viewId The id of the view whose text should change
   1350      * @param intent The intent of the service which will be
   1351      *            providing data to the RemoteViewsAdapter
   1352      * @deprecated This method has been deprecated. See
   1353      *      {@link android.widget.RemoteViews#setRemoteAdapter(int, Intent)}
   1354      */
   1355     @Deprecated
   1356     public void setRemoteAdapter(int appWidgetId, int viewId, Intent intent) {
   1357         setRemoteAdapter(viewId, intent);
   1358     }
   1359 
   1360     /**
   1361      * Equivalent to calling {@link android.widget.AbsListView#setRemoteViewsAdapter(Intent)}.
   1362      * Can only be used for App Widgets.
   1363      *
   1364      * @param viewId The id of the view whose text should change
   1365      * @param intent The intent of the service which will be
   1366      *            providing data to the RemoteViewsAdapter
   1367      */
   1368     public void setRemoteAdapter(int viewId, Intent intent) {
   1369         addAction(new SetRemoteViewsAdapterIntent(viewId, intent));
   1370     }
   1371 
   1372     /**
   1373      * Equivalent to calling {@link android.widget.AbsListView#smoothScrollToPosition(int, int)}.
   1374      *
   1375      * @param viewId The id of the view whose text should change
   1376      * @param position Scroll to this adapter position
   1377      */
   1378     public void setScrollPosition(int viewId, int position) {
   1379         setInt(viewId, "smoothScrollToPosition", position);
   1380     }
   1381 
   1382     /**
   1383      * Equivalent to calling {@link android.widget.AbsListView#smoothScrollToPosition(int, int)}.
   1384      *
   1385      * @param viewId The id of the view whose text should change
   1386      * @param offset Scroll by this adapter position offset
   1387      */
   1388     public void setRelativeScrollPosition(int viewId, int offset) {
   1389         setInt(viewId, "smoothScrollByOffset", offset);
   1390     }
   1391 
   1392     /**
   1393      * Call a method taking one boolean on a view in the layout for this RemoteViews.
   1394      *
   1395      * @param viewId The id of the view whose text should change
   1396      * @param methodName The name of the method to call.
   1397      * @param value The value to pass to the method.
   1398      */
   1399     public void setBoolean(int viewId, String methodName, boolean value) {
   1400         addAction(new ReflectionAction(viewId, methodName, ReflectionAction.BOOLEAN, value));
   1401     }
   1402 
   1403     /**
   1404      * Call a method taking one byte on a view in the layout for this RemoteViews.
   1405      *
   1406      * @param viewId The id of the view whose text should change
   1407      * @param methodName The name of the method to call.
   1408      * @param value The value to pass to the method.
   1409      */
   1410     public void setByte(int viewId, String methodName, byte value) {
   1411         addAction(new ReflectionAction(viewId, methodName, ReflectionAction.BYTE, value));
   1412     }
   1413 
   1414     /**
   1415      * Call a method taking one short on a view in the layout for this RemoteViews.
   1416      *
   1417      * @param viewId The id of the view whose text should change
   1418      * @param methodName The name of the method to call.
   1419      * @param value The value to pass to the method.
   1420      */
   1421     public void setShort(int viewId, String methodName, short value) {
   1422         addAction(new ReflectionAction(viewId, methodName, ReflectionAction.SHORT, value));
   1423     }
   1424 
   1425     /**
   1426      * Call a method taking one int on a view in the layout for this RemoteViews.
   1427      *
   1428      * @param viewId The id of the view whose text should change
   1429      * @param methodName The name of the method to call.
   1430      * @param value The value to pass to the method.
   1431      */
   1432     public void setInt(int viewId, String methodName, int value) {
   1433         addAction(new ReflectionAction(viewId, methodName, ReflectionAction.INT, value));
   1434     }
   1435 
   1436     /**
   1437      * Call a method taking one long on a view in the layout for this RemoteViews.
   1438      *
   1439      * @param viewId The id of the view whose text should change
   1440      * @param methodName The name of the method to call.
   1441      * @param value The value to pass to the method.
   1442      */
   1443     public void setLong(int viewId, String methodName, long value) {
   1444         addAction(new ReflectionAction(viewId, methodName, ReflectionAction.LONG, value));
   1445     }
   1446 
   1447     /**
   1448      * Call a method taking one float on a view in the layout for this RemoteViews.
   1449      *
   1450      * @param viewId The id of the view whose text should change
   1451      * @param methodName The name of the method to call.
   1452      * @param value The value to pass to the method.
   1453      */
   1454     public void setFloat(int viewId, String methodName, float value) {
   1455         addAction(new ReflectionAction(viewId, methodName, ReflectionAction.FLOAT, value));
   1456     }
   1457 
   1458     /**
   1459      * Call a method taking one double on a view in the layout for this RemoteViews.
   1460      *
   1461      * @param viewId The id of the view whose text should change
   1462      * @param methodName The name of the method to call.
   1463      * @param value The value to pass to the method.
   1464      */
   1465     public void setDouble(int viewId, String methodName, double value) {
   1466         addAction(new ReflectionAction(viewId, methodName, ReflectionAction.DOUBLE, value));
   1467     }
   1468 
   1469     /**
   1470      * Call a method taking one char on a view in the layout for this RemoteViews.
   1471      *
   1472      * @param viewId The id of the view whose text should change
   1473      * @param methodName The name of the method to call.
   1474      * @param value The value to pass to the method.
   1475      */
   1476     public void setChar(int viewId, String methodName, char value) {
   1477         addAction(new ReflectionAction(viewId, methodName, ReflectionAction.CHAR, value));
   1478     }
   1479 
   1480     /**
   1481      * Call a method taking one String on a view in the layout for this RemoteViews.
   1482      *
   1483      * @param viewId The id of the view whose text should change
   1484      * @param methodName The name of the method to call.
   1485      * @param value The value to pass to the method.
   1486      */
   1487     public void setString(int viewId, String methodName, String value) {
   1488         addAction(new ReflectionAction(viewId, methodName, ReflectionAction.STRING, value));
   1489     }
   1490 
   1491     /**
   1492      * Call a method taking one CharSequence on a view in the layout for this RemoteViews.
   1493      *
   1494      * @param viewId The id of the view whose text should change
   1495      * @param methodName The name of the method to call.
   1496      * @param value The value to pass to the method.
   1497      */
   1498     public void setCharSequence(int viewId, String methodName, CharSequence value) {
   1499         addAction(new ReflectionAction(viewId, methodName, ReflectionAction.CHAR_SEQUENCE, value));
   1500     }
   1501 
   1502     /**
   1503      * Call a method taking one Uri on a view in the layout for this RemoteViews.
   1504      *
   1505      * @param viewId The id of the view whose text should change
   1506      * @param methodName The name of the method to call.
   1507      * @param value The value to pass to the method.
   1508      */
   1509     public void setUri(int viewId, String methodName, Uri value) {
   1510         addAction(new ReflectionAction(viewId, methodName, ReflectionAction.URI, value));
   1511     }
   1512 
   1513     /**
   1514      * Call a method taking one Bitmap on a view in the layout for this RemoteViews.
   1515      * @more
   1516      * <p class="note">The bitmap will be flattened into the parcel if this object is
   1517      * sent across processes, so it may end up using a lot of memory, and may be fairly slow.</p>
   1518      *
   1519      * @param viewId The id of the view whose text should change
   1520      * @param methodName The name of the method to call.
   1521      * @param value The value to pass to the method.
   1522      */
   1523     public void setBitmap(int viewId, String methodName, Bitmap value) {
   1524         addAction(new ReflectionAction(viewId, methodName, ReflectionAction.BITMAP, value));
   1525     }
   1526 
   1527     /**
   1528      * Call a method taking one Bundle on a view in the layout for this RemoteViews.
   1529      *
   1530      * @param viewId The id of the view whose text should change
   1531      * @param methodName The name of the method to call.
   1532      * @param value The value to pass to the method.
   1533      */
   1534     public void setBundle(int viewId, String methodName, Bundle value) {
   1535         addAction(new ReflectionAction(viewId, methodName, ReflectionAction.BUNDLE, value));
   1536     }
   1537 
   1538     /**
   1539      *
   1540      * @param viewId
   1541      * @param methodName
   1542      * @param value
   1543      */
   1544     public void setIntent(int viewId, String methodName, Intent value) {
   1545         addAction(new ReflectionAction(viewId, methodName, ReflectionAction.INTENT, value));
   1546     }
   1547 
   1548     /**
   1549      * Equivalent to calling View.setContentDescription
   1550      *
   1551      * @param viewId The id of the view whose content description should change
   1552      * @param contentDescription The new content description for the view
   1553      */
   1554     public void setContentDescription(int viewId, CharSequence contentDescription) {
   1555         setCharSequence(viewId, "setContentDescription", contentDescription);
   1556     }
   1557 
   1558     /**
   1559      * Inflates the view hierarchy represented by this object and applies
   1560      * all of the actions.
   1561      *
   1562      * <p><strong>Caller beware: this may throw</strong>
   1563      *
   1564      * @param context Default context to use
   1565      * @param parent Parent that the resulting view hierarchy will be attached to. This method
   1566      * does <strong>not</strong> attach the hierarchy. The caller should do so when appropriate.
   1567      * @return The inflated view hierarchy
   1568      */
   1569     public View apply(Context context, ViewGroup parent) {
   1570         View result;
   1571 
   1572         Context c = prepareContext(context);
   1573 
   1574         LayoutInflater inflater = (LayoutInflater)
   1575                 c.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
   1576 
   1577         inflater = inflater.cloneInContext(c);
   1578         inflater.setFilter(this);
   1579 
   1580         result = inflater.inflate(mLayoutId, parent, false);
   1581 
   1582         performApply(result, parent);
   1583 
   1584         return result;
   1585     }
   1586 
   1587     /**
   1588      * Applies all of the actions to the provided view.
   1589      *
   1590      * <p><strong>Caller beware: this may throw</strong>
   1591      *
   1592      * @param v The view to apply the actions to.  This should be the result of
   1593      * the {@link #apply(Context,ViewGroup)} call.
   1594      */
   1595     public void reapply(Context context, View v) {
   1596         prepareContext(context);
   1597         performApply(v, (ViewGroup) v.getParent());
   1598     }
   1599 
   1600     private void performApply(View v, ViewGroup parent) {
   1601         if (mActions != null) {
   1602             final int count = mActions.size();
   1603             for (int i = 0; i < count; i++) {
   1604                 Action a = mActions.get(i);
   1605                 a.apply(v, parent);
   1606             }
   1607         }
   1608     }
   1609 
   1610     private Context prepareContext(Context context) {
   1611         Context c;
   1612         String packageName = mPackage;
   1613 
   1614         if (packageName != null) {
   1615             try {
   1616                 c = context.createPackageContext(packageName, Context.CONTEXT_RESTRICTED);
   1617             } catch (NameNotFoundException e) {
   1618                 Log.e(LOG_TAG, "Package name " + packageName + " not found");
   1619                 c = context;
   1620             }
   1621         } else {
   1622             c = context;
   1623         }
   1624 
   1625         return c;
   1626     }
   1627 
   1628     /* (non-Javadoc)
   1629      * Used to restrict the views which can be inflated
   1630      *
   1631      * @see android.view.LayoutInflater.Filter#onLoadClass(java.lang.Class)
   1632      */
   1633     public boolean onLoadClass(Class clazz) {
   1634         return clazz.isAnnotationPresent(RemoteView.class);
   1635     }
   1636 
   1637     public int describeContents() {
   1638         return 0;
   1639     }
   1640 
   1641     public void writeToParcel(Parcel dest, int flags) {
   1642         dest.writeString(mPackage);
   1643         dest.writeInt(mLayoutId);
   1644         dest.writeInt(mIsWidgetCollectionChild ? 1 : 0);
   1645         int count;
   1646         if (mActions != null) {
   1647             count = mActions.size();
   1648         } else {
   1649             count = 0;
   1650         }
   1651         dest.writeInt(count);
   1652         for (int i=0; i<count; i++) {
   1653             Action a = mActions.get(i);
   1654             a.writeToParcel(dest, 0);
   1655         }
   1656     }
   1657 
   1658     /**
   1659      * Parcelable.Creator that instantiates RemoteViews objects
   1660      */
   1661     public static final Parcelable.Creator<RemoteViews> CREATOR = new Parcelable.Creator<RemoteViews>() {
   1662         public RemoteViews createFromParcel(Parcel parcel) {
   1663             return new RemoteViews(parcel);
   1664         }
   1665 
   1666         public RemoteViews[] newArray(int size) {
   1667             return new RemoteViews[size];
   1668         }
   1669     };
   1670 }
   1671