blob: c854fac08626050faa7703f493c3cd6d50661b47 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
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
17package android.widget;
18
Adam Cohenca6fd842010-09-03 18:10:35 -070019import java.lang.annotation.ElementType;
20import java.lang.annotation.Retention;
21import java.lang.annotation.RetentionPolicy;
22import java.lang.annotation.Target;
23import java.lang.reflect.Method;
24import java.util.ArrayList;
25
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080026import android.app.PendingIntent;
Adam Cohen1480fdd2010-08-25 17:24:53 -070027import android.appwidget.AppWidgetHostView;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080028import android.content.Context;
Dianne Hackbornfa82f222009-09-17 15:14:12 -070029import android.content.Intent;
30import android.content.IntentSender;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080031import android.content.pm.PackageManager.NameNotFoundException;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080032import android.graphics.Bitmap;
33import android.graphics.PorterDuff;
Joe Onorato75970652009-12-02 23:04:55 -080034import android.graphics.Rect;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080035import android.graphics.drawable.Drawable;
36import android.net.Uri;
Bjorn Bringertd755b062010-01-06 17:15:37 +000037import android.os.Bundle;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080038import android.os.Parcel;
39import android.os.Parcelable;
40import android.text.TextUtils;
41import android.util.Log;
42import android.view.LayoutInflater;
43import android.view.RemotableViewMethod;
44import android.view.View;
45import android.view.ViewGroup;
46import android.view.LayoutInflater.Filter;
47import android.view.View.OnClickListener;
Adam Cohena32edd42010-10-26 10:35:01 -070048import android.widget.AdapterView.OnItemClickListener;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080049
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080050
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 */
57public class RemoteViews implements Parcelable, Filter {
58
59 private static final String LOG_TAG = "RemoteViews";
60
61 /**
Winson Chung81f39eb2011-01-11 18:05:01 -080062 * The intent extra that contains the appWidgetId.
63 * @hide
64 */
65 static final String EXTRA_REMOTEADAPTER_APPWIDGET_ID = "remoteAdapterAppWidgetId";
66
67 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080068 * The package name of the package containing the layout
69 * resource. (Added to the parcel)
70 */
Gilles Debunne30301932010-06-16 18:32:00 -070071 private final String mPackage;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080072
73 /**
74 * The resource ID of the layout file. (Added to the parcel)
75 */
Gilles Debunne30301932010-06-16 18:32:00 -070076 private final int mLayoutId;
Romain Guya5475592009-07-01 17:20:08 -070077
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080078 /**
79 * An array of actions to perform on the view tree once it has been
80 * inflated
81 */
82 private ArrayList<Action> mActions;
83
Winson Chung3ec9a452010-09-23 16:40:28 -070084 /**
85 * A class to keep track of memory usage by this RemoteViews
86 */
87 private MemoryUsageCounter mMemoryUsageCounter;
88
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080089
90 /**
Adam Cohenca6fd842010-09-03 18:10:35 -070091 * 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 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800100 * This annotation indicates that a subclass of View is alllowed to be used
Romain Guya5475592009-07-01 17:20:08 -0700101 * with the {@link RemoteViews} mechanism.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800102 */
103 @Target({ ElementType.TYPE })
104 @Retention(RetentionPolicy.RUNTIME)
105 public @interface RemoteView {
106 }
107
108 /**
109 * Exception to send when something goes wrong executing an action
110 *
111 */
112 public static class ActionException extends RuntimeException {
113 public ActionException(Exception ex) {
114 super(ex);
115 }
116 public ActionException(String message) {
117 super(message);
118 }
119 }
Adam Cohena32edd42010-10-26 10:35:01 -0700120
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800121 /**
122 * Base class for all actions that can be performed on an
123 * inflated view.
124 *
Joe Onorato18e69df2010-05-17 22:26:12 -0700125 * SUBCLASSES MUST BE IMMUTABLE SO CLONE WORKS!!!!!
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800126 */
127 private abstract static class Action implements Parcelable {
128 public abstract void apply(View root) throws ActionException;
129
130 public int describeContents() {
131 return 0;
132 }
Winson Chung3ec9a452010-09-23 16:40:28 -0700133
134 /**
135 * Overridden by each class to report on it's own memory usage
136 */
137 public void updateMemoryUsageEstimate(MemoryUsageCounter counter) {
138 // We currently only calculate Bitmap memory usage, so by default, don't do anything
139 // here
140 return;
141 }
Winson Chungae615982010-11-01 12:14:49 -0700142
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 }
Romain Guya5475592009-07-01 17:20:08 -0700161 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800162
Adam Cohen1480fdd2010-08-25 17:24:53 -0700163 private class SetEmptyView extends Action {
164 int viewId;
165 int emptyViewId;
166
167 public final static int TAG = 6;
168
169 SetEmptyView(int viewId, int emptyViewId) {
170 this.viewId = viewId;
171 this.emptyViewId = emptyViewId;
172 }
173
174 SetEmptyView(Parcel in) {
175 this.viewId = in.readInt();
176 this.emptyViewId = in.readInt();
177 }
178
179 public void writeToParcel(Parcel out, int flags) {
180 out.writeInt(TAG);
181 out.writeInt(this.viewId);
182 out.writeInt(this.emptyViewId);
183 }
184
185 @Override
186 public void apply(View root) {
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
Adam Cohen35ae9ca2010-09-20 15:20:41 -0700199 private class SetOnClickFillInIntent extends Action {
200 public SetOnClickFillInIntent(int id, Intent fillInIntent) {
201 this.viewId = id;
202 this.fillInIntent = fillInIntent;
203 }
204
205 public SetOnClickFillInIntent(Parcel parcel) {
206 viewId = parcel.readInt();
207 fillInIntent = Intent.CREATOR.createFromParcel(parcel);
208 }
209
210 public void writeToParcel(Parcel dest, int flags) {
211 dest.writeInt(TAG);
212 dest.writeInt(viewId);
213 fillInIntent.writeToParcel(dest, 0 /* no flags */);
214 }
215
216 @Override
217 public void apply(View root) {
218 final View target = root.findViewById(viewId);
Joe Onorato2b69ce42010-10-31 11:35:41 -0700219 if (target == null) return;
Adam Cohen35ae9ca2010-09-20 15:20:41 -0700220
221 if (!mIsWidgetCollectionChild) {
222 Log.e("RemoteViews", "The method setOnClickFillInIntent is available " +
223 "only from RemoteViewsFactory (ie. on collection items).");
224 return;
225 }
Adam Cohena32edd42010-10-26 10:35:01 -0700226 if (target == root) {
227 target.setTagInternal(com.android.internal.R.id.fillInIntent, fillInIntent);
228 } else if (target != null && fillInIntent != null) {
Adam Cohen35ae9ca2010-09-20 15:20:41 -0700229 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" +
Adam Cohena32edd42010-10-26 10:35:01 -0700248 " calling setPendingIntentTemplate on parent.");
Adam Cohen35ae9ca2010-09-20 15:20:41 -0700249 return;
250 }
251
252 PendingIntent pendingIntent = (PendingIntent) parent.getTag();
253
254 final float appScale = v.getContext().getResources()
Adam Cohena32edd42010-10-26 10:35:01 -0700255 .getCompatibilityInfo().applicationScale;
Adam Cohen35ae9ca2010-09-20 15:20:41 -0700256 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);
Winson Chungae615982010-11-01 12:14:49 -0700266 startIntentSafely(v.getContext(), pendingIntent, fillInIntent);
Adam Cohen35ae9ca2010-09-20 15:20:41 -0700267 }
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
Adam Cohenca6fd842010-09-03 18:10:35 -0700280 private class SetPendingIntentTemplate extends Action {
281 public SetPendingIntentTemplate(int id, PendingIntent pendingIntentTemplate) {
282 this.viewId = id;
283 this.pendingIntentTemplate = pendingIntentTemplate;
284 }
285
286 public SetPendingIntentTemplate(Parcel parcel) {
287 viewId = parcel.readInt();
288 pendingIntentTemplate = PendingIntent.readPendingIntentOrNullFromParcel(parcel);
289 }
290
291 public void writeToParcel(Parcel dest, int flags) {
292 dest.writeInt(TAG);
293 dest.writeInt(viewId);
294 pendingIntentTemplate.writeToParcel(dest, 0 /* no flags */);
295 }
296
297 @Override
298 public void apply(View root) {
299 final View target = root.findViewById(viewId);
Joe Onorato2b69ce42010-10-31 11:35:41 -0700300 if (target == null) return;
Adam Cohenca6fd842010-09-03 18:10:35 -0700301
302 // If the view isn't an AdapterView, setting a PendingIntent template doesn't make sense
303 if (target instanceof AdapterView<?>) {
Adam Cohena32edd42010-10-26 10:35:01 -0700304 AdapterView<?> av = (AdapterView<?>) target;
Adam Cohenca6fd842010-09-03 18:10:35 -0700305 // The PendingIntent template is stored in the view's tag.
Adam Cohena32edd42010-10-26 10:35:01 -0700306 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);
Adam Cohenca6fd842010-09-03 18:10:35 -0700350 } else {
351 Log.e("RemoteViews", "Cannot setPendingIntentTemplate on a view which is not" +
Adam Cohen35ae9ca2010-09-20 15:20:41 -0700352 "an AdapterView (id: " + viewId + ")");
Adam Cohenca6fd842010-09-03 18:10:35 -0700353 return;
354 }
355 }
356
357 int viewId;
358 PendingIntent pendingIntentTemplate;
359
360 public final static int TAG = 8;
361 }
362
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800363 /**
364 * Equivalent to calling
365 * {@link android.view.View#setOnClickListener(android.view.View.OnClickListener)}
366 * to launch the provided {@link PendingIntent}.
367 */
368 private class SetOnClickPendingIntent extends Action {
369 public SetOnClickPendingIntent(int id, PendingIntent pendingIntent) {
370 this.viewId = id;
371 this.pendingIntent = pendingIntent;
372 }
Adam Cohenca6fd842010-09-03 18:10:35 -0700373
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800374 public SetOnClickPendingIntent(Parcel parcel) {
375 viewId = parcel.readInt();
376 pendingIntent = PendingIntent.readPendingIntentOrNullFromParcel(parcel);
377 }
Adam Cohenca6fd842010-09-03 18:10:35 -0700378
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800379 public void writeToParcel(Parcel dest, int flags) {
380 dest.writeInt(TAG);
381 dest.writeInt(viewId);
382 pendingIntent.writeToParcel(dest, 0 /* no flags */);
383 }
Adam Cohenca6fd842010-09-03 18:10:35 -0700384
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800385 @Override
386 public void apply(View root) {
387 final View target = root.findViewById(viewId);
Joe Onorato2b69ce42010-10-31 11:35:41 -0700388 if (target == null) return;
Adam Cohenca6fd842010-09-03 18:10:35 -0700389
390 // If the view is an AdapterView, setting a PendingIntent on click doesn't make much
391 // sense, do they mean to set a PendingIntent template for the AdapterView's children?
392 if (mIsWidgetCollectionChild) {
Adam Cohen35ae9ca2010-09-20 15:20:41 -0700393 Log.e("RemoteViews", "Cannot setOnClickPendingIntent for collection item " +
394 "(id: " + viewId + ")");
Adam Cohenca6fd842010-09-03 18:10:35 -0700395 // TODO: return; We'll let this slide until apps are up to date.
396 }
397
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800398 if (target != null && pendingIntent != null) {
399 OnClickListener listener = new OnClickListener() {
400 public void onClick(View v) {
Jeff Sharkeya503f6f2009-12-08 21:50:14 -0800401 // Find target view location in screen coordinates and
402 // fill into PendingIntent before sending.
403 final float appScale = v.getContext().getResources()
404 .getCompatibilityInfo().applicationScale;
405 final int[] pos = new int[2];
Joe Onorato75970652009-12-02 23:04:55 -0800406 v.getLocationOnScreen(pos);
Jeff Sharkeya503f6f2009-12-08 21:50:14 -0800407
408 final Rect rect = new Rect();
409 rect.left = (int) (pos[0] * appScale + 0.5f);
410 rect.top = (int) (pos[1] * appScale + 0.5f);
411 rect.right = (int) ((pos[0] + v.getWidth()) * appScale + 0.5f);
412 rect.bottom = (int) ((pos[1] + v.getHeight()) * appScale + 0.5f);
413
414 final Intent intent = new Intent();
415 intent.setSourceBounds(rect);
Winson Chungae615982010-11-01 12:14:49 -0700416 startIntentSafely(v.getContext(), pendingIntent, intent);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800417 }
418 };
419 target.setOnClickListener(listener);
420 }
421 }
422
423 int viewId;
424 PendingIntent pendingIntent;
425
426 public final static int TAG = 1;
427 }
428
429 /**
430 * Equivalent to calling a combination of {@link Drawable#setAlpha(int)},
431 * {@link Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)},
432 * and/or {@link Drawable#setLevel(int)} on the {@link Drawable} of a given view.
433 * <p>
434 * These operations will be performed on the {@link Drawable} returned by the
435 * target {@link View#getBackground()} by default. If targetBackground is false,
436 * we assume the target is an {@link ImageView} and try applying the operations
437 * to {@link ImageView#getDrawable()}.
438 * <p>
439 * You can omit specific calls by marking their values with null or -1.
440 */
441 private class SetDrawableParameters extends Action {
442 public SetDrawableParameters(int id, boolean targetBackground, int alpha,
443 int colorFilter, PorterDuff.Mode mode, int level) {
444 this.viewId = id;
445 this.targetBackground = targetBackground;
446 this.alpha = alpha;
447 this.colorFilter = colorFilter;
448 this.filterMode = mode;
449 this.level = level;
450 }
451
452 public SetDrawableParameters(Parcel parcel) {
453 viewId = parcel.readInt();
454 targetBackground = parcel.readInt() != 0;
455 alpha = parcel.readInt();
456 colorFilter = parcel.readInt();
457 boolean hasMode = parcel.readInt() != 0;
458 if (hasMode) {
459 filterMode = PorterDuff.Mode.valueOf(parcel.readString());
460 } else {
461 filterMode = null;
462 }
463 level = parcel.readInt();
464 }
465
466 public void writeToParcel(Parcel dest, int flags) {
467 dest.writeInt(TAG);
468 dest.writeInt(viewId);
469 dest.writeInt(targetBackground ? 1 : 0);
470 dest.writeInt(alpha);
471 dest.writeInt(colorFilter);
472 if (filterMode != null) {
473 dest.writeInt(1);
474 dest.writeString(filterMode.toString());
475 } else {
476 dest.writeInt(0);
477 }
478 dest.writeInt(level);
479 }
480
481 @Override
482 public void apply(View root) {
483 final View target = root.findViewById(viewId);
Joe Onorato2b69ce42010-10-31 11:35:41 -0700484 if (target == null) return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800485
486 // Pick the correct drawable to modify for this view
487 Drawable targetDrawable = null;
488 if (targetBackground) {
489 targetDrawable = target.getBackground();
490 } else if (target instanceof ImageView) {
491 ImageView imageView = (ImageView) target;
492 targetDrawable = imageView.getDrawable();
493 }
494
Romain Guya5475592009-07-01 17:20:08 -0700495 if (targetDrawable != null) {
496 // Perform modifications only if values are set correctly
497 if (alpha != -1) {
498 targetDrawable.setAlpha(alpha);
499 }
500 if (colorFilter != -1 && filterMode != null) {
501 targetDrawable.setColorFilter(colorFilter, filterMode);
502 }
503 if (level != -1) {
504 targetDrawable.setLevel(level);
505 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800506 }
507 }
Winson Chung3ec9a452010-09-23 16:40:28 -0700508
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800509 int viewId;
510 boolean targetBackground;
511 int alpha;
512 int colorFilter;
513 PorterDuff.Mode filterMode;
514 int level;
515
516 public final static int TAG = 3;
517 }
518
Adam Cohen2dd21972010-08-15 18:20:04 -0700519 private class ReflectionActionWithoutParams extends Action {
520 int viewId;
521 String methodName;
522
523 public final static int TAG = 5;
524
525 ReflectionActionWithoutParams(int viewId, String methodName) {
526 this.viewId = viewId;
527 this.methodName = methodName;
528 }
529
530 ReflectionActionWithoutParams(Parcel in) {
531 this.viewId = in.readInt();
532 this.methodName = in.readString();
533 }
534
535 public void writeToParcel(Parcel out, int flags) {
536 out.writeInt(TAG);
537 out.writeInt(this.viewId);
538 out.writeString(this.methodName);
539 }
540
541 @Override
542 public void apply(View root) {
543 final View view = root.findViewById(viewId);
Joe Onorato2b69ce42010-10-31 11:35:41 -0700544 if (view == null) return;
Adam Cohen2dd21972010-08-15 18:20:04 -0700545
546 Class klass = view.getClass();
547 Method method;
548 try {
549 method = klass.getMethod(this.methodName);
550 } catch (NoSuchMethodException ex) {
551 throw new ActionException("view: " + klass.getName() + " doesn't have method: "
552 + this.methodName + "()");
553 }
554
555 if (!method.isAnnotationPresent(RemotableViewMethod.class)) {
556 throw new ActionException("view: " + klass.getName()
557 + " can't use method with RemoteViews: "
558 + this.methodName + "()");
559 }
560
561 try {
562 //noinspection ConstantIfStatement
563 if (false) {
564 Log.d("RemoteViews", "view: " + klass.getName() + " calling method: "
565 + this.methodName + "()");
566 }
567 method.invoke(view);
568 } catch (Exception ex) {
569 throw new ActionException(ex);
570 }
571 }
572 }
573
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800574 /**
575 * Base class for the reflection actions.
576 */
577 private class ReflectionAction extends Action {
578 static final int TAG = 2;
579
580 static final int BOOLEAN = 1;
581 static final int BYTE = 2;
582 static final int SHORT = 3;
583 static final int INT = 4;
584 static final int LONG = 5;
585 static final int FLOAT = 6;
586 static final int DOUBLE = 7;
587 static final int CHAR = 8;
588 static final int STRING = 9;
589 static final int CHAR_SEQUENCE = 10;
590 static final int URI = 11;
591 static final int BITMAP = 12;
Bjorn Bringertd755b062010-01-06 17:15:37 +0000592 static final int BUNDLE = 13;
Winson Chung499cb9f2010-07-16 11:18:17 -0700593 static final int INTENT = 14;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800594
595 int viewId;
596 String methodName;
597 int type;
598 Object value;
599
600 ReflectionAction(int viewId, String methodName, int type, Object value) {
601 this.viewId = viewId;
602 this.methodName = methodName;
603 this.type = type;
604 this.value = value;
605 }
606
607 ReflectionAction(Parcel in) {
608 this.viewId = in.readInt();
609 this.methodName = in.readString();
610 this.type = in.readInt();
Romain Guya5475592009-07-01 17:20:08 -0700611 //noinspection ConstantIfStatement
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800612 if (false) {
613 Log.d("RemoteViews", "read viewId=0x" + Integer.toHexString(this.viewId)
614 + " methodName=" + this.methodName + " type=" + this.type);
615 }
616 switch (this.type) {
617 case BOOLEAN:
618 this.value = in.readInt() != 0;
619 break;
620 case BYTE:
621 this.value = in.readByte();
622 break;
623 case SHORT:
624 this.value = (short)in.readInt();
625 break;
626 case INT:
627 this.value = in.readInt();
628 break;
629 case LONG:
630 this.value = in.readLong();
631 break;
632 case FLOAT:
633 this.value = in.readFloat();
634 break;
635 case DOUBLE:
636 this.value = in.readDouble();
637 break;
638 case CHAR:
639 this.value = (char)in.readInt();
640 break;
641 case STRING:
642 this.value = in.readString();
643 break;
644 case CHAR_SEQUENCE:
645 this.value = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
646 break;
647 case URI:
648 this.value = Uri.CREATOR.createFromParcel(in);
649 break;
650 case BITMAP:
651 this.value = Bitmap.CREATOR.createFromParcel(in);
652 break;
Bjorn Bringertd755b062010-01-06 17:15:37 +0000653 case BUNDLE:
654 this.value = in.readBundle();
655 break;
Winson Chung499cb9f2010-07-16 11:18:17 -0700656 case INTENT:
657 this.value = Intent.CREATOR.createFromParcel(in);
658 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800659 default:
660 break;
661 }
662 }
663
664 public void writeToParcel(Parcel out, int flags) {
665 out.writeInt(TAG);
666 out.writeInt(this.viewId);
667 out.writeString(this.methodName);
668 out.writeInt(this.type);
Romain Guya5475592009-07-01 17:20:08 -0700669 //noinspection ConstantIfStatement
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800670 if (false) {
671 Log.d("RemoteViews", "write viewId=0x" + Integer.toHexString(this.viewId)
672 + " methodName=" + this.methodName + " type=" + this.type);
673 }
674 switch (this.type) {
675 case BOOLEAN:
Romain Guya5475592009-07-01 17:20:08 -0700676 out.writeInt((Boolean) this.value ? 1 : 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800677 break;
678 case BYTE:
Romain Guya5475592009-07-01 17:20:08 -0700679 out.writeByte((Byte) this.value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800680 break;
681 case SHORT:
Romain Guya5475592009-07-01 17:20:08 -0700682 out.writeInt((Short) this.value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800683 break;
684 case INT:
Romain Guya5475592009-07-01 17:20:08 -0700685 out.writeInt((Integer) this.value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800686 break;
687 case LONG:
Romain Guya5475592009-07-01 17:20:08 -0700688 out.writeLong((Long) this.value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800689 break;
690 case FLOAT:
Romain Guya5475592009-07-01 17:20:08 -0700691 out.writeFloat((Float) this.value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800692 break;
693 case DOUBLE:
Romain Guya5475592009-07-01 17:20:08 -0700694 out.writeDouble((Double) this.value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800695 break;
696 case CHAR:
697 out.writeInt((int)((Character)this.value).charValue());
698 break;
699 case STRING:
700 out.writeString((String)this.value);
701 break;
702 case CHAR_SEQUENCE:
703 TextUtils.writeToParcel((CharSequence)this.value, out, flags);
704 break;
705 case URI:
706 ((Uri)this.value).writeToParcel(out, flags);
707 break;
708 case BITMAP:
709 ((Bitmap)this.value).writeToParcel(out, flags);
710 break;
Bjorn Bringertd755b062010-01-06 17:15:37 +0000711 case BUNDLE:
712 out.writeBundle((Bundle) this.value);
713 break;
Winson Chung499cb9f2010-07-16 11:18:17 -0700714 case INTENT:
715 ((Intent)this.value).writeToParcel(out, flags);
716 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800717 default:
718 break;
719 }
720 }
721
722 private Class getParameterType() {
723 switch (this.type) {
724 case BOOLEAN:
725 return boolean.class;
726 case BYTE:
727 return byte.class;
728 case SHORT:
729 return short.class;
730 case INT:
731 return int.class;
732 case LONG:
733 return long.class;
734 case FLOAT:
735 return float.class;
736 case DOUBLE:
737 return double.class;
738 case CHAR:
739 return char.class;
740 case STRING:
741 return String.class;
742 case CHAR_SEQUENCE:
743 return CharSequence.class;
744 case URI:
745 return Uri.class;
746 case BITMAP:
747 return Bitmap.class;
Bjorn Bringertd755b062010-01-06 17:15:37 +0000748 case BUNDLE:
749 return Bundle.class;
Winson Chung499cb9f2010-07-16 11:18:17 -0700750 case INTENT:
751 return Intent.class;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800752 default:
753 return null;
754 }
755 }
756
757 @Override
758 public void apply(View root) {
759 final View view = root.findViewById(viewId);
Joe Onorato2b69ce42010-10-31 11:35:41 -0700760 if (view == null) return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800761
762 Class param = getParameterType();
763 if (param == null) {
764 throw new ActionException("bad type: " + this.type);
765 }
766
767 Class klass = view.getClass();
Romain Guya5475592009-07-01 17:20:08 -0700768 Method method;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800769 try {
770 method = klass.getMethod(this.methodName, getParameterType());
771 }
772 catch (NoSuchMethodException ex) {
773 throw new ActionException("view: " + klass.getName() + " doesn't have method: "
774 + this.methodName + "(" + param.getName() + ")");
775 }
776
777 if (!method.isAnnotationPresent(RemotableViewMethod.class)) {
778 throw new ActionException("view: " + klass.getName()
779 + " can't use method with RemoteViews: "
780 + this.methodName + "(" + param.getName() + ")");
781 }
782
783 try {
Romain Guya5475592009-07-01 17:20:08 -0700784 //noinspection ConstantIfStatement
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800785 if (false) {
786 Log.d("RemoteViews", "view: " + klass.getName() + " calling method: "
787 + this.methodName + "(" + param.getName() + ") with "
788 + (this.value == null ? "null" : this.value.getClass().getName()));
789 }
790 method.invoke(view, this.value);
791 }
792 catch (Exception ex) {
793 throw new ActionException(ex);
794 }
795 }
Winson Chung3ec9a452010-09-23 16:40:28 -0700796
797 @Override
798 public void updateMemoryUsageEstimate(MemoryUsageCounter counter) {
799 // We currently only calculate Bitmap memory usage
800 switch (this.type) {
801 case BITMAP:
802 if (this.value != null) {
803 final Bitmap b = (Bitmap) this.value;
804 final Bitmap.Config c = b.getConfig();
Joe Onoratof01345e2011-01-17 17:20:09 -0800805 // If we don't know, be pessimistic and assume 4
Winson Chung3ec9a452010-09-23 16:40:28 -0700806 int bpp = 4;
Joe Onoratof01345e2011-01-17 17:20:09 -0800807 if (c != null) {
808 switch (c) {
809 case ALPHA_8:
810 bpp = 1;
811 break;
812 case RGB_565:
813 case ARGB_4444:
814 bpp = 2;
815 break;
816 case ARGB_8888:
817 bpp = 4;
818 break;
819 }
Winson Chung3ec9a452010-09-23 16:40:28 -0700820 }
821 counter.bitmapIncrement(b.getWidth() * b.getHeight() * bpp);
822 }
823 break;
824 default:
825 break;
826 }
827 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800828 }
829
Jeff Sharkey1162fd72009-11-04 17:58:08 -0800830 /**
831 * Equivalent to calling {@link ViewGroup#addView(View)} after inflating the
832 * given {@link RemoteViews}, or calling {@link ViewGroup#removeAllViews()}
833 * when null. This allows users to build "nested" {@link RemoteViews}.
834 */
835 private class ViewGroupAction extends Action {
836 public ViewGroupAction(int viewId, RemoteViews nestedViews) {
837 this.viewId = viewId;
838 this.nestedViews = nestedViews;
839 }
840
841 public ViewGroupAction(Parcel parcel) {
842 viewId = parcel.readInt();
843 nestedViews = parcel.readParcelable(null);
844 }
845
846 public void writeToParcel(Parcel dest, int flags) {
847 dest.writeInt(TAG);
848 dest.writeInt(viewId);
849 dest.writeParcelable(nestedViews, 0 /* no flags */);
850 }
851
852 @Override
853 public void apply(View root) {
854 final Context context = root.getContext();
855 final ViewGroup target = (ViewGroup) root.findViewById(viewId);
Joe Onorato2b69ce42010-10-31 11:35:41 -0700856 if (target == null) return;
Jeff Sharkey1162fd72009-11-04 17:58:08 -0800857 if (nestedViews != null) {
858 // Inflate nested views and add as children
859 target.addView(nestedViews.apply(context, target));
Joe Onorato2b69ce42010-10-31 11:35:41 -0700860 } else {
Jeff Sharkey1162fd72009-11-04 17:58:08 -0800861 // Clear all children when nested views omitted
862 target.removeAllViews();
863 }
864 }
865
Winson Chung3ec9a452010-09-23 16:40:28 -0700866 @Override
867 public void updateMemoryUsageEstimate(MemoryUsageCounter counter) {
868 if (nestedViews != null) {
869 counter.bitmapIncrement(nestedViews.estimateBitmapMemoryUsage());
870 }
871 }
872
Jeff Sharkey1162fd72009-11-04 17:58:08 -0800873 int viewId;
874 RemoteViews nestedViews;
875
876 public final static int TAG = 4;
877 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800878
879 /**
Winson Chung3ec9a452010-09-23 16:40:28 -0700880 * Simple class used to keep track of memory usage in a RemoteViews.
881 *
882 */
883 private class MemoryUsageCounter {
884 public void clear() {
885 mBitmapHeapMemoryUsage = 0;
886 }
887
888 public void bitmapIncrement(int numBytes) {
889 mBitmapHeapMemoryUsage += numBytes;
890 }
891
892 public int getBitmapHeapMemoryUsage() {
893 return mBitmapHeapMemoryUsage;
894 }
895
896 int mBitmapHeapMemoryUsage;
897 }
898
899 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800900 * Create a new RemoteViews object that will display the views contained
901 * in the specified layout file.
902 *
903 * @param packageName Name of the package that contains the layout resource
904 * @param layoutId The id of the layout resource
905 */
906 public RemoteViews(String packageName, int layoutId) {
907 mPackage = packageName;
908 mLayoutId = layoutId;
Winson Chung3ec9a452010-09-23 16:40:28 -0700909
910 // setup the memory usage statistics
911 mMemoryUsageCounter = new MemoryUsageCounter();
912 recalculateMemoryUsage();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800913 }
914
915 /**
916 * Reads a RemoteViews object from a parcel.
917 *
918 * @param parcel
919 */
920 public RemoteViews(Parcel parcel) {
921 mPackage = parcel.readString();
922 mLayoutId = parcel.readInt();
Adam Cohenca6fd842010-09-03 18:10:35 -0700923 mIsWidgetCollectionChild = parcel.readInt() == 1 ? true : false;
924
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800925 int count = parcel.readInt();
926 if (count > 0) {
927 mActions = new ArrayList<Action>(count);
928 for (int i=0; i<count; i++) {
929 int tag = parcel.readInt();
930 switch (tag) {
931 case SetOnClickPendingIntent.TAG:
932 mActions.add(new SetOnClickPendingIntent(parcel));
933 break;
934 case SetDrawableParameters.TAG:
935 mActions.add(new SetDrawableParameters(parcel));
936 break;
937 case ReflectionAction.TAG:
938 mActions.add(new ReflectionAction(parcel));
939 break;
Jeff Sharkey1162fd72009-11-04 17:58:08 -0800940 case ViewGroupAction.TAG:
941 mActions.add(new ViewGroupAction(parcel));
942 break;
Adam Cohen2dd21972010-08-15 18:20:04 -0700943 case ReflectionActionWithoutParams.TAG:
944 mActions.add(new ReflectionActionWithoutParams(parcel));
945 break;
Adam Cohen1480fdd2010-08-25 17:24:53 -0700946 case SetEmptyView.TAG:
947 mActions.add(new SetEmptyView(parcel));
948 break;
Adam Cohenca6fd842010-09-03 18:10:35 -0700949 case SetPendingIntentTemplate.TAG:
950 mActions.add(new SetPendingIntentTemplate(parcel));
951 break;
Adam Cohen35ae9ca2010-09-20 15:20:41 -0700952 case SetOnClickFillInIntent.TAG:
953 mActions.add(new SetOnClickFillInIntent(parcel));
954 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800955 default:
956 throw new ActionException("Tag " + tag + " not found");
957 }
958 }
959 }
Winson Chung3ec9a452010-09-23 16:40:28 -0700960
961 // setup the memory usage statistics
962 mMemoryUsageCounter = new MemoryUsageCounter();
963 recalculateMemoryUsage();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800964 }
965
Gilles Debunne30301932010-06-16 18:32:00 -0700966 @Override
Joe Onorato18e69df2010-05-17 22:26:12 -0700967 public RemoteViews clone() {
968 final RemoteViews that = new RemoteViews(mPackage, mLayoutId);
969 if (mActions != null) {
970 that.mActions = (ArrayList<Action>)mActions.clone();
971 }
Winson Chung3ec9a452010-09-23 16:40:28 -0700972
973 // update the memory usage stats of the cloned RemoteViews
974 that.recalculateMemoryUsage();
Joe Onorato18e69df2010-05-17 22:26:12 -0700975 return that;
976 }
977
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800978 public String getPackage() {
979 return mPackage;
980 }
981
982 public int getLayoutId() {
983 return mLayoutId;
984 }
985
Winson Chung3ec9a452010-09-23 16:40:28 -0700986 /*
Adam Cohenca6fd842010-09-03 18:10:35 -0700987 * This flag indicates whether this RemoteViews object is being created from a
988 * RemoteViewsService for use as a child of a widget collection. This flag is used
989 * to determine whether or not certain features are available, in particular,
990 * setting on click extras and setting on click pending intents. The former is enabled,
991 * and the latter disabled when this flag is true.
992 */
993 void setIsWidgetCollectionChild(boolean isWidgetCollectionChild) {
994 mIsWidgetCollectionChild = isWidgetCollectionChild;
995 }
996
997 /**
Winson Chung3ec9a452010-09-23 16:40:28 -0700998 * Updates the memory usage statistics.
999 */
1000 private void recalculateMemoryUsage() {
1001 mMemoryUsageCounter.clear();
1002
1003 // Accumulate the memory usage for each action
1004 if (mActions != null) {
1005 final int count = mActions.size();
1006 for (int i= 0; i < count; ++i) {
1007 mActions.get(i).updateMemoryUsageEstimate(mMemoryUsageCounter);
1008 }
1009 }
1010 }
1011
1012 /**
1013 * Returns an estimate of the bitmap heap memory usage for this RemoteViews.
1014 */
1015 int estimateBitmapMemoryUsage() {
1016 return mMemoryUsageCounter.getBitmapHeapMemoryUsage();
1017 }
1018
1019 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001020 * Add an action to be executed on the remote side when apply is called.
1021 *
1022 * @param a The action to add
1023 */
1024 private void addAction(Action a) {
1025 if (mActions == null) {
1026 mActions = new ArrayList<Action>();
1027 }
1028 mActions.add(a);
Winson Chung3ec9a452010-09-23 16:40:28 -07001029
1030 // update the memory usage stats
1031 a.updateMemoryUsageEstimate(mMemoryUsageCounter);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001032 }
Jeff Sharkey1162fd72009-11-04 17:58:08 -08001033
1034 /**
1035 * Equivalent to calling {@link ViewGroup#addView(View)} after inflating the
1036 * given {@link RemoteViews}. This allows users to build "nested"
1037 * {@link RemoteViews}. In cases where consumers of {@link RemoteViews} may
1038 * recycle layouts, use {@link #removeAllViews(int)} to clear any existing
1039 * children.
1040 *
1041 * @param viewId The id of the parent {@link ViewGroup} to add child into.
1042 * @param nestedView {@link RemoteViews} that describes the child.
1043 */
1044 public void addView(int viewId, RemoteViews nestedView) {
1045 addAction(new ViewGroupAction(viewId, nestedView));
1046 }
1047
1048 /**
1049 * Equivalent to calling {@link ViewGroup#removeAllViews()}.
1050 *
1051 * @param viewId The id of the parent {@link ViewGroup} to remove all
1052 * children from.
1053 */
1054 public void removeAllViews(int viewId) {
1055 addAction(new ViewGroupAction(viewId, null));
1056 }
1057
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001058 /**
Adam Cohen0b96a572011-02-10 15:56:16 -08001059 * Equivalent to calling {@link AdapterViewAnimator#showNext()}
Adam Cohen2dd21972010-08-15 18:20:04 -07001060 *
Adam Cohen0b96a572011-02-10 15:56:16 -08001061 * @param viewId The id of the view on which to call {@link AdapterViewAnimator#showNext()}
Adam Cohen2dd21972010-08-15 18:20:04 -07001062 */
1063 public void showNext(int viewId) {
1064 addAction(new ReflectionActionWithoutParams(viewId, "showNext"));
1065 }
1066
1067 /**
Adam Cohen0b96a572011-02-10 15:56:16 -08001068 * Equivalent to calling {@link AdapterViewAnimator#showPrevious()}
Adam Cohen2dd21972010-08-15 18:20:04 -07001069 *
Adam Cohen0b96a572011-02-10 15:56:16 -08001070 * @param viewId The id of the view on which to call {@link AdapterViewAnimator#showPrevious()}
Adam Cohen2dd21972010-08-15 18:20:04 -07001071 */
1072 public void showPrevious(int viewId) {
1073 addAction(new ReflectionActionWithoutParams(viewId, "showPrevious"));
1074 }
1075
1076 /**
Adam Cohen0b96a572011-02-10 15:56:16 -08001077 * Equivalent to calling {@link AdapterViewAnimator#setDisplayedChild(int)}
1078 *
1079 * @param viewId The id of the view on which to call
1080 * {@link AdapterViewAnimator#setDisplayedChild(int)}
1081 */
1082 public void setDisplayedChild(int viewId, int childIndex) {
1083 setInt(viewId, "setDisplayedChild", childIndex);
1084 }
1085
1086 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001087 * Equivalent to calling View.setVisibility
1088 *
1089 * @param viewId The id of the view whose visibility should change
1090 * @param visibility The new visibility for the view
1091 */
1092 public void setViewVisibility(int viewId, int visibility) {
1093 setInt(viewId, "setVisibility", visibility);
1094 }
Adam Cohenca6fd842010-09-03 18:10:35 -07001095
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001096 /**
1097 * Equivalent to calling TextView.setText
1098 *
1099 * @param viewId The id of the view whose text should change
1100 * @param text The new text for the view
1101 */
1102 public void setTextViewText(int viewId, CharSequence text) {
1103 setCharSequence(viewId, "setText", text);
1104 }
1105
1106 /**
1107 * Equivalent to calling ImageView.setImageResource
1108 *
1109 * @param viewId The id of the view whose drawable should change
1110 * @param srcId The new resource id for the drawable
1111 */
1112 public void setImageViewResource(int viewId, int srcId) {
1113 setInt(viewId, "setImageResource", srcId);
1114 }
1115
1116 /**
1117 * Equivalent to calling ImageView.setImageURI
1118 *
1119 * @param viewId The id of the view whose drawable should change
1120 * @param uri The Uri for the image
1121 */
1122 public void setImageViewUri(int viewId, Uri uri) {
1123 setUri(viewId, "setImageURI", uri);
1124 }
1125
1126 /**
1127 * Equivalent to calling ImageView.setImageBitmap
1128 *
1129 * @param viewId The id of the view whose drawable should change
1130 * @param bitmap The new Bitmap for the drawable
1131 */
1132 public void setImageViewBitmap(int viewId, Bitmap bitmap) {
1133 setBitmap(viewId, "setImageBitmap", bitmap);
1134 }
1135
1136 /**
Adam Cohen1480fdd2010-08-25 17:24:53 -07001137 * Equivalent to calling AdapterView.setEmptyView
1138 *
1139 * @param viewId The id of the view on which to set the empty view
1140 * @param emptyViewId The view id of the empty view
1141 */
1142 public void setEmptyView(int viewId, int emptyViewId) {
1143 addAction(new SetEmptyView(viewId, emptyViewId));
1144 }
1145
1146 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001147 * Equivalent to calling {@link Chronometer#setBase Chronometer.setBase},
1148 * {@link Chronometer#setFormat Chronometer.setFormat},
1149 * and {@link Chronometer#start Chronometer.start()} or
1150 * {@link Chronometer#stop Chronometer.stop()}.
1151 *
1152 * @param viewId The id of the view whose text should change
1153 * @param base The time at which the timer would have read 0:00. This
1154 * time should be based off of
1155 * {@link android.os.SystemClock#elapsedRealtime SystemClock.elapsedRealtime()}.
1156 * @param format The Chronometer format string, or null to
1157 * simply display the timer value.
1158 * @param started True if you want the clock to be started, false if not.
1159 */
1160 public void setChronometer(int viewId, long base, String format, boolean started) {
1161 setLong(viewId, "setBase", base);
1162 setString(viewId, "setFormat", format);
1163 setBoolean(viewId, "setStarted", started);
1164 }
1165
1166 /**
1167 * Equivalent to calling {@link ProgressBar#setMax ProgressBar.setMax},
1168 * {@link ProgressBar#setProgress ProgressBar.setProgress}, and
1169 * {@link ProgressBar#setIndeterminate ProgressBar.setIndeterminate}
1170 *
1171 * If indeterminate is true, then the values for max and progress are ignored.
1172 *
1173 * @param viewId The id of the view whose text should change
1174 * @param max The 100% value for the progress bar
1175 * @param progress The current value of the progress bar.
1176 * @param indeterminate True if the progress bar is indeterminate,
1177 * false if not.
1178 */
1179 public void setProgressBar(int viewId, int max, int progress,
1180 boolean indeterminate) {
1181 setBoolean(viewId, "setIndeterminate", indeterminate);
1182 if (!indeterminate) {
1183 setInt(viewId, "setMax", max);
1184 setInt(viewId, "setProgress", progress);
1185 }
1186 }
1187
1188 /**
1189 * Equivalent to calling
1190 * {@link android.view.View#setOnClickListener(android.view.View.OnClickListener)}
1191 * to launch the provided {@link PendingIntent}.
1192 *
Adam Cohen35ae9ca2010-09-20 15:20:41 -07001193 * When setting the on-click action of items within collections (eg. {@link ListView},
1194 * {@link StackView} etc.), this method will not work. Instead, use {@link
1195 * RemoteViews#setPendingIntentTemplate(int, PendingIntent) in conjunction with
1196 * RemoteViews#setOnClickFillInIntent(int, Intent).
1197 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001198 * @param viewId The id of the view that will trigger the {@link PendingIntent} when clicked
1199 * @param pendingIntent The {@link PendingIntent} to send when user clicks
1200 */
1201 public void setOnClickPendingIntent(int viewId, PendingIntent pendingIntent) {
1202 addAction(new SetOnClickPendingIntent(viewId, pendingIntent));
1203 }
1204
1205 /**
Adam Cohen35ae9ca2010-09-20 15:20:41 -07001206 * When using collections (eg. {@link ListView}, {@link StackView} etc.) in widgets, it is very
1207 * costly to set PendingIntents on the individual items, and is hence not permitted. Instead
1208 * this method should be used to set a single PendingIntent template on the collection, and
1209 * individual items can differentiate their on-click behavior using
1210 * {@link RemoteViews#setOnClickFillInIntent(int, Intent)}.
Adam Cohenca6fd842010-09-03 18:10:35 -07001211 *
1212 * @param viewId The id of the collection who's children will use this PendingIntent template
1213 * when clicked
1214 * @param pendingIntentTemplate The {@link PendingIntent} to be combined with extras specified
1215 * by a child of viewId and executed when that child is clicked
1216 */
1217 public void setPendingIntentTemplate(int viewId, PendingIntent pendingIntentTemplate) {
1218 addAction(new SetPendingIntentTemplate(viewId, pendingIntentTemplate));
1219 }
1220
1221 /**
Adam Cohen35ae9ca2010-09-20 15:20:41 -07001222 * When using collections (eg. {@link ListView}, {@link StackView} etc.) in widgets, it is very
1223 * costly to set PendingIntents on the individual items, and is hence not permitted. Instead
1224 * a single PendingIntent template can be set on the collection, see {@link
1225 * RemoteViews#setPendingIntentTemplate(int, PendingIntent)}, and the individual on-click
1226 * action of a given item can be distinguished by setting a fillInIntent on that item. The
1227 * fillInIntent is then combined with the PendingIntent template in order to determine the final
1228 * intent which will be executed when the item is clicked. This works as follows: any fields
1229 * which are left blank in the PendingIntent template, but are provided by the fillInIntent
1230 * will be overwritten, and the resulting PendingIntent will be used.
1231 *
1232 *
1233 * of the PendingIntent template will then be filled in with the associated fields that are
1234 * set in fillInIntent. See {@link Intent#fillIn(Intent, int)} for more details.
1235 *
1236 * @param viewId The id of the view on which to set the fillInIntent
1237 * @param fillInIntent The intent which will be combined with the parent's PendingIntent
1238 * in order to determine the on-click behavior of the view specified by viewId
1239 */
1240 public void setOnClickFillInIntent(int viewId, Intent fillInIntent) {
1241 addAction(new SetOnClickFillInIntent(viewId, fillInIntent));
1242 }
1243
1244 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001245 * @hide
1246 * Equivalent to calling a combination of {@link Drawable#setAlpha(int)},
1247 * {@link Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)},
1248 * and/or {@link Drawable#setLevel(int)} on the {@link Drawable} of a given
1249 * view.
1250 * <p>
1251 * You can omit specific calls by marking their values with null or -1.
1252 *
1253 * @param viewId The id of the view that contains the target
1254 * {@link Drawable}
1255 * @param targetBackground If true, apply these parameters to the
1256 * {@link Drawable} returned by
1257 * {@link android.view.View#getBackground()}. Otherwise, assume
1258 * the target view is an {@link ImageView} and apply them to
1259 * {@link ImageView#getDrawable()}.
1260 * @param alpha Specify an alpha value for the drawable, or -1 to leave
1261 * unchanged.
1262 * @param colorFilter Specify a color for a
1263 * {@link android.graphics.ColorFilter} for this drawable, or -1
1264 * to leave unchanged.
1265 * @param mode Specify a PorterDuff mode for this drawable, or null to leave
1266 * unchanged.
1267 * @param level Specify the level for the drawable, or -1 to leave
1268 * unchanged.
1269 */
1270 public void setDrawableParameters(int viewId, boolean targetBackground, int alpha,
1271 int colorFilter, PorterDuff.Mode mode, int level) {
1272 addAction(new SetDrawableParameters(viewId, targetBackground, alpha,
1273 colorFilter, mode, level));
1274 }
1275
1276 /**
1277 * Equivalent to calling {@link android.widget.TextView#setTextColor(int)}.
1278 *
1279 * @param viewId The id of the view whose text should change
1280 * @param color Sets the text color for all the states (normal, selected,
1281 * focused) to be this color.
1282 */
1283 public void setTextColor(int viewId, int color) {
1284 setInt(viewId, "setTextColor", color);
1285 }
1286
Joe Onorato592d0652009-03-24 22:25:52 -07001287 /**
Winson Chung499cb9f2010-07-16 11:18:17 -07001288 * Equivalent to calling {@link android.widget.AbsListView#setRemoteViewsAdapter(Intent)}.
1289 *
Winson Chung81f39eb2011-01-11 18:05:01 -08001290 * @param appWidgetId The id of the app widget which contains the specified view
1291 * @param viewId The id of the view whose text should change
1292 * @param intent The intent of the service which will be
1293 * providing data to the RemoteViewsAdapter
1294 */
1295 public void setRemoteAdapter(int appWidgetId, int viewId, Intent intent) {
1296 // Embed the AppWidget Id for use in RemoteViewsAdapter when connecting to the intent
1297 // RemoteViewsService
1298 intent.putExtra(EXTRA_REMOTEADAPTER_APPWIDGET_ID, appWidgetId);
Winson Chung499cb9f2010-07-16 11:18:17 -07001299 setIntent(viewId, "setRemoteViewsAdapter", intent);
1300 }
1301
1302 /**
1303 * Equivalent to calling {@link android.widget.AbsListView#smoothScrollToPosition(int, int)}.
1304 *
1305 * @param viewId The id of the view whose text should change
1306 * @param position Scroll to this adapter position
1307 */
1308 public void setScrollPosition(int viewId, int position) {
1309 setInt(viewId, "smoothScrollToPosition", position);
1310 }
1311
1312 /**
1313 * Equivalent to calling {@link android.widget.AbsListView#smoothScrollToPosition(int, int)}.
1314 *
1315 * @param viewId The id of the view whose text should change
Winson Chung95362592010-07-19 16:05:50 -07001316 * @param offset Scroll by this adapter position offset
Winson Chung499cb9f2010-07-16 11:18:17 -07001317 */
1318 public void setRelativeScrollPosition(int viewId, int offset) {
1319 setInt(viewId, "smoothScrollByOffset", offset);
1320 }
1321
1322 /**
Joe Onorato592d0652009-03-24 22:25:52 -07001323 * Call a method taking one boolean on a view in the layout for this RemoteViews.
1324 *
1325 * @param viewId The id of the view whose text should change
1326 * @param methodName The name of the method to call.
1327 * @param value The value to pass to the method.
1328 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001329 public void setBoolean(int viewId, String methodName, boolean value) {
1330 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.BOOLEAN, value));
1331 }
1332
Joe Onorato592d0652009-03-24 22:25:52 -07001333 /**
1334 * Call a method taking one byte on a view in the layout for this RemoteViews.
1335 *
1336 * @param viewId The id of the view whose text should change
1337 * @param methodName The name of the method to call.
1338 * @param value The value to pass to the method.
1339 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001340 public void setByte(int viewId, String methodName, byte value) {
1341 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.BYTE, value));
1342 }
1343
Joe Onorato592d0652009-03-24 22:25:52 -07001344 /**
1345 * Call a method taking one short on a view in the layout for this RemoteViews.
1346 *
1347 * @param viewId The id of the view whose text should change
1348 * @param methodName The name of the method to call.
1349 * @param value The value to pass to the method.
1350 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001351 public void setShort(int viewId, String methodName, short value) {
1352 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.SHORT, value));
1353 }
1354
Joe Onorato592d0652009-03-24 22:25:52 -07001355 /**
1356 * Call a method taking one int on a view in the layout for this RemoteViews.
1357 *
1358 * @param viewId The id of the view whose text should change
1359 * @param methodName The name of the method to call.
1360 * @param value The value to pass to the method.
1361 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001362 public void setInt(int viewId, String methodName, int value) {
1363 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.INT, value));
1364 }
1365
Joe Onorato592d0652009-03-24 22:25:52 -07001366 /**
1367 * Call a method taking one long on a view in the layout for this RemoteViews.
1368 *
1369 * @param viewId The id of the view whose text should change
1370 * @param methodName The name of the method to call.
1371 * @param value The value to pass to the method.
1372 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001373 public void setLong(int viewId, String methodName, long value) {
1374 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.LONG, value));
1375 }
1376
Joe Onorato592d0652009-03-24 22:25:52 -07001377 /**
1378 * Call a method taking one float on a view in the layout for this RemoteViews.
1379 *
1380 * @param viewId The id of the view whose text should change
1381 * @param methodName The name of the method to call.
1382 * @param value The value to pass to the method.
1383 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001384 public void setFloat(int viewId, String methodName, float value) {
1385 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.FLOAT, value));
1386 }
1387
Joe Onorato592d0652009-03-24 22:25:52 -07001388 /**
1389 * Call a method taking one double on a view in the layout for this RemoteViews.
1390 *
1391 * @param viewId The id of the view whose text should change
1392 * @param methodName The name of the method to call.
1393 * @param value The value to pass to the method.
1394 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001395 public void setDouble(int viewId, String methodName, double value) {
1396 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.DOUBLE, value));
1397 }
1398
Joe Onorato592d0652009-03-24 22:25:52 -07001399 /**
1400 * Call a method taking one char on a view in the layout for this RemoteViews.
1401 *
1402 * @param viewId The id of the view whose text should change
1403 * @param methodName The name of the method to call.
1404 * @param value The value to pass to the method.
1405 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001406 public void setChar(int viewId, String methodName, char value) {
1407 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.CHAR, value));
1408 }
1409
Joe Onorato592d0652009-03-24 22:25:52 -07001410 /**
1411 * Call a method taking one String on a view in the layout for this RemoteViews.
1412 *
1413 * @param viewId The id of the view whose text should change
1414 * @param methodName The name of the method to call.
1415 * @param value The value to pass to the method.
1416 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001417 public void setString(int viewId, String methodName, String value) {
1418 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.STRING, value));
1419 }
1420
Joe Onorato592d0652009-03-24 22:25:52 -07001421 /**
1422 * Call a method taking one CharSequence on a view in the layout for this RemoteViews.
1423 *
1424 * @param viewId The id of the view whose text should change
1425 * @param methodName The name of the method to call.
1426 * @param value The value to pass to the method.
1427 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001428 public void setCharSequence(int viewId, String methodName, CharSequence value) {
1429 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.CHAR_SEQUENCE, value));
1430 }
1431
Joe Onorato592d0652009-03-24 22:25:52 -07001432 /**
1433 * Call a method taking one Uri on a view in the layout for this RemoteViews.
1434 *
1435 * @param viewId The id of the view whose text should change
1436 * @param methodName The name of the method to call.
1437 * @param value The value to pass to the method.
1438 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001439 public void setUri(int viewId, String methodName, Uri value) {
1440 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.URI, value));
1441 }
1442
Joe Onorato592d0652009-03-24 22:25:52 -07001443 /**
1444 * Call a method taking one Bitmap on a view in the layout for this RemoteViews.
1445 * @more
1446 * <p class="note">The bitmap will be flattened into the parcel if this object is
1447 * sent across processes, so it may end up using a lot of memory, and may be fairly slow.</p>
1448 *
1449 * @param viewId The id of the view whose text should change
1450 * @param methodName The name of the method to call.
1451 * @param value The value to pass to the method.
1452 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001453 public void setBitmap(int viewId, String methodName, Bitmap value) {
1454 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.BITMAP, value));
1455 }
1456
1457 /**
Bjorn Bringertd755b062010-01-06 17:15:37 +00001458 * Call a method taking one Bundle on a view in the layout for this RemoteViews.
1459 *
1460 * @param viewId The id of the view whose text should change
1461 * @param methodName The name of the method to call.
1462 * @param value The value to pass to the method.
1463 */
1464 public void setBundle(int viewId, String methodName, Bundle value) {
1465 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.BUNDLE, value));
1466 }
1467
1468 /**
Winson Chung499cb9f2010-07-16 11:18:17 -07001469 *
1470 * @param viewId
1471 * @param methodName
1472 * @param value
1473 */
1474 public void setIntent(int viewId, String methodName, Intent value) {
1475 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.INTENT, value));
1476 }
1477
1478 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001479 * Inflates the view hierarchy represented by this object and applies
1480 * all of the actions.
1481 *
1482 * <p><strong>Caller beware: this may throw</strong>
1483 *
1484 * @param context Default context to use
1485 * @param parent Parent that the resulting view hierarchy will be attached to. This method
1486 * does <strong>not</strong> attach the hierarchy. The caller should do so when appropriate.
1487 * @return The inflated view hierarchy
1488 */
1489 public View apply(Context context, ViewGroup parent) {
Romain Guya5475592009-07-01 17:20:08 -07001490 View result;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001491
1492 Context c = prepareContext(context);
1493
Romain Guya5475592009-07-01 17:20:08 -07001494 LayoutInflater inflater = (LayoutInflater)
1495 c.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001496
1497 inflater = inflater.cloneInContext(c);
1498 inflater.setFilter(this);
1499
1500 result = inflater.inflate(mLayoutId, parent, false);
1501
1502 performApply(result);
1503
1504 return result;
1505 }
1506
1507 /**
1508 * Applies all of the actions to the provided view.
1509 *
1510 * <p><strong>Caller beware: this may throw</strong>
1511 *
1512 * @param v The view to apply the actions to. This should be the result of
1513 * the {@link #apply(Context,ViewGroup)} call.
1514 */
1515 public void reapply(Context context, View v) {
1516 prepareContext(context);
1517 performApply(v);
1518 }
1519
1520 private void performApply(View v) {
1521 if (mActions != null) {
1522 final int count = mActions.size();
1523 for (int i = 0; i < count; i++) {
1524 Action a = mActions.get(i);
1525 a.apply(v);
1526 }
1527 }
1528 }
1529
1530 private Context prepareContext(Context context) {
Romain Guya5475592009-07-01 17:20:08 -07001531 Context c;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001532 String packageName = mPackage;
1533
1534 if (packageName != null) {
1535 try {
Romain Guy870e09f2009-07-06 16:35:25 -07001536 c = context.createPackageContext(packageName, Context.CONTEXT_RESTRICTED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001537 } catch (NameNotFoundException e) {
1538 Log.e(LOG_TAG, "Package name " + packageName + " not found");
1539 c = context;
1540 }
1541 } else {
1542 c = context;
1543 }
1544
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001545 return c;
1546 }
1547
1548 /* (non-Javadoc)
1549 * Used to restrict the views which can be inflated
1550 *
1551 * @see android.view.LayoutInflater.Filter#onLoadClass(java.lang.Class)
1552 */
Gilles Debunnee6ac8b92010-06-17 10:55:04 -07001553 public boolean onLoadClass(Class clazz) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001554 return clazz.isAnnotationPresent(RemoteView.class);
1555 }
1556
1557 public int describeContents() {
1558 return 0;
1559 }
1560
1561 public void writeToParcel(Parcel dest, int flags) {
1562 dest.writeString(mPackage);
1563 dest.writeInt(mLayoutId);
Adam Cohenca6fd842010-09-03 18:10:35 -07001564 dest.writeInt(mIsWidgetCollectionChild ? 1 : 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001565 int count;
1566 if (mActions != null) {
1567 count = mActions.size();
1568 } else {
1569 count = 0;
1570 }
1571 dest.writeInt(count);
1572 for (int i=0; i<count; i++) {
1573 Action a = mActions.get(i);
1574 a.writeToParcel(dest, 0);
1575 }
1576 }
1577
1578 /**
1579 * Parcelable.Creator that instantiates RemoteViews objects
1580 */
1581 public static final Parcelable.Creator<RemoteViews> CREATOR = new Parcelable.Creator<RemoteViews>() {
1582 public RemoteViews createFromParcel(Parcel parcel) {
1583 return new RemoteViews(parcel);
1584 }
1585
1586 public RemoteViews[] newArray(int size) {
1587 return new RemoteViews[size];
1588 }
1589 };
1590}