OSDN Git Service

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