• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 {
ActionException(Exception ex)113         public ActionException(Exception ex) {
114             super(ex);
115         }
ActionException(String message)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 {
apply(View root, ViewGroup rootParent)128         public abstract void apply(View root, ViewGroup rootParent) throws ActionException;
129 
describeContents()130         public int describeContents() {
131             return 0;
132         }
133 
134         /**
135          * Overridden by each class to report on it's own memory usage
136          */
updateMemoryUsageEstimate(MemoryUsageCounter counter)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 
startIntentSafely(Context context, PendingIntent pendingIntent, Intent fillInIntent)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 
SetEmptyView(int viewId, int emptyViewId)169         SetEmptyView(int viewId, int emptyViewId) {
170             this.viewId = viewId;
171             this.emptyViewId = emptyViewId;
172         }
173 
SetEmptyView(Parcel in)174         SetEmptyView(Parcel in) {
175             this.viewId = in.readInt();
176             this.emptyViewId = in.readInt();
177         }
178 
writeToParcel(Parcel out, int flags)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
apply(View root, ViewGroup rootParent)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 {
SetOnClickFillInIntent(int id, Intent fillInIntent)200         public SetOnClickFillInIntent(int id, Intent fillInIntent) {
201             this.viewId = id;
202             this.fillInIntent = fillInIntent;
203         }
204 
SetOnClickFillInIntent(Parcel parcel)205         public SetOnClickFillInIntent(Parcel parcel) {
206             viewId = parcel.readInt();
207             fillInIntent = Intent.CREATOR.createFromParcel(parcel);
208         }
209 
writeToParcel(Parcel dest, int flags)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
apply(View root, ViewGroup rootParent)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 {
SetPendingIntentTemplate(int id, PendingIntent pendingIntentTemplate)281         public SetPendingIntentTemplate(int id, PendingIntent pendingIntentTemplate) {
282             this.viewId = id;
283             this.pendingIntentTemplate = pendingIntentTemplate;
284         }
285 
SetPendingIntentTemplate(Parcel parcel)286         public SetPendingIntentTemplate(Parcel parcel) {
287             viewId = parcel.readInt();
288             pendingIntentTemplate = PendingIntent.readPendingIntentOrNullFromParcel(parcel);
289         }
290 
writeToParcel(Parcel dest, int flags)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
apply(View root, ViewGroup rootParent)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 {
SetRemoteViewsAdapterIntent(int id, Intent intent)364         public SetRemoteViewsAdapterIntent(int id, Intent intent) {
365             this.viewId = id;
366             this.intent = intent;
367         }
368 
SetRemoteViewsAdapterIntent(Parcel parcel)369         public SetRemoteViewsAdapterIntent(Parcel parcel) {
370             viewId = parcel.readInt();
371             intent = Intent.CREATOR.createFromParcel(parcel);
372         }
373 
writeToParcel(Parcel dest, int flags)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
apply(View root, ViewGroup rootParent)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 {
SetOnClickPendingIntent(int id, PendingIntent pendingIntent)423         public SetOnClickPendingIntent(int id, PendingIntent pendingIntent) {
424             this.viewId = id;
425             this.pendingIntent = pendingIntent;
426         }
427 
SetOnClickPendingIntent(Parcel parcel)428         public SetOnClickPendingIntent(Parcel parcel) {
429             viewId = parcel.readInt();
430             pendingIntent = PendingIntent.readPendingIntentOrNullFromParcel(parcel);
431         }
432 
writeToParcel(Parcel dest, int flags)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
apply(View root, ViewGroup rootParent)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 {
SetDrawableParameters(int id, boolean targetBackground, int alpha, int colorFilter, PorterDuff.Mode mode, int level)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 
SetDrawableParameters(Parcel parcel)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 
writeToParcel(Parcel dest, int flags)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
apply(View root, ViewGroup rootParent)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 
ReflectionActionWithoutParams(int viewId, String methodName)579         ReflectionActionWithoutParams(int viewId, String methodName) {
580             this.viewId = viewId;
581             this.methodName = methodName;
582         }
583 
ReflectionActionWithoutParams(Parcel in)584         ReflectionActionWithoutParams(Parcel in) {
585             this.viewId = in.readInt();
586             this.methodName = in.readString();
587         }
588 
writeToParcel(Parcel out, int flags)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
apply(View root, ViewGroup rootParent)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 
ReflectionAction(int viewId, String methodName, int type, Object value)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 
ReflectionAction(Parcel in)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 
writeToParcel(Parcel out, int flags)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 
getParameterType()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
apply(View root, ViewGroup rootParent)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
updateMemoryUsageEstimate(MemoryUsageCounter counter)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 {
ViewGroupAction(int viewId, RemoteViews nestedViews)890         public ViewGroupAction(int viewId, RemoteViews nestedViews) {
891             this.viewId = viewId;
892             this.nestedViews = nestedViews;
893         }
894 
ViewGroupAction(Parcel parcel)895         public ViewGroupAction(Parcel parcel) {
896             viewId = parcel.readInt();
897             nestedViews = parcel.readParcelable(null);
898         }
899 
writeToParcel(Parcel dest, int flags)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
apply(View root, ViewGroup rootParent)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
updateMemoryUsageEstimate(MemoryUsageCounter counter)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 {
clear()938         public void clear() {
939             mBitmapHeapMemoryUsage = 0;
940         }
941 
bitmapIncrement(int numBytes)942         public void bitmapIncrement(int numBytes) {
943             mBitmapHeapMemoryUsage += numBytes;
944         }
945 
getBitmapHeapMemoryUsage()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      */
RemoteViews(String packageName, int layoutId)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      */
RemoteViews(Parcel parcel)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
clone()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 
getPackage()1035     public String getPackage() {
1036         return mPackage;
1037     }
1038 
getLayoutId()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      */
setIsWidgetCollectionChild(boolean isWidgetCollectionChild)1050     void setIsWidgetCollectionChild(boolean isWidgetCollectionChild) {
1051         mIsWidgetCollectionChild = isWidgetCollectionChild;
1052     }
1053 
1054     /**
1055      * Updates the memory usage statistics.
1056      */
recalculateMemoryUsage()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      */
estimateBitmapMemoryUsage()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      */
addAction(Action a)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      */
addView(int viewId, RemoteViews nestedView)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      */
removeAllViews(int viewId)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      */
showNext(int viewId)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      */
showPrevious(int viewId)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      */
setDisplayedChild(int viewId, int childIndex)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      */
setViewVisibility(int viewId, int visibility)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      */
setTextViewText(int viewId, CharSequence text)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      */
setImageViewResource(int viewId, int srcId)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      */
setImageViewUri(int viewId, Uri uri)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      */
setImageViewBitmap(int viewId, Bitmap bitmap)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      */
setEmptyView(int viewId, int emptyViewId)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      */
setChronometer(int viewId, long base, String format, boolean started)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      */
setProgressBar(int viewId, int max, int progress, boolean indeterminate)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      */
setOnClickPendingIntent(int viewId, PendingIntent pendingIntent)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      */
setPendingIntentTemplate(int viewId, PendingIntent pendingIntentTemplate)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      */
setOnClickFillInIntent(int viewId, Intent fillInIntent)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      */
setDrawableParameters(int viewId, boolean targetBackground, int alpha, int colorFilter, PorterDuff.Mode mode, int level)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      */
setTextColor(int viewId, int color)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
setRemoteAdapter(int appWidgetId, int viewId, Intent intent)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      */
setRemoteAdapter(int viewId, Intent intent)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      */
setScrollPosition(int viewId, int position)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      */
setRelativeScrollPosition(int viewId, int offset)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      */
setBoolean(int viewId, String methodName, boolean value)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      */
setByte(int viewId, String methodName, byte value)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      */
setShort(int viewId, String methodName, short value)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      */
setInt(int viewId, String methodName, int value)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      */
setLong(int viewId, String methodName, long value)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      */
setFloat(int viewId, String methodName, float value)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      */
setDouble(int viewId, String methodName, double value)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      */
setChar(int viewId, String methodName, char value)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      */
setString(int viewId, String methodName, String value)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      */
setCharSequence(int viewId, String methodName, CharSequence value)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      */
setUri(int viewId, String methodName, Uri value)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      */
setBitmap(int viewId, String methodName, Bitmap value)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      */
setBundle(int viewId, String methodName, Bundle value)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      */
setIntent(int viewId, String methodName, Intent value)1544     public void setIntent(int viewId, String methodName, Intent value) {
1545         addAction(new ReflectionAction(viewId, methodName, ReflectionAction.INTENT, value));
1546     }
1547 
1548     /**
1549      * Inflates the view hierarchy represented by this object and applies
1550      * all of the actions.
1551      *
1552      * <p><strong>Caller beware: this may throw</strong>
1553      *
1554      * @param context Default context to use
1555      * @param parent Parent that the resulting view hierarchy will be attached to. This method
1556      * does <strong>not</strong> attach the hierarchy. The caller should do so when appropriate.
1557      * @return The inflated view hierarchy
1558      */
apply(Context context, ViewGroup parent)1559     public View apply(Context context, ViewGroup parent) {
1560         View result;
1561 
1562         Context c = prepareContext(context);
1563 
1564         LayoutInflater inflater = (LayoutInflater)
1565                 c.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
1566 
1567         inflater = inflater.cloneInContext(c);
1568         inflater.setFilter(this);
1569 
1570         result = inflater.inflate(mLayoutId, parent, false);
1571 
1572         performApply(result, parent);
1573 
1574         return result;
1575     }
1576 
1577     /**
1578      * Applies all of the actions to the provided view.
1579      *
1580      * <p><strong>Caller beware: this may throw</strong>
1581      *
1582      * @param v The view to apply the actions to.  This should be the result of
1583      * the {@link #apply(Context,ViewGroup)} call.
1584      */
reapply(Context context, View v)1585     public void reapply(Context context, View v) {
1586         prepareContext(context);
1587         performApply(v, (ViewGroup) v.getParent());
1588     }
1589 
performApply(View v, ViewGroup parent)1590     private void performApply(View v, ViewGroup parent) {
1591         if (mActions != null) {
1592             final int count = mActions.size();
1593             for (int i = 0; i < count; i++) {
1594                 Action a = mActions.get(i);
1595                 a.apply(v, parent);
1596             }
1597         }
1598     }
1599 
prepareContext(Context context)1600     private Context prepareContext(Context context) {
1601         Context c;
1602         String packageName = mPackage;
1603 
1604         if (packageName != null) {
1605             try {
1606                 c = context.createPackageContext(packageName, Context.CONTEXT_RESTRICTED);
1607             } catch (NameNotFoundException e) {
1608                 Log.e(LOG_TAG, "Package name " + packageName + " not found");
1609                 c = context;
1610             }
1611         } else {
1612             c = context;
1613         }
1614 
1615         return c;
1616     }
1617 
1618     /* (non-Javadoc)
1619      * Used to restrict the views which can be inflated
1620      *
1621      * @see android.view.LayoutInflater.Filter#onLoadClass(java.lang.Class)
1622      */
onLoadClass(Class clazz)1623     public boolean onLoadClass(Class clazz) {
1624         return clazz.isAnnotationPresent(RemoteView.class);
1625     }
1626 
describeContents()1627     public int describeContents() {
1628         return 0;
1629     }
1630 
writeToParcel(Parcel dest, int flags)1631     public void writeToParcel(Parcel dest, int flags) {
1632         dest.writeString(mPackage);
1633         dest.writeInt(mLayoutId);
1634         dest.writeInt(mIsWidgetCollectionChild ? 1 : 0);
1635         int count;
1636         if (mActions != null) {
1637             count = mActions.size();
1638         } else {
1639             count = 0;
1640         }
1641         dest.writeInt(count);
1642         for (int i=0; i<count; i++) {
1643             Action a = mActions.get(i);
1644             a.writeToParcel(dest, 0);
1645         }
1646     }
1647 
1648     /**
1649      * Parcelable.Creator that instantiates RemoteViews objects
1650      */
1651     public static final Parcelable.Creator<RemoteViews> CREATOR = new Parcelable.Creator<RemoteViews>() {
1652         public RemoteViews createFromParcel(Parcel parcel) {
1653             return new RemoteViews(parcel);
1654         }
1655 
1656         public RemoteViews[] newArray(int size) {
1657             return new RemoteViews[size];
1658         }
1659     };
1660 }
1661