2 * Copyright (C) 2007 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package android.widget;
19 import android.annotation.ColorInt;
20 import android.annotation.DimenRes;
21 import android.app.ActivityManager.StackId;
22 import android.app.ActivityOptions;
23 import android.app.ActivityThread;
24 import android.app.Application;
25 import android.app.PendingIntent;
26 import android.app.RemoteInput;
27 import android.appwidget.AppWidgetHostView;
28 import android.content.Context;
29 import android.content.ContextWrapper;
30 import android.content.Intent;
31 import android.content.IntentSender;
32 import android.content.pm.ApplicationInfo;
33 import android.content.pm.PackageManager.NameNotFoundException;
34 import android.content.res.ColorStateList;
35 import android.content.res.Configuration;
36 import android.content.res.Resources;
37 import android.content.res.TypedArray;
38 import android.graphics.Bitmap;
39 import android.graphics.PorterDuff;
40 import android.graphics.Rect;
41 import android.graphics.drawable.Drawable;
42 import android.graphics.drawable.Icon;
43 import android.net.Uri;
44 import android.os.AsyncTask;
45 import android.os.Build;
46 import android.os.Bundle;
47 import android.os.CancellationSignal;
48 import android.os.Parcel;
49 import android.os.Parcelable;
50 import android.os.StrictMode;
51 import android.os.UserHandle;
52 import android.text.TextUtils;
53 import android.util.ArrayMap;
54 import android.util.Log;
55 import android.view.LayoutInflater;
56 import android.view.LayoutInflater.Filter;
57 import android.view.RemotableViewMethod;
58 import android.view.View;
59 import android.view.View.OnClickListener;
60 import android.view.ViewGroup;
61 import android.widget.AdapterView.OnItemClickListener;
62 import libcore.util.Objects;
64 import com.android.internal.R;
65 import com.android.internal.util.Preconditions;
67 import java.lang.annotation.ElementType;
68 import java.lang.annotation.Retention;
69 import java.lang.annotation.RetentionPolicy;
70 import java.lang.annotation.Target;
71 import java.lang.reflect.Method;
72 import java.util.ArrayList;
73 import java.util.HashMap;
74 import java.util.concurrent.Executor;
77 * A class that describes a view hierarchy that can be displayed in
78 * another process. The hierarchy is inflated from a layout resource
79 * file, and this class provides some basic operations for modifying
80 * the content of the inflated hierarchy.
82 public class RemoteViews implements Parcelable, Filter {
84 private static final String LOG_TAG = "RemoteViews";
87 * The intent extra that contains the appWidgetId.
90 static final String EXTRA_REMOTEADAPTER_APPWIDGET_ID = "remoteAdapterAppWidgetId";
93 * Application that hosts the remote views.
97 private ApplicationInfo mApplication;
100 * The resource ID of the layout file. (Added to the parcel)
102 private final int mLayoutId;
105 * An array of actions to perform on the view tree once it has been
108 private ArrayList<Action> mActions;
111 * A class to keep track of memory usage by this RemoteViews
113 private MemoryUsageCounter mMemoryUsageCounter;
116 * Maps bitmaps to unique indicies to avoid Bitmap duplication.
118 private BitmapCache mBitmapCache;
121 * Indicates whether or not this RemoteViews object is contained as a child of any other
124 private boolean mIsRoot = true;
127 * Constants to whether or not this RemoteViews is composed of a landscape and portrait
130 private static final int MODE_NORMAL = 0;
131 private static final int MODE_HAS_LANDSCAPE_AND_PORTRAIT = 1;
134 * Used in conjunction with the special constructor
135 * {@link #RemoteViews(RemoteViews, RemoteViews)} to keep track of the landscape and portrait
138 private RemoteViews mLandscape = null;
139 private RemoteViews mPortrait = null;
142 * This flag indicates whether this RemoteViews object is being created from a
143 * RemoteViewsService for use as a child of a widget collection. This flag is used
144 * to determine whether or not certain features are available, in particular,
145 * setting on click extras and setting on click pending intents. The former is enabled,
146 * and the latter disabled when this flag is true.
148 private boolean mIsWidgetCollectionChild = false;
150 private static final OnClickHandler DEFAULT_ON_CLICK_HANDLER = new OnClickHandler();
152 private static final Object[] sMethodsLock = new Object[0];
153 private static final ArrayMap<Class<? extends View>, ArrayMap<MutablePair<String, Class<?>>, Method>> sMethods =
154 new ArrayMap<Class<? extends View>, ArrayMap<MutablePair<String, Class<?>>, Method>>();
155 private static final ArrayMap<Method, Method> sAsyncMethods = new ArrayMap<>();
157 private static final ThreadLocal<Object[]> sInvokeArgsTls = new ThreadLocal<Object[]>() {
159 protected Object[] initialValue() {
160 return new Object[1];
167 public void setRemoteInputs(int viewId, RemoteInput[] remoteInputs) {
168 mActions.add(new SetRemoteInputsAction(viewId, remoteInputs));
174 static class MutablePair<F, S> {
178 MutablePair(F first, S second) {
180 this.second = second;
184 public boolean equals(Object o) {
185 if (!(o instanceof MutablePair)) {
188 MutablePair<?, ?> p = (MutablePair<?, ?>) o;
189 return Objects.equal(p.first, first) && Objects.equal(p.second, second);
193 public int hashCode() {
194 return (first == null ? 0 : first.hashCode()) ^ (second == null ? 0 : second.hashCode());
199 * This pair is used to perform lookups in sMethods without causing allocations.
201 private final MutablePair<String, Class<?>> mPair =
202 new MutablePair<String, Class<?>>(null, null);
205 * This annotation indicates that a subclass of View is alllowed to be used
206 * with the {@link RemoteViews} mechanism.
208 @Target({ ElementType.TYPE })
209 @Retention(RetentionPolicy.RUNTIME)
210 public @interface RemoteView {
214 * Exception to send when something goes wrong executing an action
217 public static class ActionException extends RuntimeException {
218 public ActionException(Exception ex) {
221 public ActionException(String message) {
227 public static class OnClickHandler {
229 private int mEnterAnimationId;
231 public boolean onClickHandler(View view, PendingIntent pendingIntent,
232 Intent fillInIntent) {
233 return onClickHandler(view, pendingIntent, fillInIntent, StackId.INVALID_STACK_ID);
236 public boolean onClickHandler(View view, PendingIntent pendingIntent,
237 Intent fillInIntent, int launchStackId) {
239 // TODO: Unregister this handler if PendingIntent.FLAG_ONE_SHOT?
240 Context context = view.getContext();
241 ActivityOptions opts;
242 if (mEnterAnimationId != 0) {
243 opts = ActivityOptions.makeCustomAnimation(context, mEnterAnimationId, 0);
245 opts = ActivityOptions.makeScaleUpAnimation(view,
247 view.getMeasuredWidth(), view.getMeasuredHeight());
250 if (launchStackId != StackId.INVALID_STACK_ID) {
251 opts.setLaunchStackId(launchStackId);
253 context.startIntentSender(
254 pendingIntent.getIntentSender(), fillInIntent,
255 Intent.FLAG_ACTIVITY_NEW_TASK,
256 Intent.FLAG_ACTIVITY_NEW_TASK, 0, opts.toBundle());
257 } catch (IntentSender.SendIntentException e) {
258 android.util.Log.e(LOG_TAG, "Cannot send pending intent: ", e);
260 } catch (Exception e) {
261 android.util.Log.e(LOG_TAG, "Cannot send pending intent due to " +
262 "unknown exception: ", e);
268 public void setEnterAnimationId(int enterAnimationId) {
269 mEnterAnimationId = enterAnimationId;
274 * Base class for all actions that can be performed on an
277 * SUBCLASSES MUST BE IMMUTABLE SO CLONE WORKS!!!!!
279 private abstract static class Action implements Parcelable {
280 public abstract void apply(View root, ViewGroup rootParent,
281 OnClickHandler handler) throws ActionException;
283 public static final int MERGE_REPLACE = 0;
284 public static final int MERGE_APPEND = 1;
285 public static final int MERGE_IGNORE = 2;
287 public int describeContents() {
292 * Overridden by each class to report on it's own memory usage
294 public void updateMemoryUsageEstimate(MemoryUsageCounter counter) {
295 // We currently only calculate Bitmap memory usage, so by default,
296 // don't do anything here
299 public void setBitmapCache(BitmapCache bitmapCache) {
303 public int mergeBehavior() {
304 return MERGE_REPLACE;
307 public abstract String getActionName();
309 public String getUniqueKey() {
310 return (getActionName() + viewId);
314 * This is called on the background thread. It should perform any non-ui computations
315 * and return the final action which will run on the UI thread.
316 * Override this if some of the tasks can be performed async.
318 public Action initActionAsync(ViewTree root, ViewGroup rootParent, OnClickHandler handler) {
326 * Action class used during async inflation of RemoteViews. Subclasses are not parcelable.
328 private static abstract class RuntimeAction extends Action {
330 public final String getActionName() {
331 return "RuntimeAction";
335 public final void writeToParcel(Parcel dest, int flags) {
336 throw new UnsupportedOperationException();
340 // Constant used during async execution. It is not parcelable.
341 private static final Action ACTION_NOOP = new RuntimeAction() {
343 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) { }
347 * Merges the passed RemoteViews actions with this RemoteViews actions according to
348 * action-specific merge rules.
354 public void mergeRemoteViews(RemoteViews newRv) {
355 if (newRv == null) return;
356 // We first copy the new RemoteViews, as the process of merging modifies the way the actions
357 // reference the bitmap cache. We don't want to modify the object as it may need to
358 // be merged and applied multiple times.
359 RemoteViews copy = newRv.clone();
361 HashMap<String, Action> map = new HashMap<String, Action>();
362 if (mActions == null) {
363 mActions = new ArrayList<Action>();
366 int count = mActions.size();
367 for (int i = 0; i < count; i++) {
368 Action a = mActions.get(i);
369 map.put(a.getUniqueKey(), a);
372 ArrayList<Action> newActions = copy.mActions;
373 if (newActions == null) return;
374 count = newActions.size();
375 for (int i = 0; i < count; i++) {
376 Action a = newActions.get(i);
377 String key = newActions.get(i).getUniqueKey();
378 int mergeBehavior = newActions.get(i).mergeBehavior();
379 if (map.containsKey(key) && mergeBehavior == Action.MERGE_REPLACE) {
380 mActions.remove(map.get(key));
384 // If the merge behavior is ignore, we don't bother keeping the extra action
385 if (mergeBehavior == Action.MERGE_REPLACE || mergeBehavior == Action.MERGE_APPEND) {
390 // Because pruning can remove the need for bitmaps, we reconstruct the bitmap cache
391 mBitmapCache = new BitmapCache();
392 setBitmapCache(mBitmapCache);
395 private class SetEmptyView extends Action {
399 public final static int TAG = 6;
401 SetEmptyView(int viewId, int emptyViewId) {
402 this.viewId = viewId;
403 this.emptyViewId = emptyViewId;
406 SetEmptyView(Parcel in) {
407 this.viewId = in.readInt();
408 this.emptyViewId = in.readInt();
411 public void writeToParcel(Parcel out, int flags) {
413 out.writeInt(this.viewId);
414 out.writeInt(this.emptyViewId);
418 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
419 final View view = root.findViewById(viewId);
420 if (!(view instanceof AdapterView<?>)) return;
422 AdapterView<?> adapterView = (AdapterView<?>) view;
424 final View emptyView = root.findViewById(emptyViewId);
425 if (emptyView == null) return;
427 adapterView.setEmptyView(emptyView);
430 public String getActionName() {
431 return "SetEmptyView";
435 private class SetOnClickFillInIntent extends Action {
436 public SetOnClickFillInIntent(int id, Intent fillInIntent) {
438 this.fillInIntent = fillInIntent;
441 public SetOnClickFillInIntent(Parcel parcel) {
442 viewId = parcel.readInt();
443 fillInIntent = Intent.CREATOR.createFromParcel(parcel);
446 public void writeToParcel(Parcel dest, int flags) {
448 dest.writeInt(viewId);
449 fillInIntent.writeToParcel(dest, 0 /* no flags */);
453 public void apply(View root, ViewGroup rootParent, final OnClickHandler handler) {
454 final View target = root.findViewById(viewId);
455 if (target == null) return;
457 if (!mIsWidgetCollectionChild) {
458 Log.e(LOG_TAG, "The method setOnClickFillInIntent is available " +
459 "only from RemoteViewsFactory (ie. on collection items).");
462 if (target == root) {
463 target.setTagInternal(com.android.internal.R.id.fillInIntent, fillInIntent);
464 } else if (fillInIntent != null) {
465 OnClickListener listener = new OnClickListener() {
466 public void onClick(View v) {
467 // Insure that this view is a child of an AdapterView
468 View parent = (View) v.getParent();
469 // Break the for loop on the first encounter of:
470 // 1) an AdapterView,
471 // 2) an AppWidgetHostView that is not a RemoteViewsFrameLayout, or
473 // 2) and 3) are unexpected and catch the case where a child is not
474 // correctly parented in an AdapterView.
475 while (parent != null && !(parent instanceof AdapterView<?>)
476 && !((parent instanceof AppWidgetHostView) &&
477 !(parent instanceof RemoteViewsAdapter.RemoteViewsFrameLayout))) {
478 parent = (View) parent.getParent();
481 if (!(parent instanceof AdapterView<?>)) {
482 // Somehow they've managed to get this far without having
483 // and AdapterView as a parent.
484 Log.e(LOG_TAG, "Collection item doesn't have AdapterView parent");
488 // Insure that a template pending intent has been set on an ancestor
489 if (!(parent.getTag() instanceof PendingIntent)) {
490 Log.e(LOG_TAG, "Attempting setOnClickFillInIntent without" +
491 " calling setPendingIntentTemplate on parent.");
495 PendingIntent pendingIntent = (PendingIntent) parent.getTag();
497 final Rect rect = getSourceBounds(v);
499 fillInIntent.setSourceBounds(rect);
500 handler.onClickHandler(v, pendingIntent, fillInIntent);
504 target.setOnClickListener(listener);
508 public String getActionName() {
509 return "SetOnClickFillInIntent";
514 public final static int TAG = 9;
517 private class SetPendingIntentTemplate extends Action {
518 public SetPendingIntentTemplate(int id, PendingIntent pendingIntentTemplate) {
520 this.pendingIntentTemplate = pendingIntentTemplate;
523 public SetPendingIntentTemplate(Parcel parcel) {
524 viewId = parcel.readInt();
525 pendingIntentTemplate = PendingIntent.readPendingIntentOrNullFromParcel(parcel);
528 public void writeToParcel(Parcel dest, int flags) {
530 dest.writeInt(viewId);
531 pendingIntentTemplate.writeToParcel(dest, 0 /* no flags */);
535 public void apply(View root, ViewGroup rootParent, final OnClickHandler handler) {
536 final View target = root.findViewById(viewId);
537 if (target == null) return;
539 // If the view isn't an AdapterView, setting a PendingIntent template doesn't make sense
540 if (target instanceof AdapterView<?>) {
541 AdapterView<?> av = (AdapterView<?>) target;
542 // The PendingIntent template is stored in the view's tag.
543 OnItemClickListener listener = new OnItemClickListener() {
544 public void onItemClick(AdapterView<?> parent, View view,
545 int position, long id) {
546 // The view should be a frame layout
547 if (view instanceof ViewGroup) {
548 ViewGroup vg = (ViewGroup) view;
550 // AdapterViews contain their children in a frame
551 // so we need to go one layer deeper here.
552 if (parent instanceof AdapterViewAnimator) {
553 vg = (ViewGroup) vg.getChildAt(0);
555 if (vg == null) return;
557 Intent fillInIntent = null;
558 int childCount = vg.getChildCount();
559 for (int i = 0; i < childCount; i++) {
560 Object tag = vg.getChildAt(i).getTag(com.android.internal.R.id.fillInIntent);
561 if (tag instanceof Intent) {
562 fillInIntent = (Intent) tag;
566 if (fillInIntent == null) return;
568 final Rect rect = getSourceBounds(view);
570 final Intent intent = new Intent();
571 intent.setSourceBounds(rect);
572 handler.onClickHandler(view, pendingIntentTemplate, fillInIntent);
576 av.setOnItemClickListener(listener);
577 av.setTag(pendingIntentTemplate);
579 Log.e(LOG_TAG, "Cannot setPendingIntentTemplate on a view which is not" +
580 "an AdapterView (id: " + viewId + ")");
585 public String getActionName() {
586 return "SetPendingIntentTemplate";
589 PendingIntent pendingIntentTemplate;
591 public final static int TAG = 8;
594 private class SetRemoteViewsAdapterList extends Action {
595 public SetRemoteViewsAdapterList(int id, ArrayList<RemoteViews> list, int viewTypeCount) {
598 this.viewTypeCount = viewTypeCount;
601 public SetRemoteViewsAdapterList(Parcel parcel) {
602 viewId = parcel.readInt();
603 viewTypeCount = parcel.readInt();
604 int count = parcel.readInt();
605 list = new ArrayList<RemoteViews>();
607 for (int i = 0; i < count; i++) {
608 RemoteViews rv = RemoteViews.CREATOR.createFromParcel(parcel);
613 public void writeToParcel(Parcel dest, int flags) {
615 dest.writeInt(viewId);
616 dest.writeInt(viewTypeCount);
618 if (list == null || list.size() == 0) {
621 int count = list.size();
622 dest.writeInt(count);
623 for (int i = 0; i < count; i++) {
624 RemoteViews rv = list.get(i);
625 rv.writeToParcel(dest, flags);
631 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
632 final View target = root.findViewById(viewId);
633 if (target == null) return;
635 // Ensure that we are applying to an AppWidget root
636 if (!(rootParent instanceof AppWidgetHostView)) {
637 Log.e(LOG_TAG, "SetRemoteViewsAdapterIntent action can only be used for " +
638 "AppWidgets (root id: " + viewId + ")");
641 // Ensure that we are calling setRemoteAdapter on an AdapterView that supports it
642 if (!(target instanceof AbsListView) && !(target instanceof AdapterViewAnimator)) {
643 Log.e(LOG_TAG, "Cannot setRemoteViewsAdapter on a view which is not " +
644 "an AbsListView or AdapterViewAnimator (id: " + viewId + ")");
648 if (target instanceof AbsListView) {
649 AbsListView v = (AbsListView) target;
650 Adapter a = v.getAdapter();
651 if (a instanceof RemoteViewsListAdapter && viewTypeCount <= a.getViewTypeCount()) {
652 ((RemoteViewsListAdapter) a).setViewsList(list);
654 v.setAdapter(new RemoteViewsListAdapter(v.getContext(), list, viewTypeCount));
656 } else if (target instanceof AdapterViewAnimator) {
657 AdapterViewAnimator v = (AdapterViewAnimator) target;
658 Adapter a = v.getAdapter();
659 if (a instanceof RemoteViewsListAdapter && viewTypeCount <= a.getViewTypeCount()) {
660 ((RemoteViewsListAdapter) a).setViewsList(list);
662 v.setAdapter(new RemoteViewsListAdapter(v.getContext(), list, viewTypeCount));
667 public String getActionName() {
668 return "SetRemoteViewsAdapterList";
672 ArrayList<RemoteViews> list;
673 public final static int TAG = 15;
676 private class SetRemoteViewsAdapterIntent extends Action {
677 public SetRemoteViewsAdapterIntent(int id, Intent intent) {
679 this.intent = intent;
682 public SetRemoteViewsAdapterIntent(Parcel parcel) {
683 viewId = parcel.readInt();
684 intent = Intent.CREATOR.createFromParcel(parcel);
687 public void writeToParcel(Parcel dest, int flags) {
689 dest.writeInt(viewId);
690 intent.writeToParcel(dest, flags);
694 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
695 final View target = root.findViewById(viewId);
696 if (target == null) return;
698 // Ensure that we are applying to an AppWidget root
699 if (!(rootParent instanceof AppWidgetHostView)) {
700 Log.e(LOG_TAG, "SetRemoteViewsAdapterIntent action can only be used for " +
701 "AppWidgets (root id: " + viewId + ")");
704 // Ensure that we are calling setRemoteAdapter on an AdapterView that supports it
705 if (!(target instanceof AbsListView) && !(target instanceof AdapterViewAnimator)) {
706 Log.e(LOG_TAG, "Cannot setRemoteViewsAdapter on a view which is not " +
707 "an AbsListView or AdapterViewAnimator (id: " + viewId + ")");
711 // Embed the AppWidget Id for use in RemoteViewsAdapter when connecting to the intent
712 // RemoteViewsService
713 AppWidgetHostView host = (AppWidgetHostView) rootParent;
714 intent.putExtra(EXTRA_REMOTEADAPTER_APPWIDGET_ID, host.getAppWidgetId());
715 if (target instanceof AbsListView) {
716 AbsListView v = (AbsListView) target;
717 v.setRemoteViewsAdapter(intent);
718 v.setRemoteViewsOnClickHandler(handler);
719 } else if (target instanceof AdapterViewAnimator) {
720 AdapterViewAnimator v = (AdapterViewAnimator) target;
721 v.setRemoteViewsAdapter(intent);
722 v.setRemoteViewsOnClickHandler(handler);
726 public String getActionName() {
727 return "SetRemoteViewsAdapterIntent";
732 public final static int TAG = 10;
736 * Equivalent to calling
737 * {@link android.view.View#setOnClickListener(android.view.View.OnClickListener)}
738 * to launch the provided {@link PendingIntent}.
740 private class SetOnClickPendingIntent extends Action {
741 public SetOnClickPendingIntent(int id, PendingIntent pendingIntent) {
743 this.pendingIntent = pendingIntent;
746 public SetOnClickPendingIntent(Parcel parcel) {
747 viewId = parcel.readInt();
749 // We check a flag to determine if the parcel contains a PendingIntent.
750 if (parcel.readInt() != 0) {
751 pendingIntent = PendingIntent.readPendingIntentOrNullFromParcel(parcel);
755 public void writeToParcel(Parcel dest, int flags) {
757 dest.writeInt(viewId);
759 // We use a flag to indicate whether the parcel contains a valid object.
760 dest.writeInt(pendingIntent != null ? 1 : 0);
761 if (pendingIntent != null) {
762 pendingIntent.writeToParcel(dest, 0 /* no flags */);
767 public void apply(View root, ViewGroup rootParent, final OnClickHandler handler) {
768 final View target = root.findViewById(viewId);
769 if (target == null) return;
771 // If the view is an AdapterView, setting a PendingIntent on click doesn't make much
772 // sense, do they mean to set a PendingIntent template for the AdapterView's children?
773 if (mIsWidgetCollectionChild) {
774 Log.w(LOG_TAG, "Cannot setOnClickPendingIntent for collection item " +
775 "(id: " + viewId + ")");
776 ApplicationInfo appInfo = root.getContext().getApplicationInfo();
778 // We let this slide for HC and ICS so as to not break compatibility. It should have
779 // been disabled from the outset, but was left open by accident.
780 if (appInfo != null &&
781 appInfo.targetSdkVersion >= Build.VERSION_CODES.JELLY_BEAN) {
786 // If the pendingIntent is null, we clear the onClickListener
787 OnClickListener listener = null;
788 if (pendingIntent != null) {
789 listener = new OnClickListener() {
790 public void onClick(View v) {
791 // Find target view location in screen coordinates and
792 // fill into PendingIntent before sending.
793 final Rect rect = getSourceBounds(v);
795 final Intent intent = new Intent();
796 intent.setSourceBounds(rect);
797 handler.onClickHandler(v, pendingIntent, intent);
801 target.setOnClickListener(listener);
804 public String getActionName() {
805 return "SetOnClickPendingIntent";
808 PendingIntent pendingIntent;
810 public final static int TAG = 1;
813 private static Rect getSourceBounds(View v) {
814 final float appScale = v.getContext().getResources()
815 .getCompatibilityInfo().applicationScale;
816 final int[] pos = new int[2];
817 v.getLocationOnScreen(pos);
819 final Rect rect = new Rect();
820 rect.left = (int) (pos[0] * appScale + 0.5f);
821 rect.top = (int) (pos[1] * appScale + 0.5f);
822 rect.right = (int) ((pos[0] + v.getWidth()) * appScale + 0.5f);
823 rect.bottom = (int) ((pos[1] + v.getHeight()) * appScale + 0.5f);
827 private Method getMethod(View view, String methodName, Class<?> paramType) {
829 Class<? extends View> klass = view.getClass();
831 synchronized (sMethodsLock) {
832 ArrayMap<MutablePair<String, Class<?>>, Method> methods = sMethods.get(klass);
833 if (methods == null) {
834 methods = new ArrayMap<MutablePair<String, Class<?>>, Method>();
835 sMethods.put(klass, methods);
838 mPair.first = methodName;
839 mPair.second = paramType;
841 method = methods.get(mPair);
842 if (method == null) {
844 if (paramType == null) {
845 method = klass.getMethod(methodName);
847 method = klass.getMethod(methodName, paramType);
849 } catch (NoSuchMethodException ex) {
850 throw new ActionException("view: " + klass.getName() + " doesn't have method: "
851 + methodName + getParameters(paramType));
854 if (!method.isAnnotationPresent(RemotableViewMethod.class)) {
855 throw new ActionException("view: " + klass.getName()
856 + " can't use method with RemoteViews: "
857 + methodName + getParameters(paramType));
860 methods.put(new MutablePair<String, Class<?>>(methodName, paramType), method);
868 * @return the async implementation of the provided method.
870 private Method getAsyncMethod(Method method) {
871 synchronized (sAsyncMethods) {
872 int valueIndex = sAsyncMethods.indexOfKey(method);
873 if (valueIndex >= 0) {
874 return sAsyncMethods.valueAt(valueIndex);
877 RemotableViewMethod annotation = method.getAnnotation(RemotableViewMethod.class);
878 Method asyncMethod = null;
879 if (!annotation.asyncImpl().isEmpty()) {
881 asyncMethod = method.getDeclaringClass()
882 .getMethod(annotation.asyncImpl(), method.getParameterTypes());
883 if (!asyncMethod.getReturnType().equals(Runnable.class)) {
884 throw new ActionException("Async implementation for " + method.getName() +
885 " does not return a Runnable");
887 } catch (NoSuchMethodException ex) {
888 throw new ActionException("Async implementation declared but not defined for " +
892 sAsyncMethods.put(method, asyncMethod);
897 private static String getParameters(Class<?> paramType) {
898 if (paramType == null) return "()";
899 return "(" + paramType + ")";
902 private static Object[] wrapArg(Object value) {
903 Object[] args = sInvokeArgsTls.get();
909 * Equivalent to calling a combination of {@link Drawable#setAlpha(int)},
910 * {@link Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)},
911 * and/or {@link Drawable#setLevel(int)} on the {@link Drawable} of a given view.
913 * These operations will be performed on the {@link Drawable} returned by the
914 * target {@link View#getBackground()} by default. If targetBackground is false,
915 * we assume the target is an {@link ImageView} and try applying the operations
916 * to {@link ImageView#getDrawable()}.
918 * You can omit specific calls by marking their values with null or -1.
920 private class SetDrawableParameters extends Action {
921 public SetDrawableParameters(int id, boolean targetBackground, int alpha,
922 int colorFilter, PorterDuff.Mode mode, int level) {
924 this.targetBackground = targetBackground;
926 this.colorFilter = colorFilter;
927 this.filterMode = mode;
931 public SetDrawableParameters(Parcel parcel) {
932 viewId = parcel.readInt();
933 targetBackground = parcel.readInt() != 0;
934 alpha = parcel.readInt();
935 colorFilter = parcel.readInt();
936 boolean hasMode = parcel.readInt() != 0;
938 filterMode = PorterDuff.Mode.valueOf(parcel.readString());
942 level = parcel.readInt();
945 public void writeToParcel(Parcel dest, int flags) {
947 dest.writeInt(viewId);
948 dest.writeInt(targetBackground ? 1 : 0);
949 dest.writeInt(alpha);
950 dest.writeInt(colorFilter);
951 if (filterMode != null) {
953 dest.writeString(filterMode.toString());
957 dest.writeInt(level);
961 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
962 final View target = root.findViewById(viewId);
963 if (target == null) return;
965 // Pick the correct drawable to modify for this view
966 Drawable targetDrawable = null;
967 if (targetBackground) {
968 targetDrawable = target.getBackground();
969 } else if (target instanceof ImageView) {
970 ImageView imageView = (ImageView) target;
971 targetDrawable = imageView.getDrawable();
974 if (targetDrawable != null) {
975 // Perform modifications only if values are set correctly
977 targetDrawable.mutate().setAlpha(alpha);
979 if (filterMode != null) {
980 targetDrawable.mutate().setColorFilter(colorFilter, filterMode);
983 targetDrawable.mutate().setLevel(level);
988 public String getActionName() {
989 return "SetDrawableParameters";
992 boolean targetBackground;
995 PorterDuff.Mode filterMode;
998 public final static int TAG = 3;
1001 private final class ReflectionActionWithoutParams extends Action {
1002 final String methodName;
1004 public final static int TAG = 5;
1006 ReflectionActionWithoutParams(int viewId, String methodName) {
1007 this.viewId = viewId;
1008 this.methodName = methodName;
1011 ReflectionActionWithoutParams(Parcel in) {
1012 this.viewId = in.readInt();
1013 this.methodName = in.readString();
1016 public void writeToParcel(Parcel out, int flags) {
1018 out.writeInt(this.viewId);
1019 out.writeString(this.methodName);
1023 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
1024 final View view = root.findViewById(viewId);
1025 if (view == null) return;
1028 getMethod(view, this.methodName, null).invoke(view);
1029 } catch (ActionException e) {
1031 } catch (Exception ex) {
1032 throw new ActionException(ex);
1036 public int mergeBehavior() {
1037 // we don't need to build up showNext or showPrevious calls
1038 if (methodName.equals("showNext") || methodName.equals("showPrevious")) {
1039 return MERGE_IGNORE;
1041 return MERGE_REPLACE;
1045 public String getActionName() {
1046 return "ReflectionActionWithoutParams";
1050 private static class BitmapCache {
1051 ArrayList<Bitmap> mBitmaps;
1053 public BitmapCache() {
1054 mBitmaps = new ArrayList<Bitmap>();
1057 public BitmapCache(Parcel source) {
1058 int count = source.readInt();
1059 mBitmaps = new ArrayList<Bitmap>();
1060 for (int i = 0; i < count; i++) {
1061 Bitmap b = Bitmap.CREATOR.createFromParcel(source);
1066 public int getBitmapId(Bitmap b) {
1070 if (mBitmaps.contains(b)) {
1071 return mBitmaps.indexOf(b);
1074 return (mBitmaps.size() - 1);
1079 public Bitmap getBitmapForId(int id) {
1080 if (id == -1 || id >= mBitmaps.size()) {
1083 return mBitmaps.get(id);
1087 public void writeBitmapsToParcel(Parcel dest, int flags) {
1088 int count = mBitmaps.size();
1089 dest.writeInt(count);
1090 for (int i = 0; i < count; i++) {
1091 mBitmaps.get(i).writeToParcel(dest, flags);
1095 public void assimilate(BitmapCache bitmapCache) {
1096 ArrayList<Bitmap> bitmapsToBeAdded = bitmapCache.mBitmaps;
1097 int count = bitmapsToBeAdded.size();
1098 for (int i = 0; i < count; i++) {
1099 Bitmap b = bitmapsToBeAdded.get(i);
1100 if (!mBitmaps.contains(b)) {
1106 public void addBitmapMemory(MemoryUsageCounter memoryCounter) {
1107 for (int i = 0; i < mBitmaps.size(); i++) {
1108 memoryCounter.addBitmapMemory(mBitmaps.get(i));
1113 protected BitmapCache clone() {
1114 BitmapCache bitmapCache = new BitmapCache();
1115 bitmapCache.mBitmaps.addAll(mBitmaps);
1120 private class BitmapReflectionAction extends Action {
1125 BitmapReflectionAction(int viewId, String methodName, Bitmap bitmap) {
1126 this.bitmap = bitmap;
1127 this.viewId = viewId;
1128 this.methodName = methodName;
1129 bitmapId = mBitmapCache.getBitmapId(bitmap);
1132 BitmapReflectionAction(Parcel in) {
1133 viewId = in.readInt();
1134 methodName = in.readString();
1135 bitmapId = in.readInt();
1136 bitmap = mBitmapCache.getBitmapForId(bitmapId);
1140 public void writeToParcel(Parcel dest, int flags) {
1142 dest.writeInt(viewId);
1143 dest.writeString(methodName);
1144 dest.writeInt(bitmapId);
1148 public void apply(View root, ViewGroup rootParent,
1149 OnClickHandler handler) throws ActionException {
1150 ReflectionAction ra = new ReflectionAction(viewId, methodName, ReflectionAction.BITMAP,
1152 ra.apply(root, rootParent, handler);
1156 public void setBitmapCache(BitmapCache bitmapCache) {
1157 bitmapId = bitmapCache.getBitmapId(bitmap);
1160 public String getActionName() {
1161 return "BitmapReflectionAction";
1164 public final static int TAG = 12;
1168 * Base class for the reflection actions.
1170 private final class ReflectionAction extends Action {
1171 static final int TAG = 2;
1173 static final int BOOLEAN = 1;
1174 static final int BYTE = 2;
1175 static final int SHORT = 3;
1176 static final int INT = 4;
1177 static final int LONG = 5;
1178 static final int FLOAT = 6;
1179 static final int DOUBLE = 7;
1180 static final int CHAR = 8;
1181 static final int STRING = 9;
1182 static final int CHAR_SEQUENCE = 10;
1183 static final int URI = 11;
1184 // BITMAP actions are never stored in the list of actions. They are only used locally
1185 // to implement BitmapReflectionAction, which eliminates duplicates using BitmapCache.
1186 static final int BITMAP = 12;
1187 static final int BUNDLE = 13;
1188 static final int INTENT = 14;
1189 static final int COLOR_STATE_LIST = 15;
1190 static final int ICON = 16;
1196 ReflectionAction(int viewId, String methodName, int type, Object value) {
1197 this.viewId = viewId;
1198 this.methodName = methodName;
1203 ReflectionAction(Parcel in) {
1204 this.viewId = in.readInt();
1205 this.methodName = in.readString();
1206 this.type = in.readInt();
1207 //noinspection ConstantIfStatement
1209 Log.d(LOG_TAG, "read viewId=0x" + Integer.toHexString(this.viewId)
1210 + " methodName=" + this.methodName + " type=" + this.type);
1213 // For some values that may have been null, we first check a flag to see if they were
1214 // written to the parcel.
1215 switch (this.type) {
1217 this.value = in.readInt() != 0;
1220 this.value = in.readByte();
1223 this.value = (short)in.readInt();
1226 this.value = in.readInt();
1229 this.value = in.readLong();
1232 this.value = in.readFloat();
1235 this.value = in.readDouble();
1238 this.value = (char)in.readInt();
1241 this.value = in.readString();
1244 this.value = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
1247 if (in.readInt() != 0) {
1248 this.value = Uri.CREATOR.createFromParcel(in);
1252 if (in.readInt() != 0) {
1253 this.value = Bitmap.CREATOR.createFromParcel(in);
1257 this.value = in.readBundle();
1260 if (in.readInt() != 0) {
1261 this.value = Intent.CREATOR.createFromParcel(in);
1264 case COLOR_STATE_LIST:
1265 if (in.readInt() != 0) {
1266 this.value = ColorStateList.CREATOR.createFromParcel(in);
1270 if (in.readInt() != 0) {
1271 this.value = Icon.CREATOR.createFromParcel(in);
1278 public void writeToParcel(Parcel out, int flags) {
1280 out.writeInt(this.viewId);
1281 out.writeString(this.methodName);
1282 out.writeInt(this.type);
1283 //noinspection ConstantIfStatement
1285 Log.d(LOG_TAG, "write viewId=0x" + Integer.toHexString(this.viewId)
1286 + " methodName=" + this.methodName + " type=" + this.type);
1289 // For some values which are null, we record an integer flag to indicate whether
1290 // we have written a valid value to the parcel.
1291 switch (this.type) {
1293 out.writeInt((Boolean) this.value ? 1 : 0);
1296 out.writeByte((Byte) this.value);
1299 out.writeInt((Short) this.value);
1302 out.writeInt((Integer) this.value);
1305 out.writeLong((Long) this.value);
1308 out.writeFloat((Float) this.value);
1311 out.writeDouble((Double) this.value);
1314 out.writeInt((int)((Character)this.value).charValue());
1317 out.writeString((String)this.value);
1320 TextUtils.writeToParcel((CharSequence)this.value, out, flags);
1323 out.writeInt(this.value != null ? 1 : 0);
1324 if (this.value != null) {
1325 ((Uri)this.value).writeToParcel(out, flags);
1329 out.writeInt(this.value != null ? 1 : 0);
1330 if (this.value != null) {
1331 ((Bitmap)this.value).writeToParcel(out, flags);
1335 out.writeBundle((Bundle) this.value);
1338 out.writeInt(this.value != null ? 1 : 0);
1339 if (this.value != null) {
1340 ((Intent)this.value).writeToParcel(out, flags);
1343 case COLOR_STATE_LIST:
1344 out.writeInt(this.value != null ? 1 : 0);
1345 if (this.value != null) {
1346 ((ColorStateList)this.value).writeToParcel(out, flags);
1350 out.writeInt(this.value != null ? 1 : 0);
1351 if (this.value != null) {
1352 ((Icon)this.value).writeToParcel(out, flags);
1360 private Class<?> getParameterType() {
1361 switch (this.type) {
1363 return boolean.class;
1375 return double.class;
1379 return String.class;
1381 return CharSequence.class;
1385 return Bitmap.class;
1387 return Bundle.class;
1389 return Intent.class;
1390 case COLOR_STATE_LIST:
1391 return ColorStateList.class;
1400 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
1401 final View view = root.findViewById(viewId);
1402 if (view == null) return;
1404 Class<?> param = getParameterType();
1405 if (param == null) {
1406 throw new ActionException("bad type: " + this.type);
1410 getMethod(view, this.methodName, param).invoke(view, wrapArg(this.value));
1411 } catch (ActionException e) {
1413 } catch (Exception ex) {
1414 throw new ActionException(ex);
1419 public Action initActionAsync(ViewTree root, ViewGroup rootParent, OnClickHandler handler) {
1420 final View view = root.findViewById(viewId);
1421 if (view == null) return ACTION_NOOP;
1423 Class<?> param = getParameterType();
1424 if (param == null) {
1425 throw new ActionException("bad type: " + this.type);
1429 Method method = getMethod(view, this.methodName, param);
1430 Method asyncMethod = getAsyncMethod(method);
1432 if (asyncMethod != null) {
1433 Runnable endAction = (Runnable) asyncMethod.invoke(view, wrapArg(this.value));
1434 if (endAction == null) {
1437 return new RunnableAction(endAction);
1440 } catch (ActionException e) {
1442 } catch (Exception ex) {
1443 throw new ActionException(ex);
1449 public int mergeBehavior() {
1450 // smoothScrollBy is cumulative, everything else overwites.
1451 if (methodName.equals("smoothScrollBy")) {
1452 return MERGE_APPEND;
1454 return MERGE_REPLACE;
1458 public String getActionName() {
1459 // Each type of reflection action corresponds to a setter, so each should be seen as
1460 // unique from the standpoint of merging.
1461 return "ReflectionAction" + this.methodName + this.type;
1466 * This is only used for async execution of actions and it not parcelable.
1468 private static final class RunnableAction extends RuntimeAction {
1469 private final Runnable mRunnable;
1471 RunnableAction(Runnable r) {
1476 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
1481 private void configureRemoteViewsAsChild(RemoteViews rv) {
1482 mBitmapCache.assimilate(rv.mBitmapCache);
1483 rv.setBitmapCache(mBitmapCache);
1492 * Equivalent to calling {@link ViewGroup#addView(View)} after inflating the
1493 * given {@link RemoteViews}, or calling {@link ViewGroup#removeAllViews()}
1494 * when null. This allows users to build "nested" {@link RemoteViews}.
1496 private class ViewGroupAction extends Action {
1497 public ViewGroupAction(int viewId, RemoteViews nestedViews) {
1498 this.viewId = viewId;
1499 this.nestedViews = nestedViews;
1500 if (nestedViews != null) {
1501 configureRemoteViewsAsChild(nestedViews);
1505 public ViewGroupAction(Parcel parcel, BitmapCache bitmapCache) {
1506 viewId = parcel.readInt();
1507 boolean nestedViewsNull = parcel.readInt() == 0;
1508 if (!nestedViewsNull) {
1509 nestedViews = new RemoteViews(parcel, bitmapCache);
1515 public void writeToParcel(Parcel dest, int flags) {
1517 dest.writeInt(viewId);
1518 if (nestedViews != null) {
1520 nestedViews.writeToParcel(dest, flags);
1528 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
1529 final Context context = root.getContext();
1530 final ViewGroup target = (ViewGroup) root.findViewById(viewId);
1531 if (target == null) return;
1532 if (nestedViews != null) {
1533 // Inflate nested views and add as children
1534 target.addView(nestedViews.apply(context, target, handler));
1536 // Clear all children when nested views omitted
1537 target.removeAllViews();
1542 public Action initActionAsync(ViewTree root, ViewGroup rootParent, OnClickHandler handler) {
1543 // In the async implementation, update the view tree so that subsequent calls to
1544 // findViewById return the currect view.
1546 ViewTree target = root.findViewTreeById(viewId);
1547 if ((target == null) || !(target.mRoot instanceof ViewGroup)) {
1550 if (nestedViews == null) {
1551 // Clear all children when nested views omitted
1552 target.mChildren = null;
1555 // Inflate nested views and perform all the async tasks for the child remoteView.
1556 final Context context = root.mRoot.getContext();
1557 final AsyncApplyTask task = nestedViews.getAsyncApplyTask(
1558 context, (ViewGroup) target.mRoot, null, handler);
1559 final ViewTree tree = task.doInBackground();
1561 // Update the global view tree, so that next call to findViewTreeById
1562 // goes through the subtree as well.
1563 target.addChild(tree);
1565 return new RuntimeAction() {
1568 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) throws ActionException {
1569 // This view will exist as we have already made sure
1570 final ViewGroup target = (ViewGroup) root.findViewById(viewId);
1571 task.onPostExecute(tree);
1572 target.addView(task.mResult);
1579 public void updateMemoryUsageEstimate(MemoryUsageCounter counter) {
1580 if (nestedViews != null) {
1581 counter.increment(nestedViews.estimateMemoryUsage());
1586 public void setBitmapCache(BitmapCache bitmapCache) {
1587 if (nestedViews != null) {
1588 nestedViews.setBitmapCache(bitmapCache);
1592 public String getActionName() {
1593 return "ViewGroupAction" + (nestedViews == null ? "Remove" : "Add");
1596 public int mergeBehavior() {
1597 return MERGE_APPEND;
1600 RemoteViews nestedViews;
1602 public final static int TAG = 4;
1606 * Helper action to set compound drawables on a TextView. Supports relative
1607 * (s/t/e/b) or cardinal (l/t/r/b) arrangement.
1609 private class TextViewDrawableAction extends Action {
1610 public TextViewDrawableAction(int viewId, boolean isRelative, int d1, int d2, int d3, int d4) {
1611 this.viewId = viewId;
1612 this.isRelative = isRelative;
1613 this.useIcons = false;
1620 public TextViewDrawableAction(int viewId, boolean isRelative,
1621 Icon i1, Icon i2, Icon i3, Icon i4) {
1622 this.viewId = viewId;
1623 this.isRelative = isRelative;
1624 this.useIcons = true;
1631 public TextViewDrawableAction(Parcel parcel) {
1632 viewId = parcel.readInt();
1633 isRelative = (parcel.readInt() != 0);
1634 useIcons = (parcel.readInt() != 0);
1636 if (parcel.readInt() != 0) {
1637 i1 = Icon.CREATOR.createFromParcel(parcel);
1639 if (parcel.readInt() != 0) {
1640 i2 = Icon.CREATOR.createFromParcel(parcel);
1642 if (parcel.readInt() != 0) {
1643 i3 = Icon.CREATOR.createFromParcel(parcel);
1645 if (parcel.readInt() != 0) {
1646 i4 = Icon.CREATOR.createFromParcel(parcel);
1649 d1 = parcel.readInt();
1650 d2 = parcel.readInt();
1651 d3 = parcel.readInt();
1652 d4 = parcel.readInt();
1656 public void writeToParcel(Parcel dest, int flags) {
1658 dest.writeInt(viewId);
1659 dest.writeInt(isRelative ? 1 : 0);
1660 dest.writeInt(useIcons ? 1 : 0);
1664 i1.writeToParcel(dest, 0);
1670 i2.writeToParcel(dest, 0);
1676 i3.writeToParcel(dest, 0);
1682 i4.writeToParcel(dest, 0);
1695 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
1696 final TextView target = (TextView) root.findViewById(viewId);
1697 if (target == null) return;
1698 if (drawablesLoaded) {
1700 target.setCompoundDrawablesRelativeWithIntrinsicBounds(id1, id2, id3, id4);
1702 target.setCompoundDrawablesWithIntrinsicBounds(id1, id2, id3, id4);
1704 } else if (useIcons) {
1705 final Context ctx = target.getContext();
1706 final Drawable id1 = i1 == null ? null : i1.loadDrawable(ctx);
1707 final Drawable id2 = i2 == null ? null : i2.loadDrawable(ctx);
1708 final Drawable id3 = i3 == null ? null : i3.loadDrawable(ctx);
1709 final Drawable id4 = i4 == null ? null : i4.loadDrawable(ctx);
1711 target.setCompoundDrawablesRelativeWithIntrinsicBounds(id1, id2, id3, id4);
1713 target.setCompoundDrawablesWithIntrinsicBounds(id1, id2, id3, id4);
1717 target.setCompoundDrawablesRelativeWithIntrinsicBounds(d1, d2, d3, d4);
1719 target.setCompoundDrawablesWithIntrinsicBounds(d1, d2, d3, d4);
1725 public Action initActionAsync(ViewTree root, ViewGroup rootParent, OnClickHandler handler) {
1726 final TextView target = (TextView) root.findViewById(viewId);
1727 if (target == null) return ACTION_NOOP;
1729 TextViewDrawableAction copy = useIcons ?
1730 new TextViewDrawableAction(viewId, isRelative, i1, i2, i3, i4) :
1731 new TextViewDrawableAction(viewId, isRelative, d1, d2, d3, d4);
1733 // Load the drawables on the background thread.
1734 copy.drawablesLoaded = true;
1735 final Context ctx = target.getContext();
1738 copy.id1 = i1 == null ? null : i1.loadDrawable(ctx);
1739 copy.id2 = i2 == null ? null : i2.loadDrawable(ctx);
1740 copy.id3 = i3 == null ? null : i3.loadDrawable(ctx);
1741 copy.id4 = i4 == null ? null : i4.loadDrawable(ctx);
1743 copy.id1 = d1 == 0 ? null : ctx.getDrawable(d1);
1744 copy.id2 = d2 == 0 ? null : ctx.getDrawable(d2);
1745 copy.id3 = d3 == 0 ? null : ctx.getDrawable(d3);
1746 copy.id4 = d4 == 0 ? null : ctx.getDrawable(d4);
1751 public String getActionName() {
1752 return "TextViewDrawableAction";
1755 boolean isRelative = false;
1756 boolean useIcons = false;
1758 Icon i1, i2, i3, i4;
1760 boolean drawablesLoaded = false;
1761 Drawable id1, id2, id3, id4;
1763 public final static int TAG = 11;
1767 * Helper action to set text size on a TextView in any supported units.
1769 private class TextViewSizeAction extends Action {
1770 public TextViewSizeAction(int viewId, int units, float size) {
1771 this.viewId = viewId;
1776 public TextViewSizeAction(Parcel parcel) {
1777 viewId = parcel.readInt();
1778 units = parcel.readInt();
1779 size = parcel.readFloat();
1782 public void writeToParcel(Parcel dest, int flags) {
1784 dest.writeInt(viewId);
1785 dest.writeInt(units);
1786 dest.writeFloat(size);
1790 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
1791 final TextView target = (TextView) root.findViewById(viewId);
1792 if (target == null) return;
1793 target.setTextSize(units, size);
1796 public String getActionName() {
1797 return "TextViewSizeAction";
1803 public final static int TAG = 13;
1807 * Helper action to set padding on a View.
1809 private class ViewPaddingAction extends Action {
1810 public ViewPaddingAction(int viewId, int left, int top, int right, int bottom) {
1811 this.viewId = viewId;
1815 this.bottom = bottom;
1818 public ViewPaddingAction(Parcel parcel) {
1819 viewId = parcel.readInt();
1820 left = parcel.readInt();
1821 top = parcel.readInt();
1822 right = parcel.readInt();
1823 bottom = parcel.readInt();
1826 public void writeToParcel(Parcel dest, int flags) {
1828 dest.writeInt(viewId);
1829 dest.writeInt(left);
1831 dest.writeInt(right);
1832 dest.writeInt(bottom);
1836 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
1837 final View target = root.findViewById(viewId);
1838 if (target == null) return;
1839 target.setPadding(left, top, right, bottom);
1842 public String getActionName() {
1843 return "ViewPaddingAction";
1846 int left, top, right, bottom;
1848 public final static int TAG = 14;
1852 * Helper action to set layout params on a View.
1854 private static class LayoutParamAction extends Action {
1856 /** Set marginEnd */
1857 public static final int LAYOUT_MARGIN_END_DIMEN = 1;
1859 public static final int LAYOUT_WIDTH = 2;
1860 public static final int LAYOUT_MARGIN_BOTTOM_DIMEN = 3;
1863 * @param viewId ID of the view alter
1864 * @param property which layout parameter to alter
1865 * @param value new value of the layout parameter
1867 public LayoutParamAction(int viewId, int property, int value) {
1868 this.viewId = viewId;
1869 this.property = property;
1873 public LayoutParamAction(Parcel parcel) {
1874 viewId = parcel.readInt();
1875 property = parcel.readInt();
1876 value = parcel.readInt();
1879 public void writeToParcel(Parcel dest, int flags) {
1881 dest.writeInt(viewId);
1882 dest.writeInt(property);
1883 dest.writeInt(value);
1887 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
1888 final View target = root.findViewById(viewId);
1889 if (target == null) {
1892 ViewGroup.LayoutParams layoutParams = target.getLayoutParams();
1893 if (layoutParams == null) {
1897 case LAYOUT_MARGIN_END_DIMEN:
1898 if (layoutParams instanceof ViewGroup.MarginLayoutParams) {
1899 int resolved = resolveDimenPixelOffset(target, value);
1900 ((ViewGroup.MarginLayoutParams) layoutParams).setMarginEnd(resolved);
1901 target.setLayoutParams(layoutParams);
1904 case LAYOUT_MARGIN_BOTTOM_DIMEN:
1905 if (layoutParams instanceof ViewGroup.MarginLayoutParams) {
1906 int resolved = resolveDimenPixelOffset(target, value);
1907 ((ViewGroup.MarginLayoutParams) layoutParams).bottomMargin = resolved;
1908 target.setLayoutParams(layoutParams);
1912 layoutParams.width = value;
1913 target.setLayoutParams(layoutParams);
1916 throw new IllegalArgumentException("Unknown property " + property);
1920 private static int resolveDimenPixelOffset(View target, int value) {
1924 return target.getContext().getResources().getDimensionPixelOffset(value);
1927 public String getActionName() {
1928 return "LayoutParamAction" + property + ".";
1934 public final static int TAG = 19;
1938 * Helper action to set a color filter on a compound drawable on a TextView. Supports relative
1939 * (s/t/e/b) or cardinal (l/t/r/b) arrangement.
1941 private class TextViewDrawableColorFilterAction extends Action {
1942 public TextViewDrawableColorFilterAction(int viewId, boolean isRelative, int index,
1943 int color, PorterDuff.Mode mode) {
1944 this.viewId = viewId;
1945 this.isRelative = isRelative;
1951 public TextViewDrawableColorFilterAction(Parcel parcel) {
1952 viewId = parcel.readInt();
1953 isRelative = (parcel.readInt() != 0);
1954 index = parcel.readInt();
1955 color = parcel.readInt();
1956 mode = readPorterDuffMode(parcel);
1959 private PorterDuff.Mode readPorterDuffMode(Parcel parcel) {
1960 int mode = parcel.readInt();
1961 if (mode >= 0 && mode < PorterDuff.Mode.values().length) {
1962 return PorterDuff.Mode.values()[mode];
1964 return PorterDuff.Mode.CLEAR;
1968 public void writeToParcel(Parcel dest, int flags) {
1970 dest.writeInt(viewId);
1971 dest.writeInt(isRelative ? 1 : 0);
1972 dest.writeInt(index);
1973 dest.writeInt(color);
1974 dest.writeInt(mode.ordinal());
1978 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
1979 final TextView target = (TextView) root.findViewById(viewId);
1980 if (target == null) return;
1981 Drawable[] drawables = isRelative
1982 ? target.getCompoundDrawablesRelative()
1983 : target.getCompoundDrawables();
1984 if (index < 0 || index >= 4) {
1985 throw new IllegalStateException("index must be in range [0, 3].");
1987 Drawable d = drawables[index];
1990 d.setColorFilter(color, mode);
1994 public String getActionName() {
1995 return "TextViewDrawableColorFilterAction";
1998 final boolean isRelative;
2001 final PorterDuff.Mode mode;
2003 public final static int TAG = 17;
2007 * Helper action to add a view tag with RemoteInputs.
2009 private class SetRemoteInputsAction extends Action {
2011 public SetRemoteInputsAction(int viewId, RemoteInput[] remoteInputs) {
2012 this.viewId = viewId;
2013 this.remoteInputs = remoteInputs;
2016 public SetRemoteInputsAction(Parcel parcel) {
2017 viewId = parcel.readInt();
2018 remoteInputs = parcel.createTypedArray(RemoteInput.CREATOR);
2021 public void writeToParcel(Parcel dest, int flags) {
2023 dest.writeInt(viewId);
2024 dest.writeTypedArray(remoteInputs, flags);
2028 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
2029 final TextView target = (TextView) root.findViewById(viewId);
2030 if (target == null) return;
2032 target.setTagInternal(R.id.remote_input_tag, remoteInputs);
2035 public String getActionName() {
2036 return "SetRemoteInputsAction";
2039 final Parcelable[] remoteInputs;
2040 public final static int TAG = 18;
2044 * Simple class used to keep track of memory usage in a RemoteViews.
2047 private class MemoryUsageCounter {
2048 public void clear() {
2052 public void increment(int numBytes) {
2053 mMemoryUsage += numBytes;
2056 public int getMemoryUsage() {
2057 return mMemoryUsage;
2060 @SuppressWarnings("deprecation")
2061 public void addBitmapMemory(Bitmap b) {
2062 final Bitmap.Config c = b.getConfig();
2063 // If we don't know, be pessimistic and assume 4
2079 increment(b.getWidth() * b.getHeight() * bpp);
2086 * Create a new RemoteViews object that will display the views contained
2087 * in the specified layout file.
2089 * @param packageName Name of the package that contains the layout resource
2090 * @param layoutId The id of the layout resource
2092 public RemoteViews(String packageName, int layoutId) {
2093 this(getApplicationInfo(packageName, UserHandle.myUserId()), layoutId);
2097 * Create a new RemoteViews object that will display the views contained
2098 * in the specified layout file.
2100 * @param packageName Name of the package that contains the layout resource.
2101 * @param userId The user under which the package is running.
2102 * @param layoutId The id of the layout resource.
2106 public RemoteViews(String packageName, int userId, int layoutId) {
2107 this(getApplicationInfo(packageName, userId), layoutId);
2111 * Create a new RemoteViews object that will display the views contained
2112 * in the specified layout file.
2114 * @param application The application whose content is shown by the views.
2115 * @param layoutId The id of the layout resource.
2119 protected RemoteViews(ApplicationInfo application, int layoutId) {
2120 mApplication = application;
2121 mLayoutId = layoutId;
2122 mBitmapCache = new BitmapCache();
2123 // setup the memory usage statistics
2124 mMemoryUsageCounter = new MemoryUsageCounter();
2125 recalculateMemoryUsage();
2128 private boolean hasLandscapeAndPortraitLayouts() {
2129 return (mLandscape != null) && (mPortrait != null);
2133 * Create a new RemoteViews object that will inflate as the specified
2134 * landspace or portrait RemoteViews, depending on the current configuration.
2136 * @param landscape The RemoteViews to inflate in landscape configuration
2137 * @param portrait The RemoteViews to inflate in portrait configuration
2139 public RemoteViews(RemoteViews landscape, RemoteViews portrait) {
2140 if (landscape == null || portrait == null) {
2141 throw new RuntimeException("Both RemoteViews must be non-null");
2143 if (landscape.mApplication.uid != portrait.mApplication.uid
2144 || !landscape.mApplication.packageName.equals(portrait.mApplication.packageName)) {
2145 throw new RuntimeException("Both RemoteViews must share the same package and user");
2147 mApplication = portrait.mApplication;
2148 mLayoutId = portrait.getLayoutId();
2150 mLandscape = landscape;
2151 mPortrait = portrait;
2153 // setup the memory usage statistics
2154 mMemoryUsageCounter = new MemoryUsageCounter();
2156 mBitmapCache = new BitmapCache();
2157 configureRemoteViewsAsChild(landscape);
2158 configureRemoteViewsAsChild(portrait);
2160 recalculateMemoryUsage();
2164 * Reads a RemoteViews object from a parcel.
2168 public RemoteViews(Parcel parcel) {
2172 private RemoteViews(Parcel parcel, BitmapCache bitmapCache) {
2173 int mode = parcel.readInt();
2175 // We only store a bitmap cache in the root of the RemoteViews.
2176 if (bitmapCache == null) {
2177 mBitmapCache = new BitmapCache(parcel);
2179 setBitmapCache(bitmapCache);
2183 if (mode == MODE_NORMAL) {
2184 mApplication = parcel.readParcelable(null);
2185 mLayoutId = parcel.readInt();
2186 mIsWidgetCollectionChild = parcel.readInt() == 1;
2188 int count = parcel.readInt();
2190 mActions = new ArrayList<Action>(count);
2191 for (int i=0; i<count; i++) {
2192 int tag = parcel.readInt();
2194 case SetOnClickPendingIntent.TAG:
2195 mActions.add(new SetOnClickPendingIntent(parcel));
2197 case SetDrawableParameters.TAG:
2198 mActions.add(new SetDrawableParameters(parcel));
2200 case ReflectionAction.TAG:
2201 mActions.add(new ReflectionAction(parcel));
2203 case ViewGroupAction.TAG:
2204 mActions.add(new ViewGroupAction(parcel, mBitmapCache));
2206 case ReflectionActionWithoutParams.TAG:
2207 mActions.add(new ReflectionActionWithoutParams(parcel));
2209 case SetEmptyView.TAG:
2210 mActions.add(new SetEmptyView(parcel));
2212 case SetPendingIntentTemplate.TAG:
2213 mActions.add(new SetPendingIntentTemplate(parcel));
2215 case SetOnClickFillInIntent.TAG:
2216 mActions.add(new SetOnClickFillInIntent(parcel));
2218 case SetRemoteViewsAdapterIntent.TAG:
2219 mActions.add(new SetRemoteViewsAdapterIntent(parcel));
2221 case TextViewDrawableAction.TAG:
2222 mActions.add(new TextViewDrawableAction(parcel));
2224 case TextViewSizeAction.TAG:
2225 mActions.add(new TextViewSizeAction(parcel));
2227 case ViewPaddingAction.TAG:
2228 mActions.add(new ViewPaddingAction(parcel));
2230 case BitmapReflectionAction.TAG:
2231 mActions.add(new BitmapReflectionAction(parcel));
2233 case SetRemoteViewsAdapterList.TAG:
2234 mActions.add(new SetRemoteViewsAdapterList(parcel));
2236 case TextViewDrawableColorFilterAction.TAG:
2237 mActions.add(new TextViewDrawableColorFilterAction(parcel));
2239 case SetRemoteInputsAction.TAG:
2240 mActions.add(new SetRemoteInputsAction(parcel));
2242 case LayoutParamAction.TAG:
2243 mActions.add(new LayoutParamAction(parcel));
2246 throw new ActionException("Tag " + tag + " not found");
2251 // MODE_HAS_LANDSCAPE_AND_PORTRAIT
2252 mLandscape = new RemoteViews(parcel, mBitmapCache);
2253 mPortrait = new RemoteViews(parcel, mBitmapCache);
2254 mApplication = mPortrait.mApplication;
2255 mLayoutId = mPortrait.getLayoutId();
2258 // setup the memory usage statistics
2259 mMemoryUsageCounter = new MemoryUsageCounter();
2260 recalculateMemoryUsage();
2264 public RemoteViews clone() {
2265 Preconditions.checkState(mIsRoot, "RemoteView has been attached to another RemoteView. "
2266 + "May only clone the root of a RemoteView hierarchy.");
2268 Parcel p = Parcel.obtain();
2270 // Do not parcel the Bitmap cache - doing so creates an expensive copy of all bitmaps.
2271 // Instead pretend we're not owning the cache while parceling.
2273 writeToParcel(p, 0);
2274 p.setDataPosition(0);
2277 RemoteViews rv = new RemoteViews(p, mBitmapCache.clone());
2284 public String getPackage() {
2285 return (mApplication != null) ? mApplication.packageName : null;
2289 * Returns the layout id of the root layout associated with this RemoteViews. In the case
2290 * that the RemoteViews has both a landscape and portrait root, this will return the layout
2291 * id associated with the portrait layout.
2293 * @return the layout id.
2295 public int getLayoutId() {
2300 * This flag indicates whether this RemoteViews object is being created from a
2301 * RemoteViewsService for use as a child of a widget collection. This flag is used
2302 * to determine whether or not certain features are available, in particular,
2303 * setting on click extras and setting on click pending intents. The former is enabled,
2304 * and the latter disabled when this flag is true.
2306 void setIsWidgetCollectionChild(boolean isWidgetCollectionChild) {
2307 mIsWidgetCollectionChild = isWidgetCollectionChild;
2311 * Updates the memory usage statistics.
2313 private void recalculateMemoryUsage() {
2314 mMemoryUsageCounter.clear();
2316 if (!hasLandscapeAndPortraitLayouts()) {
2317 // Accumulate the memory usage for each action
2318 if (mActions != null) {
2319 final int count = mActions.size();
2320 for (int i= 0; i < count; ++i) {
2321 mActions.get(i).updateMemoryUsageEstimate(mMemoryUsageCounter);
2325 mBitmapCache.addBitmapMemory(mMemoryUsageCounter);
2328 mMemoryUsageCounter.increment(mLandscape.estimateMemoryUsage());
2329 mMemoryUsageCounter.increment(mPortrait.estimateMemoryUsage());
2330 mBitmapCache.addBitmapMemory(mMemoryUsageCounter);
2335 * Recursively sets BitmapCache in the hierarchy and update the bitmap ids.
2337 private void setBitmapCache(BitmapCache bitmapCache) {
2338 mBitmapCache = bitmapCache;
2339 if (!hasLandscapeAndPortraitLayouts()) {
2340 if (mActions != null) {
2341 final int count = mActions.size();
2342 for (int i= 0; i < count; ++i) {
2343 mActions.get(i).setBitmapCache(bitmapCache);
2347 mLandscape.setBitmapCache(bitmapCache);
2348 mPortrait.setBitmapCache(bitmapCache);
2353 * Returns an estimate of the bitmap heap memory usage for this RemoteViews.
2356 public int estimateMemoryUsage() {
2357 return mMemoryUsageCounter.getMemoryUsage();
2361 * Add an action to be executed on the remote side when apply is called.
2363 * @param a The action to add
2365 private void addAction(Action a) {
2366 if (hasLandscapeAndPortraitLayouts()) {
2367 throw new RuntimeException("RemoteViews specifying separate landscape and portrait" +
2368 " layouts cannot be modified. Instead, fully configure the landscape and" +
2369 " portrait layouts individually before constructing the combined layout.");
2371 if (mActions == null) {
2372 mActions = new ArrayList<Action>();
2376 // update the memory usage stats
2377 a.updateMemoryUsageEstimate(mMemoryUsageCounter);
2381 * Equivalent to calling {@link ViewGroup#addView(View)} after inflating the
2382 * given {@link RemoteViews}. This allows users to build "nested"
2383 * {@link RemoteViews}. In cases where consumers of {@link RemoteViews} may
2384 * recycle layouts, use {@link #removeAllViews(int)} to clear any existing
2387 * @param viewId The id of the parent {@link ViewGroup} to add child into.
2388 * @param nestedView {@link RemoteViews} that describes the child.
2390 public void addView(int viewId, RemoteViews nestedView) {
2391 addAction(new ViewGroupAction(viewId, nestedView));
2395 * Equivalent to calling {@link ViewGroup#removeAllViews()}.
2397 * @param viewId The id of the parent {@link ViewGroup} to remove all
2400 public void removeAllViews(int viewId) {
2401 addAction(new ViewGroupAction(viewId, null));
2405 * Equivalent to calling {@link AdapterViewAnimator#showNext()}
2407 * @param viewId The id of the view on which to call {@link AdapterViewAnimator#showNext()}
2409 public void showNext(int viewId) {
2410 addAction(new ReflectionActionWithoutParams(viewId, "showNext"));
2414 * Equivalent to calling {@link AdapterViewAnimator#showPrevious()}
2416 * @param viewId The id of the view on which to call {@link AdapterViewAnimator#showPrevious()}
2418 public void showPrevious(int viewId) {
2419 addAction(new ReflectionActionWithoutParams(viewId, "showPrevious"));
2423 * Equivalent to calling {@link AdapterViewAnimator#setDisplayedChild(int)}
2425 * @param viewId The id of the view on which to call
2426 * {@link AdapterViewAnimator#setDisplayedChild(int)}
2428 public void setDisplayedChild(int viewId, int childIndex) {
2429 setInt(viewId, "setDisplayedChild", childIndex);
2433 * Equivalent to calling View.setVisibility
2435 * @param viewId The id of the view whose visibility should change
2436 * @param visibility The new visibility for the view
2438 public void setViewVisibility(int viewId, int visibility) {
2439 setInt(viewId, "setVisibility", visibility);
2443 * Equivalent to calling TextView.setText
2445 * @param viewId The id of the view whose text should change
2446 * @param text The new text for the view
2448 public void setTextViewText(int viewId, CharSequence text) {
2449 setCharSequence(viewId, "setText", text);
2453 * Equivalent to calling {@link TextView#setTextSize(int, float)}
2455 * @param viewId The id of the view whose text size should change
2456 * @param units The units of size (e.g. COMPLEX_UNIT_SP)
2457 * @param size The size of the text
2459 public void setTextViewTextSize(int viewId, int units, float size) {
2460 addAction(new TextViewSizeAction(viewId, units, size));
2464 * Equivalent to calling
2465 * {@link TextView#setCompoundDrawablesWithIntrinsicBounds(int, int, int, int)}.
2467 * @param viewId The id of the view whose text should change
2468 * @param left The id of a drawable to place to the left of the text, or 0
2469 * @param top The id of a drawable to place above the text, or 0
2470 * @param right The id of a drawable to place to the right of the text, or 0
2471 * @param bottom The id of a drawable to place below the text, or 0
2473 public void setTextViewCompoundDrawables(int viewId, int left, int top, int right, int bottom) {
2474 addAction(new TextViewDrawableAction(viewId, false, left, top, right, bottom));
2478 * Equivalent to calling {@link
2479 * TextView#setCompoundDrawablesRelativeWithIntrinsicBounds(int, int, int, int)}.
2481 * @param viewId The id of the view whose text should change
2482 * @param start The id of a drawable to place before the text (relative to the
2483 * layout direction), or 0
2484 * @param top The id of a drawable to place above the text, or 0
2485 * @param end The id of a drawable to place after the text, or 0
2486 * @param bottom The id of a drawable to place below the text, or 0
2488 public void setTextViewCompoundDrawablesRelative(int viewId, int start, int top, int end, int bottom) {
2489 addAction(new TextViewDrawableAction(viewId, true, start, top, end, bottom));
2493 * Equivalent to applying a color filter on one of the drawables in
2494 * {@link android.widget.TextView#getCompoundDrawablesRelative()}.
2496 * @param viewId The id of the view whose text should change.
2497 * @param index The index of the drawable in the array of
2498 * {@link android.widget.TextView#getCompoundDrawablesRelative()} to set the color
2499 * filter on. Must be in [0, 3].
2500 * @param color The color of the color filter. See
2501 * {@link Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)}.
2502 * @param mode The mode of the color filter. See
2503 * {@link Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)}.
2506 public void setTextViewCompoundDrawablesRelativeColorFilter(int viewId,
2507 int index, int color, PorterDuff.Mode mode) {
2508 if (index < 0 || index >= 4) {
2509 throw new IllegalArgumentException("index must be in range [0, 3].");
2511 addAction(new TextViewDrawableColorFilterAction(viewId, true, index, color, mode));
2515 * Equivalent to calling {@link
2516 * TextView#setCompoundDrawablesWithIntrinsicBounds(Drawable, Drawable, Drawable, Drawable)}
2517 * using the drawables yielded by {@link Icon#loadDrawable(Context)}.
2519 * @param viewId The id of the view whose text should change
2520 * @param left an Icon to place to the left of the text, or 0
2521 * @param top an Icon to place above the text, or 0
2522 * @param right an Icon to place to the right of the text, or 0
2523 * @param bottom an Icon to place below the text, or 0
2527 public void setTextViewCompoundDrawables(int viewId, Icon left, Icon top, Icon right, Icon bottom) {
2528 addAction(new TextViewDrawableAction(viewId, false, left, top, right, bottom));
2532 * Equivalent to calling {@link
2533 * TextView#setCompoundDrawablesRelativeWithIntrinsicBounds(Drawable, Drawable, Drawable, Drawable)}
2534 * using the drawables yielded by {@link Icon#loadDrawable(Context)}.
2536 * @param viewId The id of the view whose text should change
2537 * @param start an Icon to place before the text (relative to the
2538 * layout direction), or 0
2539 * @param top an Icon to place above the text, or 0
2540 * @param end an Icon to place after the text, or 0
2541 * @param bottom an Icon to place below the text, or 0
2545 public void setTextViewCompoundDrawablesRelative(int viewId, Icon start, Icon top, Icon end, Icon bottom) {
2546 addAction(new TextViewDrawableAction(viewId, true, start, top, end, bottom));
2550 * Equivalent to calling ImageView.setImageResource
2552 * @param viewId The id of the view whose drawable should change
2553 * @param srcId The new resource id for the drawable
2555 public void setImageViewResource(int viewId, int srcId) {
2556 setInt(viewId, "setImageResource", srcId);
2560 * Equivalent to calling ImageView.setImageURI
2562 * @param viewId The id of the view whose drawable should change
2563 * @param uri The Uri for the image
2565 public void setImageViewUri(int viewId, Uri uri) {
2566 setUri(viewId, "setImageURI", uri);
2570 * Equivalent to calling ImageView.setImageBitmap
2572 * @param viewId The id of the view whose bitmap should change
2573 * @param bitmap The new Bitmap for the drawable
2575 public void setImageViewBitmap(int viewId, Bitmap bitmap) {
2576 setBitmap(viewId, "setImageBitmap", bitmap);
2580 * Equivalent to calling ImageView.setImageIcon
2582 * @param viewId The id of the view whose bitmap should change
2583 * @param icon The new Icon for the ImageView
2585 public void setImageViewIcon(int viewId, Icon icon) {
2586 setIcon(viewId, "setImageIcon", icon);
2590 * Equivalent to calling AdapterView.setEmptyView
2592 * @param viewId The id of the view on which to set the empty view
2593 * @param emptyViewId The view id of the empty view
2595 public void setEmptyView(int viewId, int emptyViewId) {
2596 addAction(new SetEmptyView(viewId, emptyViewId));
2600 * Equivalent to calling {@link Chronometer#setBase Chronometer.setBase},
2601 * {@link Chronometer#setFormat Chronometer.setFormat},
2602 * and {@link Chronometer#start Chronometer.start()} or
2603 * {@link Chronometer#stop Chronometer.stop()}.
2605 * @param viewId The id of the {@link Chronometer} to change
2606 * @param base The time at which the timer would have read 0:00. This
2607 * time should be based off of
2608 * {@link android.os.SystemClock#elapsedRealtime SystemClock.elapsedRealtime()}.
2609 * @param format The Chronometer format string, or null to
2610 * simply display the timer value.
2611 * @param started True if you want the clock to be started, false if not.
2613 * @see #setChronometerCountDown(int, boolean)
2615 public void setChronometer(int viewId, long base, String format, boolean started) {
2616 setLong(viewId, "setBase", base);
2617 setString(viewId, "setFormat", format);
2618 setBoolean(viewId, "setStarted", started);
2622 * Equivalent to calling {@link Chronometer#setCountDown(boolean) Chronometer.setCountDown} on
2623 * the chronometer with the given viewId.
2625 * @param viewId The id of the {@link Chronometer} to change
2626 * @param isCountDown True if you want the chronometer to count down to base instead of
2629 public void setChronometerCountDown(int viewId, boolean isCountDown) {
2630 setBoolean(viewId, "setCountDown", isCountDown);
2634 * Equivalent to calling {@link ProgressBar#setMax ProgressBar.setMax},
2635 * {@link ProgressBar#setProgress ProgressBar.setProgress}, and
2636 * {@link ProgressBar#setIndeterminate ProgressBar.setIndeterminate}
2638 * If indeterminate is true, then the values for max and progress are ignored.
2640 * @param viewId The id of the {@link ProgressBar} to change
2641 * @param max The 100% value for the progress bar
2642 * @param progress The current value of the progress bar.
2643 * @param indeterminate True if the progress bar is indeterminate,
2646 public void setProgressBar(int viewId, int max, int progress,
2647 boolean indeterminate) {
2648 setBoolean(viewId, "setIndeterminate", indeterminate);
2649 if (!indeterminate) {
2650 setInt(viewId, "setMax", max);
2651 setInt(viewId, "setProgress", progress);
2656 * Equivalent to calling
2657 * {@link android.view.View#setOnClickListener(android.view.View.OnClickListener)}
2658 * to launch the provided {@link PendingIntent}.
2660 * When setting the on-click action of items within collections (eg. {@link ListView},
2661 * {@link StackView} etc.), this method will not work. Instead, use {@link
2662 * RemoteViews#setPendingIntentTemplate(int, PendingIntent) in conjunction with
2663 * RemoteViews#setOnClickFillInIntent(int, Intent).
2665 * @param viewId The id of the view that will trigger the {@link PendingIntent} when clicked
2666 * @param pendingIntent The {@link PendingIntent} to send when user clicks
2668 public void setOnClickPendingIntent(int viewId, PendingIntent pendingIntent) {
2669 addAction(new SetOnClickPendingIntent(viewId, pendingIntent));
2673 * When using collections (eg. {@link ListView}, {@link StackView} etc.) in widgets, it is very
2674 * costly to set PendingIntents on the individual items, and is hence not permitted. Instead
2675 * this method should be used to set a single PendingIntent template on the collection, and
2676 * individual items can differentiate their on-click behavior using
2677 * {@link RemoteViews#setOnClickFillInIntent(int, Intent)}.
2679 * @param viewId The id of the collection who's children will use this PendingIntent template
2681 * @param pendingIntentTemplate The {@link PendingIntent} to be combined with extras specified
2682 * by a child of viewId and executed when that child is clicked
2684 public void setPendingIntentTemplate(int viewId, PendingIntent pendingIntentTemplate) {
2685 addAction(new SetPendingIntentTemplate(viewId, pendingIntentTemplate));
2689 * When using collections (eg. {@link ListView}, {@link StackView} etc.) in widgets, it is very
2690 * costly to set PendingIntents on the individual items, and is hence not permitted. Instead
2691 * a single PendingIntent template can be set on the collection, see {@link
2692 * RemoteViews#setPendingIntentTemplate(int, PendingIntent)}, and the individual on-click
2693 * action of a given item can be distinguished by setting a fillInIntent on that item. The
2694 * fillInIntent is then combined with the PendingIntent template in order to determine the final
2695 * intent which will be executed when the item is clicked. This works as follows: any fields
2696 * which are left blank in the PendingIntent template, but are provided by the fillInIntent
2697 * will be overwritten, and the resulting PendingIntent will be used.
2700 * of the PendingIntent template will then be filled in with the associated fields that are
2701 * set in fillInIntent. See {@link Intent#fillIn(Intent, int)} for more details.
2703 * @param viewId The id of the view on which to set the fillInIntent
2704 * @param fillInIntent The intent which will be combined with the parent's PendingIntent
2705 * in order to determine the on-click behavior of the view specified by viewId
2707 public void setOnClickFillInIntent(int viewId, Intent fillInIntent) {
2708 addAction(new SetOnClickFillInIntent(viewId, fillInIntent));
2713 * Equivalent to calling a combination of {@link Drawable#setAlpha(int)},
2714 * {@link Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)},
2715 * and/or {@link Drawable#setLevel(int)} on the {@link Drawable} of a given
2718 * You can omit specific calls by marking their values with null or -1.
2720 * @param viewId The id of the view that contains the target
2722 * @param targetBackground If true, apply these parameters to the
2723 * {@link Drawable} returned by
2724 * {@link android.view.View#getBackground()}. Otherwise, assume
2725 * the target view is an {@link ImageView} and apply them to
2726 * {@link ImageView#getDrawable()}.
2727 * @param alpha Specify an alpha value for the drawable, or -1 to leave
2729 * @param colorFilter Specify a color for a
2730 * {@link android.graphics.ColorFilter} for this drawable. This will be ignored if
2731 * {@code mode} is {@code null}.
2732 * @param mode Specify a PorterDuff mode for this drawable, or null to leave
2734 * @param level Specify the level for the drawable, or -1 to leave
2737 public void setDrawableParameters(int viewId, boolean targetBackground, int alpha,
2738 int colorFilter, PorterDuff.Mode mode, int level) {
2739 addAction(new SetDrawableParameters(viewId, targetBackground, alpha,
2740 colorFilter, mode, level));
2745 * Equivalent to calling {@link android.widget.ProgressBar#setProgressTintList}.
2747 * @param viewId The id of the view whose tint should change
2748 * @param tint the tint to apply, may be {@code null} to clear tint
2750 public void setProgressTintList(int viewId, ColorStateList tint) {
2751 addAction(new ReflectionAction(viewId, "setProgressTintList",
2752 ReflectionAction.COLOR_STATE_LIST, tint));
2757 * Equivalent to calling {@link android.widget.ProgressBar#setProgressBackgroundTintList}.
2759 * @param viewId The id of the view whose tint should change
2760 * @param tint the tint to apply, may be {@code null} to clear tint
2762 public void setProgressBackgroundTintList(int viewId, ColorStateList tint) {
2763 addAction(new ReflectionAction(viewId, "setProgressBackgroundTintList",
2764 ReflectionAction.COLOR_STATE_LIST, tint));
2769 * Equivalent to calling {@link android.widget.ProgressBar#setIndeterminateTintList}.
2771 * @param viewId The id of the view whose tint should change
2772 * @param tint the tint to apply, may be {@code null} to clear tint
2774 public void setProgressIndeterminateTintList(int viewId, ColorStateList tint) {
2775 addAction(new ReflectionAction(viewId, "setIndeterminateTintList",
2776 ReflectionAction.COLOR_STATE_LIST, tint));
2780 * Equivalent to calling {@link android.widget.TextView#setTextColor(int)}.
2782 * @param viewId The id of the view whose text color should change
2783 * @param color Sets the text color for all the states (normal, selected,
2784 * focused) to be this color.
2786 public void setTextColor(int viewId, @ColorInt int color) {
2787 setInt(viewId, "setTextColor", color);
2791 * Equivalent to calling {@link android.widget.AbsListView#setRemoteViewsAdapter(Intent)}.
2793 * @param appWidgetId The id of the app widget which contains the specified view. (This
2794 * parameter is ignored in this deprecated method)
2795 * @param viewId The id of the {@link AdapterView}
2796 * @param intent The intent of the service which will be
2797 * providing data to the RemoteViewsAdapter
2798 * @deprecated This method has been deprecated. See
2799 * {@link android.widget.RemoteViews#setRemoteAdapter(int, Intent)}
2802 public void setRemoteAdapter(int appWidgetId, int viewId, Intent intent) {
2803 setRemoteAdapter(viewId, intent);
2807 * Equivalent to calling {@link android.widget.AbsListView#setRemoteViewsAdapter(Intent)}.
2808 * Can only be used for App Widgets.
2810 * @param viewId The id of the {@link AdapterView}
2811 * @param intent The intent of the service which will be
2812 * providing data to the RemoteViewsAdapter
2814 public void setRemoteAdapter(int viewId, Intent intent) {
2815 addAction(new SetRemoteViewsAdapterIntent(viewId, intent));
2819 * Creates a simple Adapter for the viewId specified. The viewId must point to an AdapterView,
2820 * ie. {@link ListView}, {@link GridView}, {@link StackView} or {@link AdapterViewAnimator}.
2821 * This is a simpler but less flexible approach to populating collection widgets. Its use is
2822 * encouraged for most scenarios, as long as the total memory within the list of RemoteViews
2823 * is relatively small (ie. doesn't contain large or numerous Bitmaps, see {@link
2824 * RemoteViews#setImageViewBitmap}). In the case of numerous images, the use of API is still
2825 * possible by setting image URIs instead of Bitmaps, see {@link RemoteViews#setImageViewUri}.
2827 * This API is supported in the compatibility library for previous API levels, see
2828 * RemoteViewsCompat.
2830 * @param viewId The id of the {@link AdapterView}
2831 * @param list The list of RemoteViews which will populate the view specified by viewId.
2832 * @param viewTypeCount The maximum number of unique layout id's used to construct the list of
2833 * RemoteViews. This count cannot change during the life-cycle of a given widget, so this
2834 * parameter should account for the maximum possible number of types that may appear in the
2835 * See {@link Adapter#getViewTypeCount()}.
2839 public void setRemoteAdapter(int viewId, ArrayList<RemoteViews> list, int viewTypeCount) {
2840 addAction(new SetRemoteViewsAdapterList(viewId, list, viewTypeCount));
2844 * Equivalent to calling {@link android.widget.AbsListView#smoothScrollToPosition(int, int)}.
2846 * @param viewId The id of the view to change
2847 * @param position Scroll to this adapter position
2849 public void setScrollPosition(int viewId, int position) {
2850 setInt(viewId, "smoothScrollToPosition", position);
2854 * Equivalent to calling {@link android.widget.AbsListView#smoothScrollToPosition(int, int)}.
2856 * @param viewId The id of the view to change
2857 * @param offset Scroll by this adapter position offset
2859 public void setRelativeScrollPosition(int viewId, int offset) {
2860 setInt(viewId, "smoothScrollByOffset", offset);
2864 * Equivalent to calling {@link android.view.View#setPadding(int, int, int, int)}.
2866 * @param viewId The id of the view to change
2867 * @param left the left padding in pixels
2868 * @param top the top padding in pixels
2869 * @param right the right padding in pixels
2870 * @param bottom the bottom padding in pixels
2872 public void setViewPadding(int viewId, int left, int top, int right, int bottom) {
2873 addAction(new ViewPaddingAction(viewId, left, top, right, bottom));
2878 * Equivalent to calling {@link android.view.ViewGroup.MarginLayoutParams#setMarginEnd(int)}.
2879 * Only works if the {@link View#getLayoutParams()} supports margins.
2880 * Hidden for now since we don't want to support this for all different layout margins yet.
2882 * @param viewId The id of the view to change
2883 * @param endMarginDimen a dimen resource to read the margin from or 0 to clear the margin.
2885 public void setViewLayoutMarginEndDimen(int viewId, @DimenRes int endMarginDimen) {
2886 addAction(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_MARGIN_END_DIMEN,
2891 * Equivalent to setting {@link android.view.ViewGroup.MarginLayoutParams#bottomMargin}.
2893 * @param bottomMarginDimen a dimen resource to read the margin from or 0 to clear the margin.
2896 public void setViewLayoutMarginBottomDimen(int viewId, @DimenRes int bottomMarginDimen) {
2897 addAction(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_MARGIN_BOTTOM_DIMEN,
2898 bottomMarginDimen));
2902 * Equivalent to setting {@link android.view.ViewGroup.LayoutParams#width}.
2904 * @param layoutWidth one of 0, MATCH_PARENT or WRAP_CONTENT. Other sizes are not allowed
2905 * because they behave poorly when the density changes.
2908 public void setViewLayoutWidth(int viewId, int layoutWidth) {
2909 if (layoutWidth != 0 && layoutWidth != ViewGroup.LayoutParams.MATCH_PARENT
2910 && layoutWidth != ViewGroup.LayoutParams.WRAP_CONTENT) {
2911 throw new IllegalArgumentException("Only supports 0, WRAP_CONTENT and MATCH_PARENT");
2913 mActions.add(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_WIDTH, layoutWidth));
2917 * Call a method taking one boolean on a view in the layout for this RemoteViews.
2919 * @param viewId The id of the view on which to call the method.
2920 * @param methodName The name of the method to call.
2921 * @param value The value to pass to the method.
2923 public void setBoolean(int viewId, String methodName, boolean value) {
2924 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.BOOLEAN, value));
2928 * Call a method taking one byte on a view in the layout for this RemoteViews.
2930 * @param viewId The id of the view on which to call the method.
2931 * @param methodName The name of the method to call.
2932 * @param value The value to pass to the method.
2934 public void setByte(int viewId, String methodName, byte value) {
2935 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.BYTE, value));
2939 * Call a method taking one short on a view in the layout for this RemoteViews.
2941 * @param viewId The id of the view on which to call the method.
2942 * @param methodName The name of the method to call.
2943 * @param value The value to pass to the method.
2945 public void setShort(int viewId, String methodName, short value) {
2946 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.SHORT, value));
2950 * Call a method taking one int on a view in the layout for this RemoteViews.
2952 * @param viewId The id of the view on which to call the method.
2953 * @param methodName The name of the method to call.
2954 * @param value The value to pass to the method.
2956 public void setInt(int viewId, String methodName, int value) {
2957 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.INT, value));
2961 * Call a method taking one long on a view in the layout for this RemoteViews.
2963 * @param viewId The id of the view on which to call the method.
2964 * @param methodName The name of the method to call.
2965 * @param value The value to pass to the method.
2967 public void setLong(int viewId, String methodName, long value) {
2968 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.LONG, value));
2972 * Call a method taking one float on a view in the layout for this RemoteViews.
2974 * @param viewId The id of the view on which to call the method.
2975 * @param methodName The name of the method to call.
2976 * @param value The value to pass to the method.
2978 public void setFloat(int viewId, String methodName, float value) {
2979 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.FLOAT, value));
2983 * Call a method taking one double on a view in the layout for this RemoteViews.
2985 * @param viewId The id of the view on which to call the method.
2986 * @param methodName The name of the method to call.
2987 * @param value The value to pass to the method.
2989 public void setDouble(int viewId, String methodName, double value) {
2990 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.DOUBLE, value));
2994 * Call a method taking one char on a view in the layout for this RemoteViews.
2996 * @param viewId The id of the view on which to call the method.
2997 * @param methodName The name of the method to call.
2998 * @param value The value to pass to the method.
3000 public void setChar(int viewId, String methodName, char value) {
3001 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.CHAR, value));
3005 * Call a method taking one String on a view in the layout for this RemoteViews.
3007 * @param viewId The id of the view on which to call the method.
3008 * @param methodName The name of the method to call.
3009 * @param value The value to pass to the method.
3011 public void setString(int viewId, String methodName, String value) {
3012 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.STRING, value));
3016 * Call a method taking one CharSequence on a view in the layout for this RemoteViews.
3018 * @param viewId The id of the view on which to call the method.
3019 * @param methodName The name of the method to call.
3020 * @param value The value to pass to the method.
3022 public void setCharSequence(int viewId, String methodName, CharSequence value) {
3023 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.CHAR_SEQUENCE, value));
3027 * Call a method taking one Uri on a view in the layout for this RemoteViews.
3029 * @param viewId The id of the view on which to call the method.
3030 * @param methodName The name of the method to call.
3031 * @param value The value to pass to the method.
3033 public void setUri(int viewId, String methodName, Uri value) {
3034 if (value != null) {
3035 // Resolve any filesystem path before sending remotely
3036 value = value.getCanonicalUri();
3037 if (StrictMode.vmFileUriExposureEnabled()) {
3038 value.checkFileUriExposed("RemoteViews.setUri()");
3041 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.URI, value));
3045 * Call a method taking one Bitmap on a view in the layout for this RemoteViews.
3047 * <p class="note">The bitmap will be flattened into the parcel if this object is
3048 * sent across processes, so it may end up using a lot of memory, and may be fairly slow.</p>
3050 * @param viewId The id of the view on which to call the method.
3051 * @param methodName The name of the method to call.
3052 * @param value The value to pass to the method.
3054 public void setBitmap(int viewId, String methodName, Bitmap value) {
3055 addAction(new BitmapReflectionAction(viewId, methodName, value));
3059 * Call a method taking one Bundle on a view in the layout for this RemoteViews.
3061 * @param viewId The id of the view on which to call the method.
3062 * @param methodName The name of the method to call.
3063 * @param value The value to pass to the method.
3065 public void setBundle(int viewId, String methodName, Bundle value) {
3066 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.BUNDLE, value));
3070 * Call a method taking one Intent on a view in the layout for this RemoteViews.
3072 * @param viewId The id of the view on which to call the method.
3073 * @param methodName The name of the method to call.
3074 * @param value The {@link android.content.Intent} to pass the method.
3076 public void setIntent(int viewId, String methodName, Intent value) {
3077 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.INTENT, value));
3081 * Call a method taking one Icon on a view in the layout for this RemoteViews.
3083 * @param viewId The id of the view on which to call the method.
3084 * @param methodName The name of the method to call.
3085 * @param value The {@link android.graphics.drawable.Icon} to pass the method.
3087 public void setIcon(int viewId, String methodName, Icon value) {
3088 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.ICON, value));
3092 * Equivalent to calling View.setContentDescription(CharSequence).
3094 * @param viewId The id of the view whose content description should change.
3095 * @param contentDescription The new content description for the view.
3097 public void setContentDescription(int viewId, CharSequence contentDescription) {
3098 setCharSequence(viewId, "setContentDescription", contentDescription);
3102 * Equivalent to calling {@link android.view.View#setAccessibilityTraversalBefore(int)}.
3104 * @param viewId The id of the view whose before view in accessibility traversal to set.
3105 * @param nextId The id of the next in the accessibility traversal.
3107 public void setAccessibilityTraversalBefore(int viewId, int nextId) {
3108 setInt(viewId, "setAccessibilityTraversalBefore", nextId);
3112 * Equivalent to calling {@link android.view.View#setAccessibilityTraversalAfter(int)}.
3114 * @param viewId The id of the view whose after view in accessibility traversal to set.
3115 * @param nextId The id of the next in the accessibility traversal.
3117 public void setAccessibilityTraversalAfter(int viewId, int nextId) {
3118 setInt(viewId, "setAccessibilityTraversalAfter", nextId);
3122 * Equivalent to calling View.setLabelFor(int).
3124 * @param viewId The id of the view whose property to set.
3125 * @param labeledId The id of a view for which this view serves as a label.
3127 public void setLabelFor(int viewId, int labeledId) {
3128 setInt(viewId, "setLabelFor", labeledId);
3131 private RemoteViews getRemoteViewsToApply(Context context) {
3132 if (hasLandscapeAndPortraitLayouts()) {
3133 int orientation = context.getResources().getConfiguration().orientation;
3134 if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
3144 * Inflates the view hierarchy represented by this object and applies
3145 * all of the actions.
3147 * <p><strong>Caller beware: this may throw</strong>
3149 * @param context Default context to use
3150 * @param parent Parent that the resulting view hierarchy will be attached to. This method
3151 * does <strong>not</strong> attach the hierarchy. The caller should do so when appropriate.
3152 * @return The inflated view hierarchy
3154 public View apply(Context context, ViewGroup parent) {
3155 return apply(context, parent, null);
3159 public View apply(Context context, ViewGroup parent, OnClickHandler handler) {
3160 RemoteViews rvToApply = getRemoteViewsToApply(context);
3162 View result = inflateView(context, rvToApply, parent);
3163 loadTransitionOverride(context, handler);
3165 rvToApply.performApply(result, parent, handler);
3170 private View inflateView(Context context, RemoteViews rv, ViewGroup parent) {
3171 // RemoteViews may be built by an application installed in another
3172 // user. So build a context that loads resources from that user but
3173 // still returns the current users userId so settings like data / time formats
3174 // are loaded without requiring cross user persmissions.
3175 final Context contextForResources = getContextForResources(context);
3176 Context inflationContext = new ContextWrapper(context) {
3178 public Resources getResources() {
3179 return contextForResources.getResources();
3182 public Resources.Theme getTheme() {
3183 return contextForResources.getTheme();
3186 public String getPackageName() {
3187 return contextForResources.getPackageName();
3191 LayoutInflater inflater = (LayoutInflater)
3192 context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
3194 // Clone inflater so we load resources from correct context and
3195 // we don't add a filter to the static version returned by getSystemService.
3196 inflater = inflater.cloneInContext(inflationContext);
3197 inflater.setFilter(this);
3198 return inflater.inflate(rv.getLayoutId(), parent, false);
3201 private static void loadTransitionOverride(Context context,
3202 RemoteViews.OnClickHandler handler) {
3203 if (handler != null && context.getResources().getBoolean(
3204 com.android.internal.R.bool.config_overrideRemoteViewsActivityTransition)) {
3205 TypedArray windowStyle = context.getTheme().obtainStyledAttributes(
3206 com.android.internal.R.styleable.Window);
3207 int windowAnimations = windowStyle.getResourceId(
3208 com.android.internal.R.styleable.Window_windowAnimationStyle, 0);
3209 TypedArray windowAnimationStyle = context.obtainStyledAttributes(
3210 windowAnimations, com.android.internal.R.styleable.WindowAnimation);
3211 handler.setEnterAnimationId(windowAnimationStyle.getResourceId(
3212 com.android.internal.R.styleable.
3213 WindowAnimation_activityOpenRemoteViewsEnterAnimation, 0));
3214 windowStyle.recycle();
3215 windowAnimationStyle.recycle();
3220 * Implement this interface to receive a callback when
3221 * {@link #applyAsync} or {@link #reapplyAsync} is finished.
3224 public interface OnViewAppliedListener {
3225 void onViewApplied(View v);
3227 void onError(Exception e);
3231 * Applies the views asynchronously, moving as much of the task on the background
3232 * thread as possible.
3234 * @see {@link #apply(Context, ViewGroup)}
3235 * @param context Default context to use
3236 * @param parent Parent that the resulting view hierarchy will be attached to. This method
3237 * does <strong>not</strong> attach the hierarchy. The caller should do so when appropriate.
3238 * @param listener the callback to run when all actions have been applied. May be null.
3239 * @param executor The executor to use. If null {@link AsyncTask#THREAD_POOL_EXECUTOR} is used.
3240 * @return CancellationSignal
3243 public CancellationSignal applyAsync(
3244 Context context, ViewGroup parent, Executor executor, OnViewAppliedListener listener) {
3245 return applyAsync(context, parent, executor, listener, null);
3248 private CancellationSignal startTaskOnExecutor(AsyncApplyTask task, Executor executor) {
3249 CancellationSignal cancelSignal = new CancellationSignal();
3250 cancelSignal.setOnCancelListener(task);
3252 task.executeOnExecutor(executor == null ? AsyncTask.THREAD_POOL_EXECUTOR : executor);
3253 return cancelSignal;
3257 public CancellationSignal applyAsync(Context context, ViewGroup parent,
3258 Executor executor, OnViewAppliedListener listener, OnClickHandler handler) {
3259 return startTaskOnExecutor(getAsyncApplyTask(context, parent, listener, handler), executor);
3262 private AsyncApplyTask getAsyncApplyTask(Context context, ViewGroup parent,
3263 OnViewAppliedListener listener, OnClickHandler handler) {
3264 return new AsyncApplyTask(getRemoteViewsToApply(context), parent, context, listener,
3268 private class AsyncApplyTask extends AsyncTask<Void, Void, ViewTree>
3269 implements CancellationSignal.OnCancelListener {
3270 final RemoteViews mRV;
3271 final ViewGroup mParent;
3272 final Context mContext;
3273 final OnViewAppliedListener mListener;
3274 final OnClickHandler mHandler;
3276 private View mResult;
3277 private ViewTree mTree;
3278 private Action[] mActions;
3279 private Exception mError;
3281 private AsyncApplyTask(
3282 RemoteViews rv, ViewGroup parent, Context context, OnViewAppliedListener listener,
3283 OnClickHandler handler, View result) {
3287 mListener = listener;
3291 loadTransitionOverride(context, handler);
3295 protected ViewTree doInBackground(Void... params) {
3297 if (mResult == null) {
3298 mResult = inflateView(mContext, mRV, mParent);
3301 mTree = new ViewTree(mResult);
3302 if (mRV.mActions != null) {
3303 int count = mRV.mActions.size();
3304 mActions = new Action[count];
3305 for (int i = 0; i < count && !isCancelled(); i++) {
3306 // TODO: check if isCanclled in nested views.
3307 mActions[i] = mRV.mActions.get(i).initActionAsync(mTree, mParent, mHandler);
3313 } catch (Exception e) {
3320 protected void onPostExecute(ViewTree viewTree) {
3321 if (mError == null) {
3323 if (mActions != null) {
3324 OnClickHandler handler = mHandler == null
3325 ? DEFAULT_ON_CLICK_HANDLER : mHandler;
3326 for (Action a : mActions) {
3327 a.apply(viewTree.mRoot, mParent, handler);
3330 } catch (Exception e) {
3335 if (mListener != null) {
3336 if (mError != null) {
3337 mListener.onError(mError);
3339 mListener.onViewApplied(viewTree.mRoot);
3341 } else if (mError != null) {
3342 if (mError instanceof ActionException) {
3343 throw (ActionException) mError;
3345 throw new ActionException(mError);
3351 public void onCancel() {
3357 * Applies all of the actions to the provided view.
3359 * <p><strong>Caller beware: this may throw</strong>
3361 * @param v The view to apply the actions to. This should be the result of
3362 * the {@link #apply(Context,ViewGroup)} call.
3364 public void reapply(Context context, View v) {
3365 reapply(context, v, null);
3369 public void reapply(Context context, View v, OnClickHandler handler) {
3370 RemoteViews rvToApply = getRemoteViewsToApply(context);
3372 // In the case that a view has this RemoteViews applied in one orientation, is persisted
3373 // across orientation change, and has the RemoteViews re-applied in the new orientation,
3374 // we throw an exception, since the layouts may be completely unrelated.
3375 if (hasLandscapeAndPortraitLayouts()) {
3376 if (v.getId() != rvToApply.getLayoutId()) {
3377 throw new RuntimeException("Attempting to re-apply RemoteViews to a view that" +
3378 " that does not share the same root layout id.");
3382 rvToApply.performApply(v, (ViewGroup) v.getParent(), handler);
3386 * Applies all the actions to the provided view, moving as much of the task on the background
3387 * thread as possible.
3389 * @see {@link #reapply(Context, View)}
3390 * @param context Default context to use
3391 * @param v The view to apply the actions to. This should be the result of
3392 * the {@link #apply(Context,ViewGroup)} call.
3393 * @param listener the callback to run when all actions have been applied. May be null.
3394 * @param executor The executor to use. If null {@link AsyncTask#THREAD_POOL_EXECUTOR} is used
3395 * @return CancellationSignal
3398 public CancellationSignal reapplyAsync(
3399 Context context, View v, Executor executor, OnViewAppliedListener listener) {
3400 return reapplyAsync(context, v, executor, listener, null);
3404 public CancellationSignal reapplyAsync(Context context, View v, Executor executor,
3405 OnViewAppliedListener listener, OnClickHandler handler) {
3406 RemoteViews rvToApply = getRemoteViewsToApply(context);
3408 // In the case that a view has this RemoteViews applied in one orientation, is persisted
3409 // across orientation change, and has the RemoteViews re-applied in the new orientation,
3410 // we throw an exception, since the layouts may be completely unrelated.
3411 if (hasLandscapeAndPortraitLayouts()) {
3412 if (v.getId() != rvToApply.getLayoutId()) {
3413 throw new RuntimeException("Attempting to re-apply RemoteViews to a view that" +
3414 " that does not share the same root layout id.");
3418 return startTaskOnExecutor(new AsyncApplyTask(rvToApply, (ViewGroup) v.getParent(),
3419 context, listener, handler, v), executor);
3422 private void performApply(View v, ViewGroup parent, OnClickHandler handler) {
3423 if (mActions != null) {
3424 handler = handler == null ? DEFAULT_ON_CLICK_HANDLER : handler;
3425 final int count = mActions.size();
3426 for (int i = 0; i < count; i++) {
3427 Action a = mActions.get(i);
3428 a.apply(v, parent, handler);
3433 private Context getContextForResources(Context context) {
3434 if (mApplication != null) {
3435 if (context.getUserId() == UserHandle.getUserId(mApplication.uid)
3436 && context.getPackageName().equals(mApplication.packageName)) {
3440 return context.createApplicationContext(mApplication,
3441 Context.CONTEXT_RESTRICTED);
3442 } catch (NameNotFoundException e) {
3443 Log.e(LOG_TAG, "Package name " + mApplication.packageName + " not found");
3451 * Returns the number of actions in this RemoteViews. Can be used as a sequence number.
3455 public int getSequenceNumber() {
3456 return (mActions == null) ? 0 : mActions.size();
3460 * Used to restrict the views which can be inflated
3462 * @see android.view.LayoutInflater.Filter#onLoadClass(java.lang.Class)
3464 public boolean onLoadClass(Class clazz) {
3465 return clazz.isAnnotationPresent(RemoteView.class);
3468 public int describeContents() {
3472 public void writeToParcel(Parcel dest, int flags) {
3473 if (!hasLandscapeAndPortraitLayouts()) {
3474 dest.writeInt(MODE_NORMAL);
3475 // We only write the bitmap cache if we are the root RemoteViews, as this cache
3476 // is shared by all children.
3478 mBitmapCache.writeBitmapsToParcel(dest, flags);
3480 dest.writeParcelable(mApplication, flags);
3481 dest.writeInt(mLayoutId);
3482 dest.writeInt(mIsWidgetCollectionChild ? 1 : 0);
3484 if (mActions != null) {
3485 count = mActions.size();
3489 dest.writeInt(count);
3490 for (int i=0; i<count; i++) {
3491 Action a = mActions.get(i);
3492 a.writeToParcel(dest, 0);
3495 dest.writeInt(MODE_HAS_LANDSCAPE_AND_PORTRAIT);
3496 // We only write the bitmap cache if we are the root RemoteViews, as this cache
3497 // is shared by all children.
3499 mBitmapCache.writeBitmapsToParcel(dest, flags);
3501 mLandscape.writeToParcel(dest, flags);
3502 mPortrait.writeToParcel(dest, flags);
3506 private static ApplicationInfo getApplicationInfo(String packageName, int userId) {
3507 if (packageName == null) {
3511 // Get the application for the passed in package and user.
3512 Application application = ActivityThread.currentApplication();
3513 if (application == null) {
3514 throw new IllegalStateException("Cannot create remote views out of an aplication.");
3517 ApplicationInfo applicationInfo = application.getApplicationInfo();
3518 if (UserHandle.getUserId(applicationInfo.uid) != userId
3519 || !applicationInfo.packageName.equals(packageName)) {
3521 Context context = application.getBaseContext().createPackageContextAsUser(
3522 packageName, 0, new UserHandle(userId));
3523 applicationInfo = context.getApplicationInfo();
3524 } catch (NameNotFoundException nnfe) {
3525 throw new IllegalArgumentException("No such package " + packageName);
3529 return applicationInfo;
3533 * Parcelable.Creator that instantiates RemoteViews objects
3535 public static final Parcelable.Creator<RemoteViews> CREATOR = new Parcelable.Creator<RemoteViews>() {
3536 public RemoteViews createFromParcel(Parcel parcel) {
3537 return new RemoteViews(parcel);
3540 public RemoteViews[] newArray(int size) {
3541 return new RemoteViews[size];
3546 * A representation of the view hierarchy. Only views which have a valid ID are added
3547 * and can be searched.
3549 private static class ViewTree {
3550 private final View mRoot;
3552 private ArrayList<ViewTree> mChildren;
3554 private ViewTree(View root) {
3558 public void createTree() {
3559 if (mChildren != null) {
3563 mChildren = new ArrayList<>();
3564 if (mRoot instanceof ViewGroup && mRoot.isRootNamespace()) {
3565 ViewGroup vg = (ViewGroup) mRoot;
3566 int count = vg.getChildCount();
3567 for (int i = 0; i < count; i++) {
3568 addViewChild(vg.getChildAt(i));
3573 public ViewTree findViewTreeById(int id) {
3574 if (mRoot.getId() == id) {
3577 if (mChildren == null) {
3580 for (ViewTree tree : mChildren) {
3581 ViewTree result = tree.findViewTreeById(id);
3582 if (result != null) {
3589 public View findViewById(int id) {
3590 if (mChildren == null) {
3591 return mRoot.findViewById(id);
3593 ViewTree tree = findViewTreeById(id);
3594 return tree == null ? null : tree.mRoot;
3597 public void addChild(ViewTree child) {
3598 if (mChildren == null) {
3599 mChildren = new ArrayList<>();
3602 mChildren.add(child);
3605 private void addViewChild(View v) {
3606 final ViewTree target;
3608 // If the view has a valid id, i.e., if can be found using findViewById, add it to the
3609 // tree, otherwise skip this view and add its children instead.
3610 if (v.getId() != 0) {
3611 ViewTree tree = new ViewTree(v);
3612 mChildren.add(tree);
3618 if (v instanceof ViewGroup && v.isRootNamespace()) {
3619 if (target.mChildren == null) {
3620 target.mChildren = new ArrayList<>();
3621 ViewGroup vg = (ViewGroup) v;
3622 int count = vg.getChildCount();
3623 for (int i = 0; i < count; i++) {
3624 target.addViewChild(vg.getChildAt(i));