• 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 android.app.PendingIntent;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.content.IntentSender;
23 import android.content.pm.PackageManager.NameNotFoundException;
24 import android.graphics.Bitmap;
25 import android.graphics.PorterDuff;
26 import android.graphics.drawable.Drawable;
27 import android.net.Uri;
28 import android.os.Parcel;
29 import android.os.Parcelable;
30 import android.text.TextUtils;
31 import android.util.Log;
32 import android.view.LayoutInflater;
33 import android.view.RemotableViewMethod;
34 import android.view.View;
35 import android.view.ViewGroup;
36 import android.view.LayoutInflater.Filter;
37 import android.view.View.OnClickListener;
38 
39 import java.lang.Class;
40 import java.lang.annotation.ElementType;
41 import java.lang.annotation.Retention;
42 import java.lang.annotation.RetentionPolicy;
43 import java.lang.annotation.Target;
44 import java.lang.reflect.Method;
45 import java.util.ArrayList;
46 
47 
48 /**
49  * A class that describes a view hierarchy that can be displayed in
50  * another process. The hierarchy is inflated from a layout resource
51  * file, and this class provides some basic operations for modifying
52  * the content of the inflated hierarchy.
53  */
54 public class RemoteViews implements Parcelable, Filter {
55 
56     private static final String LOG_TAG = "RemoteViews";
57 
58     /**
59      * The package name of the package containing the layout
60      * resource. (Added to the parcel)
61      */
62     private String mPackage;
63 
64     /**
65      * The resource ID of the layout file. (Added to the parcel)
66      */
67     private int mLayoutId;
68 
69     /**
70      * An array of actions to perform on the view tree once it has been
71      * inflated
72      */
73     private ArrayList<Action> mActions;
74 
75 
76     /**
77      * This annotation indicates that a subclass of View is alllowed to be used
78      * with the {@link RemoteViews} mechanism.
79      */
80     @Target({ ElementType.TYPE })
81     @Retention(RetentionPolicy.RUNTIME)
82     public @interface RemoteView {
83     }
84 
85     /**
86      * Exception to send when something goes wrong executing an action
87      *
88      */
89     public static class ActionException extends RuntimeException {
ActionException(Exception ex)90         public ActionException(Exception ex) {
91             super(ex);
92         }
ActionException(String message)93         public ActionException(String message) {
94             super(message);
95         }
96     }
97 
98     /**
99      * Base class for all actions that can be performed on an
100      * inflated view.
101      *
102      */
103     private abstract static class Action implements Parcelable {
apply(View root)104         public abstract void apply(View root) throws ActionException;
105 
describeContents()106         public int describeContents() {
107             return 0;
108         }
109     }
110 
111     /**
112      * Equivalent to calling
113      * {@link android.view.View#setOnClickListener(android.view.View.OnClickListener)}
114      * to launch the provided {@link PendingIntent}.
115      */
116     private class SetOnClickPendingIntent extends Action {
SetOnClickPendingIntent(int id, PendingIntent pendingIntent)117         public SetOnClickPendingIntent(int id, PendingIntent pendingIntent) {
118             this.viewId = id;
119             this.pendingIntent = pendingIntent;
120         }
121 
SetOnClickPendingIntent(Parcel parcel)122         public SetOnClickPendingIntent(Parcel parcel) {
123             viewId = parcel.readInt();
124             pendingIntent = PendingIntent.readPendingIntentOrNullFromParcel(parcel);
125         }
126 
writeToParcel(Parcel dest, int flags)127         public void writeToParcel(Parcel dest, int flags) {
128             dest.writeInt(TAG);
129             dest.writeInt(viewId);
130             pendingIntent.writeToParcel(dest, 0 /* no flags */);
131         }
132 
133         @Override
apply(View root)134         public void apply(View root) {
135             final View target = root.findViewById(viewId);
136             if (target != null && pendingIntent != null) {
137                 OnClickListener listener = new OnClickListener() {
138                     public void onClick(View v) {
139                         try {
140                             // TODO: Unregister this handler if PendingIntent.FLAG_ONE_SHOT?
141                             v.getContext().startIntentSender(
142                                     pendingIntent.getIntentSender(), null,
143                                     Intent.FLAG_ACTIVITY_NEW_TASK,
144                                     Intent.FLAG_ACTIVITY_NEW_TASK, 0);
145                         } catch (IntentSender.SendIntentException e) {
146                             android.util.Log.e(LOG_TAG, "Cannot send pending intent: ", e);
147                         }
148                     }
149                 };
150                 target.setOnClickListener(listener);
151             }
152         }
153 
154         int viewId;
155         PendingIntent pendingIntent;
156 
157         public final static int TAG = 1;
158     }
159 
160     /**
161      * Equivalent to calling a combination of {@link Drawable#setAlpha(int)},
162      * {@link Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)},
163      * and/or {@link Drawable#setLevel(int)} on the {@link Drawable} of a given view.
164      * <p>
165      * These operations will be performed on the {@link Drawable} returned by the
166      * target {@link View#getBackground()} by default.  If targetBackground is false,
167      * we assume the target is an {@link ImageView} and try applying the operations
168      * to {@link ImageView#getDrawable()}.
169      * <p>
170      * You can omit specific calls by marking their values with null or -1.
171      */
172     private class SetDrawableParameters extends Action {
SetDrawableParameters(int id, boolean targetBackground, int alpha, int colorFilter, PorterDuff.Mode mode, int level)173         public SetDrawableParameters(int id, boolean targetBackground, int alpha,
174                 int colorFilter, PorterDuff.Mode mode, int level) {
175             this.viewId = id;
176             this.targetBackground = targetBackground;
177             this.alpha = alpha;
178             this.colorFilter = colorFilter;
179             this.filterMode = mode;
180             this.level = level;
181         }
182 
SetDrawableParameters(Parcel parcel)183         public SetDrawableParameters(Parcel parcel) {
184             viewId = parcel.readInt();
185             targetBackground = parcel.readInt() != 0;
186             alpha = parcel.readInt();
187             colorFilter = parcel.readInt();
188             boolean hasMode = parcel.readInt() != 0;
189             if (hasMode) {
190                 filterMode = PorterDuff.Mode.valueOf(parcel.readString());
191             } else {
192                 filterMode = null;
193             }
194             level = parcel.readInt();
195         }
196 
writeToParcel(Parcel dest, int flags)197         public void writeToParcel(Parcel dest, int flags) {
198             dest.writeInt(TAG);
199             dest.writeInt(viewId);
200             dest.writeInt(targetBackground ? 1 : 0);
201             dest.writeInt(alpha);
202             dest.writeInt(colorFilter);
203             if (filterMode != null) {
204                 dest.writeInt(1);
205                 dest.writeString(filterMode.toString());
206             } else {
207                 dest.writeInt(0);
208             }
209             dest.writeInt(level);
210         }
211 
212         @Override
apply(View root)213         public void apply(View root) {
214             final View target = root.findViewById(viewId);
215             if (target == null) {
216                 return;
217             }
218 
219             // Pick the correct drawable to modify for this view
220             Drawable targetDrawable = null;
221             if (targetBackground) {
222                 targetDrawable = target.getBackground();
223             } else if (target instanceof ImageView) {
224                 ImageView imageView = (ImageView) target;
225                 targetDrawable = imageView.getDrawable();
226             }
227 
228             if (targetDrawable != null) {
229                 // Perform modifications only if values are set correctly
230                 if (alpha != -1) {
231                     targetDrawable.setAlpha(alpha);
232                 }
233                 if (colorFilter != -1 && filterMode != null) {
234                     targetDrawable.setColorFilter(colorFilter, filterMode);
235                 }
236                 if (level != -1) {
237                     targetDrawable.setLevel(level);
238                 }
239             }
240         }
241 
242         int viewId;
243         boolean targetBackground;
244         int alpha;
245         int colorFilter;
246         PorterDuff.Mode filterMode;
247         int level;
248 
249         public final static int TAG = 3;
250     }
251 
252     /**
253      * Base class for the reflection actions.
254      */
255     private class ReflectionAction extends Action {
256         static final int TAG = 2;
257 
258         static final int BOOLEAN = 1;
259         static final int BYTE = 2;
260         static final int SHORT = 3;
261         static final int INT = 4;
262         static final int LONG = 5;
263         static final int FLOAT = 6;
264         static final int DOUBLE = 7;
265         static final int CHAR = 8;
266         static final int STRING = 9;
267         static final int CHAR_SEQUENCE = 10;
268         static final int URI = 11;
269         static final int BITMAP = 12;
270 
271         int viewId;
272         String methodName;
273         int type;
274         Object value;
275 
ReflectionAction(int viewId, String methodName, int type, Object value)276         ReflectionAction(int viewId, String methodName, int type, Object value) {
277             this.viewId = viewId;
278             this.methodName = methodName;
279             this.type = type;
280             this.value = value;
281         }
282 
ReflectionAction(Parcel in)283         ReflectionAction(Parcel in) {
284             this.viewId = in.readInt();
285             this.methodName = in.readString();
286             this.type = in.readInt();
287             //noinspection ConstantIfStatement
288             if (false) {
289                 Log.d("RemoteViews", "read viewId=0x" + Integer.toHexString(this.viewId)
290                         + " methodName=" + this.methodName + " type=" + this.type);
291             }
292             switch (this.type) {
293                 case BOOLEAN:
294                     this.value = in.readInt() != 0;
295                     break;
296                 case BYTE:
297                     this.value = in.readByte();
298                     break;
299                 case SHORT:
300                     this.value = (short)in.readInt();
301                     break;
302                 case INT:
303                     this.value = in.readInt();
304                     break;
305                 case LONG:
306                     this.value = in.readLong();
307                     break;
308                 case FLOAT:
309                     this.value = in.readFloat();
310                     break;
311                 case DOUBLE:
312                     this.value = in.readDouble();
313                     break;
314                 case CHAR:
315                     this.value = (char)in.readInt();
316                     break;
317                 case STRING:
318                     this.value = in.readString();
319                     break;
320                 case CHAR_SEQUENCE:
321                     this.value = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
322                     break;
323                 case URI:
324                     this.value = Uri.CREATOR.createFromParcel(in);
325                     break;
326                 case BITMAP:
327                     this.value = Bitmap.CREATOR.createFromParcel(in);
328                     break;
329                 default:
330                     break;
331             }
332         }
333 
writeToParcel(Parcel out, int flags)334         public void writeToParcel(Parcel out, int flags) {
335             out.writeInt(TAG);
336             out.writeInt(this.viewId);
337             out.writeString(this.methodName);
338             out.writeInt(this.type);
339             //noinspection ConstantIfStatement
340             if (false) {
341                 Log.d("RemoteViews", "write viewId=0x" + Integer.toHexString(this.viewId)
342                         + " methodName=" + this.methodName + " type=" + this.type);
343             }
344             switch (this.type) {
345                 case BOOLEAN:
346                     out.writeInt((Boolean) this.value ? 1 : 0);
347                     break;
348                 case BYTE:
349                     out.writeByte((Byte) this.value);
350                     break;
351                 case SHORT:
352                     out.writeInt((Short) this.value);
353                     break;
354                 case INT:
355                     out.writeInt((Integer) this.value);
356                     break;
357                 case LONG:
358                     out.writeLong((Long) this.value);
359                     break;
360                 case FLOAT:
361                     out.writeFloat((Float) this.value);
362                     break;
363                 case DOUBLE:
364                     out.writeDouble((Double) this.value);
365                     break;
366                 case CHAR:
367                     out.writeInt((int)((Character)this.value).charValue());
368                     break;
369                 case STRING:
370                     out.writeString((String)this.value);
371                     break;
372                 case CHAR_SEQUENCE:
373                     TextUtils.writeToParcel((CharSequence)this.value, out, flags);
374                     break;
375                 case URI:
376                     ((Uri)this.value).writeToParcel(out, flags);
377                     break;
378                 case BITMAP:
379                     ((Bitmap)this.value).writeToParcel(out, flags);
380                     break;
381                 default:
382                     break;
383             }
384         }
385 
getParameterType()386         private Class getParameterType() {
387             switch (this.type) {
388                 case BOOLEAN:
389                     return boolean.class;
390                 case BYTE:
391                     return byte.class;
392                 case SHORT:
393                     return short.class;
394                 case INT:
395                     return int.class;
396                 case LONG:
397                     return long.class;
398                 case FLOAT:
399                     return float.class;
400                 case DOUBLE:
401                     return double.class;
402                 case CHAR:
403                     return char.class;
404                 case STRING:
405                     return String.class;
406                 case CHAR_SEQUENCE:
407                     return CharSequence.class;
408                 case URI:
409                     return Uri.class;
410                 case BITMAP:
411                     return Bitmap.class;
412                 default:
413                     return null;
414             }
415         }
416 
417         @Override
apply(View root)418         public void apply(View root) {
419             final View view = root.findViewById(viewId);
420             if (view == null) {
421                 throw new ActionException("can't find view: 0x" + Integer.toHexString(viewId));
422             }
423 
424             Class param = getParameterType();
425             if (param == null) {
426                 throw new ActionException("bad type: " + this.type);
427             }
428 
429             Class klass = view.getClass();
430             Method method;
431             try {
432                 method = klass.getMethod(this.methodName, getParameterType());
433             }
434             catch (NoSuchMethodException ex) {
435                 throw new ActionException("view: " + klass.getName() + " doesn't have method: "
436                         + this.methodName + "(" + param.getName() + ")");
437             }
438 
439             if (!method.isAnnotationPresent(RemotableViewMethod.class)) {
440                 throw new ActionException("view: " + klass.getName()
441                         + " can't use method with RemoteViews: "
442                         + this.methodName + "(" + param.getName() + ")");
443             }
444 
445             try {
446                 //noinspection ConstantIfStatement
447                 if (false) {
448                     Log.d("RemoteViews", "view: " + klass.getName() + " calling method: "
449                         + this.methodName + "(" + param.getName() + ") with "
450                         + (this.value == null ? "null" : this.value.getClass().getName()));
451                 }
452                 method.invoke(view, this.value);
453             }
454             catch (Exception ex) {
455                 throw new ActionException(ex);
456             }
457         }
458     }
459 
460 
461     /**
462      * Create a new RemoteViews object that will display the views contained
463      * in the specified layout file.
464      *
465      * @param packageName Name of the package that contains the layout resource
466      * @param layoutId The id of the layout resource
467      */
RemoteViews(String packageName, int layoutId)468     public RemoteViews(String packageName, int layoutId) {
469         mPackage = packageName;
470         mLayoutId = layoutId;
471     }
472 
473     /**
474      * Reads a RemoteViews object from a parcel.
475      *
476      * @param parcel
477      */
RemoteViews(Parcel parcel)478     public RemoteViews(Parcel parcel) {
479         mPackage = parcel.readString();
480         mLayoutId = parcel.readInt();
481         int count = parcel.readInt();
482         if (count > 0) {
483             mActions = new ArrayList<Action>(count);
484             for (int i=0; i<count; i++) {
485                 int tag = parcel.readInt();
486                 switch (tag) {
487                 case SetOnClickPendingIntent.TAG:
488                     mActions.add(new SetOnClickPendingIntent(parcel));
489                     break;
490                 case SetDrawableParameters.TAG:
491                     mActions.add(new SetDrawableParameters(parcel));
492                     break;
493                 case ReflectionAction.TAG:
494                     mActions.add(new ReflectionAction(parcel));
495                     break;
496                 default:
497                     throw new ActionException("Tag " + tag + " not found");
498                 }
499             }
500         }
501     }
502 
getPackage()503     public String getPackage() {
504         return mPackage;
505     }
506 
getLayoutId()507     public int getLayoutId() {
508         return mLayoutId;
509     }
510 
511     /**
512      * Add an action to be executed on the remote side when apply is called.
513      *
514      * @param a The action to add
515      */
addAction(Action a)516     private void addAction(Action a) {
517         if (mActions == null) {
518             mActions = new ArrayList<Action>();
519         }
520         mActions.add(a);
521     }
522 
523     /**
524      * Equivalent to calling View.setVisibility
525      *
526      * @param viewId The id of the view whose visibility should change
527      * @param visibility The new visibility for the view
528      */
setViewVisibility(int viewId, int visibility)529     public void setViewVisibility(int viewId, int visibility) {
530         setInt(viewId, "setVisibility", visibility);
531     }
532 
533     /**
534      * Equivalent to calling TextView.setText
535      *
536      * @param viewId The id of the view whose text should change
537      * @param text The new text for the view
538      */
setTextViewText(int viewId, CharSequence text)539     public void setTextViewText(int viewId, CharSequence text) {
540         setCharSequence(viewId, "setText", text);
541     }
542 
543     /**
544      * Equivalent to calling ImageView.setImageResource
545      *
546      * @param viewId The id of the view whose drawable should change
547      * @param srcId The new resource id for the drawable
548      */
setImageViewResource(int viewId, int srcId)549     public void setImageViewResource(int viewId, int srcId) {
550         setInt(viewId, "setImageResource", srcId);
551     }
552 
553     /**
554      * Equivalent to calling ImageView.setImageURI
555      *
556      * @param viewId The id of the view whose drawable should change
557      * @param uri The Uri for the image
558      */
setImageViewUri(int viewId, Uri uri)559     public void setImageViewUri(int viewId, Uri uri) {
560         setUri(viewId, "setImageURI", uri);
561     }
562 
563     /**
564      * Equivalent to calling ImageView.setImageBitmap
565      *
566      * @param viewId The id of the view whose drawable should change
567      * @param bitmap The new Bitmap for the drawable
568      */
setImageViewBitmap(int viewId, Bitmap bitmap)569     public void setImageViewBitmap(int viewId, Bitmap bitmap) {
570         setBitmap(viewId, "setImageBitmap", bitmap);
571     }
572 
573     /**
574      * Equivalent to calling {@link Chronometer#setBase Chronometer.setBase},
575      * {@link Chronometer#setFormat Chronometer.setFormat},
576      * and {@link Chronometer#start Chronometer.start()} or
577      * {@link Chronometer#stop Chronometer.stop()}.
578      *
579      * @param viewId The id of the view whose text should change
580      * @param base The time at which the timer would have read 0:00.  This
581      *             time should be based off of
582      *             {@link android.os.SystemClock#elapsedRealtime SystemClock.elapsedRealtime()}.
583      * @param format The Chronometer format string, or null to
584      *               simply display the timer value.
585      * @param started True if you want the clock to be started, false if not.
586      */
setChronometer(int viewId, long base, String format, boolean started)587     public void setChronometer(int viewId, long base, String format, boolean started) {
588         setLong(viewId, "setBase", base);
589         setString(viewId, "setFormat", format);
590         setBoolean(viewId, "setStarted", started);
591     }
592 
593     /**
594      * Equivalent to calling {@link ProgressBar#setMax ProgressBar.setMax},
595      * {@link ProgressBar#setProgress ProgressBar.setProgress}, and
596      * {@link ProgressBar#setIndeterminate ProgressBar.setIndeterminate}
597      *
598      * If indeterminate is true, then the values for max and progress are ignored.
599      *
600      * @param viewId The id of the view whose text should change
601      * @param max The 100% value for the progress bar
602      * @param progress The current value of the progress bar.
603      * @param indeterminate True if the progress bar is indeterminate,
604      *                false if not.
605      */
setProgressBar(int viewId, int max, int progress, boolean indeterminate)606     public void setProgressBar(int viewId, int max, int progress,
607             boolean indeterminate) {
608         setBoolean(viewId, "setIndeterminate", indeterminate);
609         if (!indeterminate) {
610             setInt(viewId, "setMax", max);
611             setInt(viewId, "setProgress", progress);
612         }
613     }
614 
615     /**
616      * Equivalent to calling
617      * {@link android.view.View#setOnClickListener(android.view.View.OnClickListener)}
618      * to launch the provided {@link PendingIntent}.
619      *
620      * @param viewId The id of the view that will trigger the {@link PendingIntent} when clicked
621      * @param pendingIntent The {@link PendingIntent} to send when user clicks
622      */
setOnClickPendingIntent(int viewId, PendingIntent pendingIntent)623     public void setOnClickPendingIntent(int viewId, PendingIntent pendingIntent) {
624         addAction(new SetOnClickPendingIntent(viewId, pendingIntent));
625     }
626 
627     /**
628      * @hide
629      * Equivalent to calling a combination of {@link Drawable#setAlpha(int)},
630      * {@link Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)},
631      * and/or {@link Drawable#setLevel(int)} on the {@link Drawable} of a given
632      * view.
633      * <p>
634      * You can omit specific calls by marking their values with null or -1.
635      *
636      * @param viewId The id of the view that contains the target
637      *            {@link Drawable}
638      * @param targetBackground If true, apply these parameters to the
639      *            {@link Drawable} returned by
640      *            {@link android.view.View#getBackground()}. Otherwise, assume
641      *            the target view is an {@link ImageView} and apply them to
642      *            {@link ImageView#getDrawable()}.
643      * @param alpha Specify an alpha value for the drawable, or -1 to leave
644      *            unchanged.
645      * @param colorFilter Specify a color for a
646      *            {@link android.graphics.ColorFilter} for this drawable, or -1
647      *            to leave unchanged.
648      * @param mode Specify a PorterDuff mode for this drawable, or null to leave
649      *            unchanged.
650      * @param level Specify the level for the drawable, or -1 to leave
651      *            unchanged.
652      */
setDrawableParameters(int viewId, boolean targetBackground, int alpha, int colorFilter, PorterDuff.Mode mode, int level)653     public void setDrawableParameters(int viewId, boolean targetBackground, int alpha,
654             int colorFilter, PorterDuff.Mode mode, int level) {
655         addAction(new SetDrawableParameters(viewId, targetBackground, alpha,
656                 colorFilter, mode, level));
657     }
658 
659     /**
660      * Equivalent to calling {@link android.widget.TextView#setTextColor(int)}.
661      *
662      * @param viewId The id of the view whose text should change
663      * @param color Sets the text color for all the states (normal, selected,
664      *            focused) to be this color.
665      */
setTextColor(int viewId, int color)666     public void setTextColor(int viewId, int color) {
667         setInt(viewId, "setTextColor", color);
668     }
669 
670     /**
671      * Call a method taking one boolean on a view in the layout for this RemoteViews.
672      *
673      * @param viewId The id of the view whose text should change
674      * @param methodName The name of the method to call.
675      * @param value The value to pass to the method.
676      */
setBoolean(int viewId, String methodName, boolean value)677     public void setBoolean(int viewId, String methodName, boolean value) {
678         addAction(new ReflectionAction(viewId, methodName, ReflectionAction.BOOLEAN, value));
679     }
680 
681     /**
682      * Call a method taking one byte on a view in the layout for this RemoteViews.
683      *
684      * @param viewId The id of the view whose text should change
685      * @param methodName The name of the method to call.
686      * @param value The value to pass to the method.
687      */
setByte(int viewId, String methodName, byte value)688     public void setByte(int viewId, String methodName, byte value) {
689         addAction(new ReflectionAction(viewId, methodName, ReflectionAction.BYTE, value));
690     }
691 
692     /**
693      * Call a method taking one short on a view in the layout for this RemoteViews.
694      *
695      * @param viewId The id of the view whose text should change
696      * @param methodName The name of the method to call.
697      * @param value The value to pass to the method.
698      */
setShort(int viewId, String methodName, short value)699     public void setShort(int viewId, String methodName, short value) {
700         addAction(new ReflectionAction(viewId, methodName, ReflectionAction.SHORT, value));
701     }
702 
703     /**
704      * Call a method taking one int on a view in the layout for this RemoteViews.
705      *
706      * @param viewId The id of the view whose text should change
707      * @param methodName The name of the method to call.
708      * @param value The value to pass to the method.
709      */
setInt(int viewId, String methodName, int value)710     public void setInt(int viewId, String methodName, int value) {
711         addAction(new ReflectionAction(viewId, methodName, ReflectionAction.INT, value));
712     }
713 
714     /**
715      * Call a method taking one long on a view in the layout for this RemoteViews.
716      *
717      * @param viewId The id of the view whose text should change
718      * @param methodName The name of the method to call.
719      * @param value The value to pass to the method.
720      */
setLong(int viewId, String methodName, long value)721     public void setLong(int viewId, String methodName, long value) {
722         addAction(new ReflectionAction(viewId, methodName, ReflectionAction.LONG, value));
723     }
724 
725     /**
726      * Call a method taking one float on a view in the layout for this RemoteViews.
727      *
728      * @param viewId The id of the view whose text should change
729      * @param methodName The name of the method to call.
730      * @param value The value to pass to the method.
731      */
setFloat(int viewId, String methodName, float value)732     public void setFloat(int viewId, String methodName, float value) {
733         addAction(new ReflectionAction(viewId, methodName, ReflectionAction.FLOAT, value));
734     }
735 
736     /**
737      * Call a method taking one double on a view in the layout for this RemoteViews.
738      *
739      * @param viewId The id of the view whose text should change
740      * @param methodName The name of the method to call.
741      * @param value The value to pass to the method.
742      */
setDouble(int viewId, String methodName, double value)743     public void setDouble(int viewId, String methodName, double value) {
744         addAction(new ReflectionAction(viewId, methodName, ReflectionAction.DOUBLE, value));
745     }
746 
747     /**
748      * Call a method taking one char on a view in the layout for this RemoteViews.
749      *
750      * @param viewId The id of the view whose text should change
751      * @param methodName The name of the method to call.
752      * @param value The value to pass to the method.
753      */
setChar(int viewId, String methodName, char value)754     public void setChar(int viewId, String methodName, char value) {
755         addAction(new ReflectionAction(viewId, methodName, ReflectionAction.CHAR, value));
756     }
757 
758     /**
759      * Call a method taking one String on a view in the layout for this RemoteViews.
760      *
761      * @param viewId The id of the view whose text should change
762      * @param methodName The name of the method to call.
763      * @param value The value to pass to the method.
764      */
setString(int viewId, String methodName, String value)765     public void setString(int viewId, String methodName, String value) {
766         addAction(new ReflectionAction(viewId, methodName, ReflectionAction.STRING, value));
767     }
768 
769     /**
770      * Call a method taking one CharSequence on a view in the layout for this RemoteViews.
771      *
772      * @param viewId The id of the view whose text should change
773      * @param methodName The name of the method to call.
774      * @param value The value to pass to the method.
775      */
setCharSequence(int viewId, String methodName, CharSequence value)776     public void setCharSequence(int viewId, String methodName, CharSequence value) {
777         addAction(new ReflectionAction(viewId, methodName, ReflectionAction.CHAR_SEQUENCE, value));
778     }
779 
780     /**
781      * Call a method taking one Uri on a view in the layout for this RemoteViews.
782      *
783      * @param viewId The id of the view whose text should change
784      * @param methodName The name of the method to call.
785      * @param value The value to pass to the method.
786      */
setUri(int viewId, String methodName, Uri value)787     public void setUri(int viewId, String methodName, Uri value) {
788         addAction(new ReflectionAction(viewId, methodName, ReflectionAction.URI, value));
789     }
790 
791     /**
792      * Call a method taking one Bitmap on a view in the layout for this RemoteViews.
793      * @more
794      * <p class="note">The bitmap will be flattened into the parcel if this object is
795      * sent across processes, so it may end up using a lot of memory, and may be fairly slow.</p>
796      *
797      * @param viewId The id of the view whose text should change
798      * @param methodName The name of the method to call.
799      * @param value The value to pass to the method.
800      */
setBitmap(int viewId, String methodName, Bitmap value)801     public void setBitmap(int viewId, String methodName, Bitmap value) {
802         addAction(new ReflectionAction(viewId, methodName, ReflectionAction.BITMAP, value));
803     }
804 
805     /**
806      * Inflates the view hierarchy represented by this object and applies
807      * all of the actions.
808      *
809      * <p><strong>Caller beware: this may throw</strong>
810      *
811      * @param context Default context to use
812      * @param parent Parent that the resulting view hierarchy will be attached to. This method
813      * does <strong>not</strong> attach the hierarchy. The caller should do so when appropriate.
814      * @return The inflated view hierarchy
815      */
apply(Context context, ViewGroup parent)816     public View apply(Context context, ViewGroup parent) {
817         View result;
818 
819         Context c = prepareContext(context);
820 
821         LayoutInflater inflater = (LayoutInflater)
822                 c.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
823 
824         inflater = inflater.cloneInContext(c);
825         inflater.setFilter(this);
826 
827         result = inflater.inflate(mLayoutId, parent, false);
828 
829         performApply(result);
830 
831         return result;
832     }
833 
834     /**
835      * Applies all of the actions to the provided view.
836      *
837      * <p><strong>Caller beware: this may throw</strong>
838      *
839      * @param v The view to apply the actions to.  This should be the result of
840      * the {@link #apply(Context,ViewGroup)} call.
841      */
reapply(Context context, View v)842     public void reapply(Context context, View v) {
843         prepareContext(context);
844         performApply(v);
845     }
846 
performApply(View v)847     private void performApply(View v) {
848         if (mActions != null) {
849             final int count = mActions.size();
850             for (int i = 0; i < count; i++) {
851                 Action a = mActions.get(i);
852                 a.apply(v);
853             }
854         }
855     }
856 
prepareContext(Context context)857     private Context prepareContext(Context context) {
858         Context c;
859         String packageName = mPackage;
860 
861         if (packageName != null) {
862             try {
863                 c = context.createPackageContext(packageName, Context.CONTEXT_RESTRICTED);
864             } catch (NameNotFoundException e) {
865                 Log.e(LOG_TAG, "Package name " + packageName + " not found");
866                 c = context;
867             }
868         } else {
869             c = context;
870         }
871 
872         return c;
873     }
874 
875     /* (non-Javadoc)
876      * Used to restrict the views which can be inflated
877      *
878      * @see android.view.LayoutInflater.Filter#onLoadClass(java.lang.Class)
879      */
onLoadClass(Class clazz)880     public boolean onLoadClass(Class clazz) {
881         return clazz.isAnnotationPresent(RemoteView.class);
882     }
883 
describeContents()884     public int describeContents() {
885         return 0;
886     }
887 
writeToParcel(Parcel dest, int flags)888     public void writeToParcel(Parcel dest, int flags) {
889         dest.writeString(mPackage);
890         dest.writeInt(mLayoutId);
891         int count;
892         if (mActions != null) {
893             count = mActions.size();
894         } else {
895             count = 0;
896         }
897         dest.writeInt(count);
898         for (int i=0; i<count; i++) {
899             Action a = mActions.get(i);
900             a.writeToParcel(dest, 0);
901         }
902     }
903 
904     /**
905      * Parcelable.Creator that instantiates RemoteViews objects
906      */
907     public static final Parcelable.Creator<RemoteViews> CREATOR = new Parcelable.Creator<RemoteViews>() {
908         public RemoteViews createFromParcel(Parcel parcel) {
909             return new RemoteViews(parcel);
910         }
911 
912         public RemoteViews[] newArray(int size) {
913             return new RemoteViews[size];
914         }
915     };
916 }
917