/*
 * Copyright (C) 2007 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.widget;

import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;

import android.annotation.ColorInt;
import android.annotation.DimenRes;
import android.annotation.NonNull;
import android.annotation.StyleRes;
import android.app.ActivityOptions;
import android.app.ActivityThread;
import android.app.Application;
import android.app.PendingIntent;
import android.app.RemoteInput;
import android.appwidget.AppWidgetHostView;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
import android.content.IntentSender;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.ColorStateList;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.Process;
import android.os.StrictMode;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
import android.view.LayoutInflater.Filter;
import android.view.RemotableViewMethod;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.ViewStub;
import android.widget.AdapterView.OnItemClickListener;

import com.android.internal.R;
import com.android.internal.util.NotificationColorUtil;
import com.android.internal.util.Preconditions;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Stack;
import java.util.concurrent.Executor;
import java.util.function.Consumer;

A class that describes a view hierarchy that can be displayed in another process. The hierarchy is inflated from a layout resource file, and this class provides some basic operations for modifying the content of the inflated hierarchy.

RemoteViews is limited to support for the following layouts:

And the following widgets:

Descendants of these classes are not supported.

/** * A class that describes a view hierarchy that can be displayed in * another process. The hierarchy is inflated from a layout resource * file, and this class provides some basic operations for modifying * the content of the inflated hierarchy. * * <p>{@code RemoteViews} is limited to support for the following layouts:</p> * <ul> * <li>{@link android.widget.AdapterViewFlipper}</li> * <li>{@link android.widget.FrameLayout}</li> * <li>{@link android.widget.GridLayout}</li> * <li>{@link android.widget.GridView}</li> * <li>{@link android.widget.LinearLayout}</li> * <li>{@link android.widget.ListView}</li> * <li>{@link android.widget.RelativeLayout}</li> * <li>{@link android.widget.StackView}</li> * <li>{@link android.widget.ViewFlipper}</li> * </ul> * <p>And the following widgets:</p> * <ul> * <li>{@link android.widget.AnalogClock}</li> * <li>{@link android.widget.Button}</li> * <li>{@link android.widget.Chronometer}</li> * <li>{@link android.widget.ImageButton}</li> * <li>{@link android.widget.ImageView}</li> * <li>{@link android.widget.ProgressBar}</li> * <li>{@link android.widget.TextClock}</li> * <li>{@link android.widget.TextView}</li> * </ul> * <p>Descendants of these classes are not supported.</p> */
public class RemoteViews implements Parcelable, Filter { private static final String LOG_TAG = "RemoteViews";
The intent extra that contains the appWidgetId.
@hide
/** * The intent extra that contains the appWidgetId. * @hide */
static final String EXTRA_REMOTEADAPTER_APPWIDGET_ID = "remoteAdapterAppWidgetId";
Maximum depth of nested views calls from addView(int, RemoteViews) and RemoteViews(RemoteViews, RemoteViews).
/** * Maximum depth of nested views calls from {@link #addView(int, RemoteViews)} and * {@link #RemoteViews(RemoteViews, RemoteViews)}. */
private static final int MAX_NESTED_VIEWS = 10; // The unique identifiers for each custom {@link Action}. private static final int SET_ON_CLICK_PENDING_INTENT_TAG = 1; private static final int REFLECTION_ACTION_TAG = 2; private static final int SET_DRAWABLE_TINT_TAG = 3; private static final int VIEW_GROUP_ACTION_ADD_TAG = 4; private static final int VIEW_CONTENT_NAVIGATION_TAG = 5; private static final int SET_EMPTY_VIEW_ACTION_TAG = 6; private static final int VIEW_GROUP_ACTION_REMOVE_TAG = 7; private static final int SET_PENDING_INTENT_TEMPLATE_TAG = 8; private static final int SET_ON_CLICK_FILL_IN_INTENT_TAG = 9; private static final int SET_REMOTE_VIEW_ADAPTER_INTENT_TAG = 10; private static final int TEXT_VIEW_DRAWABLE_ACTION_TAG = 11; private static final int BITMAP_REFLECTION_ACTION_TAG = 12; private static final int TEXT_VIEW_SIZE_ACTION_TAG = 13; private static final int VIEW_PADDING_ACTION_TAG = 14; private static final int SET_REMOTE_VIEW_ADAPTER_LIST_TAG = 15; private static final int SET_REMOTE_INPUTS_ACTION_TAG = 18; private static final int LAYOUT_PARAM_ACTION_TAG = 19; private static final int OVERRIDE_TEXT_COLORS_TAG = 20;
Application that hosts the remote views.
@hide
/** * Application that hosts the remote views. * * @hide */
public ApplicationInfo mApplication;
The resource ID of the layout file. (Added to the parcel)
/** * The resource ID of the layout file. (Added to the parcel) */
private final int mLayoutId;
An array of actions to perform on the view tree once it has been inflated
/** * An array of actions to perform on the view tree once it has been * inflated */
private ArrayList<Action> mActions;
Maps bitmaps to unique indicies to avoid Bitmap duplication.
/** * Maps bitmaps to unique indicies to avoid Bitmap duplication. */
private BitmapCache mBitmapCache;
Indicates whether or not this RemoteViews object is contained as a child of any other RemoteViews.
/** * Indicates whether or not this RemoteViews object is contained as a child of any other * RemoteViews. */
private boolean mIsRoot = true;
Optional theme resource id applied in inflateView(). When 0, Theme.DeviceDefault will be used.
/** * Optional theme resource id applied in inflateView(). When 0, Theme.DeviceDefault will be * used. */
private int mApplyThemeResId;
Whether reapply is disallowed on this remoteview. This maybe be true if some actions modify the layout in a way that isn't recoverable, since views are being removed.
/** * Whether reapply is disallowed on this remoteview. This maybe be true if some actions modify * the layout in a way that isn't recoverable, since views are being removed. */
private boolean mReapplyDisallowed;
Constants to whether or not this RemoteViews is composed of a landscape and portrait RemoteViews.
/** * Constants to whether or not this RemoteViews is composed of a landscape and portrait * RemoteViews. */
private static final int MODE_NORMAL = 0; private static final int MODE_HAS_LANDSCAPE_AND_PORTRAIT = 1;
Used in conjunction with the special constructor RemoteViews(RemoteViews, RemoteViews) to keep track of the landscape and portrait RemoteViews.
/** * Used in conjunction with the special constructor * {@link #RemoteViews(RemoteViews, RemoteViews)} to keep track of the landscape and portrait * RemoteViews. */
private RemoteViews mLandscape = null; private RemoteViews mPortrait = null;
This flag indicates whether this RemoteViews object is being created from a RemoteViewsService for use as a child of a widget collection. This flag is used to determine whether or not certain features are available, in particular, setting on click extras and setting on click pending intents. The former is enabled, and the latter disabled when this flag is true.
/** * This flag indicates whether this RemoteViews object is being created from a * RemoteViewsService for use as a child of a widget collection. This flag is used * to determine whether or not certain features are available, in particular, * setting on click extras and setting on click pending intents. The former is enabled, * and the latter disabled when this flag is true. */
private boolean mIsWidgetCollectionChild = false;
Class cookies of the Parcel this instance was read from.
/** Class cookies of the Parcel this instance was read from. */
private final Map<Class, Object> mClassCookies; private static final OnClickHandler DEFAULT_ON_CLICK_HANDLER = new OnClickHandler(); private static final ArrayMap<MethodKey, MethodArgs> sMethods = new ArrayMap<>();
This key is used to perform lookups in sMethods without causing allocations.
/** * This key is used to perform lookups in sMethods without causing allocations. */
private static final MethodKey sLookupKey = new MethodKey();
@hide
/** * @hide */
public void setRemoteInputs(int viewId, RemoteInput[] remoteInputs) { mActions.add(new SetRemoteInputsAction(viewId, remoteInputs)); }
Reduces all images and ensures that they are all below the given sizes.
Params:
  • maxWidth – the maximum width allowed
  • maxHeight – the maximum height allowed
@hide
/** * Reduces all images and ensures that they are all below the given sizes. * * @param maxWidth the maximum width allowed * @param maxHeight the maximum height allowed * * @hide */
public void reduceImageSizes(int maxWidth, int maxHeight) { ArrayList<Bitmap> cache = mBitmapCache.mBitmaps; for (int i = 0; i < cache.size(); i++) { Bitmap bitmap = cache.get(i); cache.set(i, Icon.scaleDownIfNecessary(bitmap, maxWidth, maxHeight)); } }
Override all text colors in this layout and replace them by the given text color.
Params:
  • textColor – The color to use.
@hide
/** * Override all text colors in this layout and replace them by the given text color. * * @param textColor The color to use. * * @hide */
public void overrideTextColors(int textColor) { addAction(new OverrideTextColorsAction(textColor)); }
Set that it is disallowed to reapply another remoteview with the same layout as this view. This should be done if an action is destroying the view tree of the base layout.
@hide
/** * Set that it is disallowed to reapply another remoteview with the same layout as this view. * This should be done if an action is destroying the view tree of the base layout. * * @hide */
public void setReapplyDisallowed() { mReapplyDisallowed = true; }
Returns:Whether it is disallowed to reapply another remoteview with the same layout as this view. True if this remoteview has actions that destroyed view tree of the base layout.
@hide
/** * @return Whether it is disallowed to reapply another remoteview with the same layout as this * view. True if this remoteview has actions that destroyed view tree of the base layout. * * @hide */
public boolean isReapplyDisallowed() { return mReapplyDisallowed; }
Stores information related to reflection method lookup.
/** * Stores information related to reflection method lookup. */
static class MethodKey { public Class targetClass; public Class paramClass; public String methodName; @Override public boolean equals(Object o) { if (!(o instanceof MethodKey)) { return false; } MethodKey p = (MethodKey) o; return Objects.equals(p.targetClass, targetClass) && Objects.equals(p.paramClass, paramClass) && Objects.equals(p.methodName, methodName); } @Override public int hashCode() { return Objects.hashCode(targetClass) ^ Objects.hashCode(paramClass) ^ Objects.hashCode(methodName); } public void set(Class targetClass, Class paramClass, String methodName) { this.targetClass = targetClass; this.paramClass = paramClass; this.methodName = methodName; } }
Stores information related to reflection method lookup result.
/** * Stores information related to reflection method lookup result. */
static class MethodArgs { public MethodHandle syncMethod; public MethodHandle asyncMethod; public String asyncMethodName; }
This annotation indicates that a subclass of View is allowed to be used with the RemoteViews mechanism.
/** * This annotation indicates that a subclass of View is allowed to be used * with the {@link RemoteViews} mechanism. */
@Target({ ElementType.TYPE }) @Retention(RetentionPolicy.RUNTIME) public @interface RemoteView { }
Exception to send when something goes wrong executing an action
/** * Exception to send when something goes wrong executing an action * */
public static class ActionException extends RuntimeException { public ActionException(Exception ex) { super(ex); } public ActionException(String message) { super(message); }
@hide
/** * @hide */
public ActionException(Throwable t) { super(t); } }
@hide
/** @hide */
public static class OnClickHandler { private int mEnterAnimationId; public boolean onClickHandler(View view, PendingIntent pendingIntent, Intent fillInIntent) { return onClickHandler(view, pendingIntent, fillInIntent, WINDOWING_MODE_UNDEFINED); } public boolean onClickHandler(View view, PendingIntent pendingIntent, Intent fillInIntent, int windowingMode) { try { // TODO: Unregister this handler if PendingIntent.FLAG_ONE_SHOT? Context context = view.getContext(); ActivityOptions opts; if (mEnterAnimationId != 0) { opts = ActivityOptions.makeCustomAnimation(context, mEnterAnimationId, 0); } else { opts = ActivityOptions.makeBasic(); } if (windowingMode != WINDOWING_MODE_UNDEFINED) { opts.setLaunchWindowingMode(windowingMode); } context.startIntentSender( pendingIntent.getIntentSender(), fillInIntent, Intent.FLAG_ACTIVITY_NEW_TASK, Intent.FLAG_ACTIVITY_NEW_TASK, 0, opts.toBundle()); } catch (IntentSender.SendIntentException e) { android.util.Log.e(LOG_TAG, "Cannot send pending intent: ", e); return false; } catch (Exception e) { android.util.Log.e(LOG_TAG, "Cannot send pending intent due to " + "unknown exception: ", e); return false; } return true; } public void setEnterAnimationId(int enterAnimationId) { mEnterAnimationId = enterAnimationId; } }
Base class for all actions that can be performed on an inflated view. SUBCLASSES MUST BE IMMUTABLE SO CLONE WORKS!!!!!
/** * Base class for all actions that can be performed on an * inflated view. * * SUBCLASSES MUST BE IMMUTABLE SO CLONE WORKS!!!!! */
private abstract static class Action implements Parcelable { public abstract void apply(View root, ViewGroup rootParent, OnClickHandler handler) throws ActionException; public static final int MERGE_REPLACE = 0; public static final int MERGE_APPEND = 1; public static final int MERGE_IGNORE = 2; public int describeContents() { return 0; } public void setBitmapCache(BitmapCache bitmapCache) { // Do nothing } public int mergeBehavior() { return MERGE_REPLACE; } public abstract int getActionTag(); public String getUniqueKey() { return (getActionTag() + "_" + viewId); }
This is called on the background thread. It should perform any non-ui computations and return the final action which will run on the UI thread. Override this if some of the tasks can be performed async.
/** * This is called on the background thread. It should perform any non-ui computations * and return the final action which will run on the UI thread. * Override this if some of the tasks can be performed async. */
public Action initActionAsync(ViewTree root, ViewGroup rootParent, OnClickHandler handler) { return this; } public boolean prefersAsyncApply() { return false; }
Overridden by subclasses which have (or inherit) an ApplicationInfo instance as member variable
/** * Overridden by subclasses which have (or inherit) an ApplicationInfo instance * as member variable */
public boolean hasSameAppInfo(ApplicationInfo parentInfo) { return true; } public void visitUris(@NonNull Consumer<Uri> visitor) { // Nothing to visit by default } int viewId; }
Action class used during async inflation of RemoteViews. Subclasses are not parcelable.
/** * Action class used during async inflation of RemoteViews. Subclasses are not parcelable. */
private static abstract class RuntimeAction extends Action { @Override public final int getActionTag() { return 0; } @Override public final void writeToParcel(Parcel dest, int flags) { throw new UnsupportedOperationException(); } } // Constant used during async execution. It is not parcelable. private static final Action ACTION_NOOP = new RuntimeAction() { @Override public void apply(View root, ViewGroup rootParent, OnClickHandler handler) { } };
Merges the passed RemoteViews actions with this RemoteViews actions according to action-specific merge rules.
Params:
  • newRv –
@hide
/** * Merges the passed RemoteViews actions with this RemoteViews actions according to * action-specific merge rules. * * @param newRv * * @hide */
public void mergeRemoteViews(RemoteViews newRv) { if (newRv == null) return; // We first copy the new RemoteViews, as the process of merging modifies the way the actions // reference the bitmap cache. We don't want to modify the object as it may need to // be merged and applied multiple times. RemoteViews copy = new RemoteViews(newRv); HashMap<String, Action> map = new HashMap<String, Action>(); if (mActions == null) { mActions = new ArrayList<Action>(); } int count = mActions.size(); for (int i = 0; i < count; i++) { Action a = mActions.get(i); map.put(a.getUniqueKey(), a); } ArrayList<Action> newActions = copy.mActions; if (newActions == null) return; count = newActions.size(); for (int i = 0; i < count; i++) { Action a = newActions.get(i); String key = newActions.get(i).getUniqueKey(); int mergeBehavior = newActions.get(i).mergeBehavior(); if (map.containsKey(key) && mergeBehavior == Action.MERGE_REPLACE) { mActions.remove(map.get(key)); map.remove(key); } // If the merge behavior is ignore, we don't bother keeping the extra action if (mergeBehavior == Action.MERGE_REPLACE || mergeBehavior == Action.MERGE_APPEND) { mActions.add(a); } } // Because pruning can remove the need for bitmaps, we reconstruct the bitmap cache mBitmapCache = new BitmapCache(); setBitmapCache(mBitmapCache); }
Note all Uri that are referenced internally, with the expectation that Uri permission grants will need to be issued to ensure the recipient of this object is able to render its contents.
@hide
/** * Note all {@link Uri} that are referenced internally, with the expectation * that Uri permission grants will need to be issued to ensure the recipient * of this object is able to render its contents. * * @hide */
public void visitUris(@NonNull Consumer<Uri> visitor) { if (mActions != null) { for (int i = 0; i < mActions.size(); i++) { mActions.get(i).visitUris(visitor); } } } private static void visitIconUri(Icon icon, @NonNull Consumer<Uri> visitor) { if (icon != null && icon.getType() == Icon.TYPE_URI) { visitor.accept(icon.getUri()); } } private static class RemoteViewsContextWrapper extends ContextWrapper { private final Context mContextForResources; RemoteViewsContextWrapper(Context context, Context contextForResources) { super(context); mContextForResources = contextForResources; } @Override public Resources getResources() { return mContextForResources.getResources(); } @Override public Resources.Theme getTheme() { return mContextForResources.getTheme(); } @Override public String getPackageName() { return mContextForResources.getPackageName(); } } private class SetEmptyView extends Action { int emptyViewId; SetEmptyView(int viewId, int emptyViewId) { this.viewId = viewId; this.emptyViewId = emptyViewId; } SetEmptyView(Parcel in) { this.viewId = in.readInt(); this.emptyViewId = in.readInt(); } public void writeToParcel(Parcel out, int flags) { out.writeInt(this.viewId); out.writeInt(this.emptyViewId); } @Override public void apply(View root, ViewGroup rootParent, OnClickHandler handler) { final View view = root.findViewById(viewId); if (!(view instanceof AdapterView<?>)) return; AdapterView<?> adapterView = (AdapterView<?>) view; final View emptyView = root.findViewById(emptyViewId); if (emptyView == null) return; adapterView.setEmptyView(emptyView); } @Override public int getActionTag() { return SET_EMPTY_VIEW_ACTION_TAG; } } private class SetOnClickFillInIntent extends Action { public SetOnClickFillInIntent(int id, Intent fillInIntent) { this.viewId = id; this.fillInIntent = fillInIntent; } public SetOnClickFillInIntent(Parcel parcel) { viewId = parcel.readInt(); fillInIntent = parcel.readTypedObject(Intent.CREATOR); } public void writeToParcel(Parcel dest, int flags) { dest.writeInt(viewId); dest.writeTypedObject(fillInIntent, 0 /* no flags */); } @Override public void apply(View root, ViewGroup rootParent, final OnClickHandler handler) { final View target = root.findViewById(viewId); if (target == null) return; if (!mIsWidgetCollectionChild) { Log.e(LOG_TAG, "The method setOnClickFillInIntent is available " + "only from RemoteViewsFactory (ie. on collection items)."); return; } if (target == root) { target.setTagInternal(com.android.internal.R.id.fillInIntent, fillInIntent); } else if (fillInIntent != null) { OnClickListener listener = new OnClickListener() { public void onClick(View v) { // Insure that this view is a child of an AdapterView View parent = (View) v.getParent(); // Break the for loop on the first encounter of: // 1) an AdapterView, // 2) an AppWidgetHostView that is not a RemoteViewsFrameLayout, or // 3) a null parent. // 2) and 3) are unexpected and catch the case where a child is not // correctly parented in an AdapterView. while (parent != null && !(parent instanceof AdapterView<?>) && !((parent instanceof AppWidgetHostView) && !(parent instanceof RemoteViewsAdapter.RemoteViewsFrameLayout))) { parent = (View) parent.getParent(); } if (!(parent instanceof AdapterView<?>)) { // Somehow they've managed to get this far without having // and AdapterView as a parent. Log.e(LOG_TAG, "Collection item doesn't have AdapterView parent"); return; } // Insure that a template pending intent has been set on an ancestor if (!(parent.getTag() instanceof PendingIntent)) { Log.e(LOG_TAG, "Attempting setOnClickFillInIntent without" + " calling setPendingIntentTemplate on parent."); return; } PendingIntent pendingIntent = (PendingIntent) parent.getTag(); final Rect rect = getSourceBounds(v); fillInIntent.setSourceBounds(rect); handler.onClickHandler(v, pendingIntent, fillInIntent); } }; target.setOnClickListener(listener); } } @Override public int getActionTag() { return SET_ON_CLICK_FILL_IN_INTENT_TAG; } Intent fillInIntent; } private class SetPendingIntentTemplate extends Action { public SetPendingIntentTemplate(int id, PendingIntent pendingIntentTemplate) { this.viewId = id; this.pendingIntentTemplate = pendingIntentTemplate; } public SetPendingIntentTemplate(Parcel parcel) { viewId = parcel.readInt(); pendingIntentTemplate = PendingIntent.readPendingIntentOrNullFromParcel(parcel); } public void writeToParcel(Parcel dest, int flags) { dest.writeInt(viewId); PendingIntent.writePendingIntentOrNullToParcel(pendingIntentTemplate, dest); } @Override public void apply(View root, ViewGroup rootParent, final OnClickHandler handler) { final View target = root.findViewById(viewId); if (target == null) return; // If the view isn't an AdapterView, setting a PendingIntent template doesn't make sense if (target instanceof AdapterView<?>) { AdapterView<?> av = (AdapterView<?>) target; // The PendingIntent template is stored in the view's tag. OnItemClickListener listener = new OnItemClickListener() { public void onItemClick(AdapterView<?> parent, View view, int position, long id) { // The view should be a frame layout if (view instanceof ViewGroup) { ViewGroup vg = (ViewGroup) view; // AdapterViews contain their children in a frame // so we need to go one layer deeper here. if (parent instanceof AdapterViewAnimator) { vg = (ViewGroup) vg.getChildAt(0); } if (vg == null) return; Intent fillInIntent = null; int childCount = vg.getChildCount(); for (int i = 0; i < childCount; i++) { Object tag = vg.getChildAt(i).getTag(com.android.internal.R.id.fillInIntent); if (tag instanceof Intent) { fillInIntent = (Intent) tag; break; } } if (fillInIntent == null) return; final Rect rect = getSourceBounds(view); final Intent intent = new Intent(); intent.setSourceBounds(rect); handler.onClickHandler(view, pendingIntentTemplate, fillInIntent); } } }; av.setOnItemClickListener(listener); av.setTag(pendingIntentTemplate); } else { Log.e(LOG_TAG, "Cannot setPendingIntentTemplate on a view which is not" + "an AdapterView (id: " + viewId + ")"); return; } } @Override public int getActionTag() { return SET_PENDING_INTENT_TEMPLATE_TAG; } PendingIntent pendingIntentTemplate; } private class SetRemoteViewsAdapterList extends Action { public SetRemoteViewsAdapterList(int id, ArrayList<RemoteViews> list, int viewTypeCount) { this.viewId = id; this.list = list; this.viewTypeCount = viewTypeCount; } public SetRemoteViewsAdapterList(Parcel parcel) { viewId = parcel.readInt(); viewTypeCount = parcel.readInt(); list = parcel.createTypedArrayList(RemoteViews.CREATOR); } public void writeToParcel(Parcel dest, int flags) { dest.writeInt(viewId); dest.writeInt(viewTypeCount); dest.writeTypedList(list, flags); } @Override public void apply(View root, ViewGroup rootParent, OnClickHandler handler) { final View target = root.findViewById(viewId); if (target == null) return; // Ensure that we are applying to an AppWidget root if (!(rootParent instanceof AppWidgetHostView)) { Log.e(LOG_TAG, "SetRemoteViewsAdapterIntent action can only be used for " + "AppWidgets (root id: " + viewId + ")"); return; } // Ensure that we are calling setRemoteAdapter on an AdapterView that supports it if (!(target instanceof AbsListView) && !(target instanceof AdapterViewAnimator)) { Log.e(LOG_TAG, "Cannot setRemoteViewsAdapter on a view which is not " + "an AbsListView or AdapterViewAnimator (id: " + viewId + ")"); return; } if (target instanceof AbsListView) { AbsListView v = (AbsListView) target; Adapter a = v.getAdapter(); if (a instanceof RemoteViewsListAdapter && viewTypeCount <= a.getViewTypeCount()) { ((RemoteViewsListAdapter) a).setViewsList(list); } else { v.setAdapter(new RemoteViewsListAdapter(v.getContext(), list, viewTypeCount)); } } else if (target instanceof AdapterViewAnimator) { AdapterViewAnimator v = (AdapterViewAnimator) target; Adapter a = v.getAdapter(); if (a instanceof RemoteViewsListAdapter && viewTypeCount <= a.getViewTypeCount()) { ((RemoteViewsListAdapter) a).setViewsList(list); } else { v.setAdapter(new RemoteViewsListAdapter(v.getContext(), list, viewTypeCount)); } } } @Override public int getActionTag() { return SET_REMOTE_VIEW_ADAPTER_LIST_TAG; } int viewTypeCount; ArrayList<RemoteViews> list; } private class SetRemoteViewsAdapterIntent extends Action { public SetRemoteViewsAdapterIntent(int id, Intent intent) { this.viewId = id; this.intent = intent; } public SetRemoteViewsAdapterIntent(Parcel parcel) { viewId = parcel.readInt(); intent = parcel.readTypedObject(Intent.CREATOR); } public void writeToParcel(Parcel dest, int flags) { dest.writeInt(viewId); dest.writeTypedObject(intent, flags); } @Override public void apply(View root, ViewGroup rootParent, OnClickHandler handler) { final View target = root.findViewById(viewId); if (target == null) return; // Ensure that we are applying to an AppWidget root if (!(rootParent instanceof AppWidgetHostView)) { Log.e(LOG_TAG, "SetRemoteViewsAdapterIntent action can only be used for " + "AppWidgets (root id: " + viewId + ")"); return; } // Ensure that we are calling setRemoteAdapter on an AdapterView that supports it if (!(target instanceof AbsListView) && !(target instanceof AdapterViewAnimator)) { Log.e(LOG_TAG, "Cannot setRemoteViewsAdapter on a view which is not " + "an AbsListView or AdapterViewAnimator (id: " + viewId + ")"); return; } // Embed the AppWidget Id for use in RemoteViewsAdapter when connecting to the intent // RemoteViewsService AppWidgetHostView host = (AppWidgetHostView) rootParent; intent.putExtra(EXTRA_REMOTEADAPTER_APPWIDGET_ID, host.getAppWidgetId()); if (target instanceof AbsListView) { AbsListView v = (AbsListView) target; v.setRemoteViewsAdapter(intent, isAsync); v.setRemoteViewsOnClickHandler(handler); } else if (target instanceof AdapterViewAnimator) { AdapterViewAnimator v = (AdapterViewAnimator) target; v.setRemoteViewsAdapter(intent, isAsync); v.setRemoteViewsOnClickHandler(handler); } } @Override public Action initActionAsync(ViewTree root, ViewGroup rootParent, OnClickHandler handler) { SetRemoteViewsAdapterIntent copy = new SetRemoteViewsAdapterIntent(viewId, intent); copy.isAsync = true; return copy; } @Override public int getActionTag() { return SET_REMOTE_VIEW_ADAPTER_INTENT_TAG; } Intent intent; boolean isAsync = false; }
Equivalent to calling View.setOnClickListener(OnClickListener) to launch the provided PendingIntent.
/** * Equivalent to calling * {@link android.view.View#setOnClickListener(android.view.View.OnClickListener)} * to launch the provided {@link PendingIntent}. */
private class SetOnClickPendingIntent extends Action { public SetOnClickPendingIntent(int id, PendingIntent pendingIntent) { this.viewId = id; this.pendingIntent = pendingIntent; } public SetOnClickPendingIntent(Parcel parcel) { viewId = parcel.readInt(); pendingIntent = PendingIntent.readPendingIntentOrNullFromParcel(parcel); } public void writeToParcel(Parcel dest, int flags) { dest.writeInt(viewId); PendingIntent.writePendingIntentOrNullToParcel(pendingIntent, dest); } @Override public void apply(View root, ViewGroup rootParent, final OnClickHandler handler) { final View target = root.findViewById(viewId); if (target == null) return; // If the view is an AdapterView, setting a PendingIntent on click doesn't make much // sense, do they mean to set a PendingIntent template for the AdapterView's children? if (mIsWidgetCollectionChild) { Log.w(LOG_TAG, "Cannot setOnClickPendingIntent for collection item " + "(id: " + viewId + ")"); ApplicationInfo appInfo = root.getContext().getApplicationInfo(); // We let this slide for HC and ICS so as to not break compatibility. It should have // been disabled from the outset, but was left open by accident. if (appInfo != null && appInfo.targetSdkVersion >= Build.VERSION_CODES.JELLY_BEAN) { return; } } // If the pendingIntent is null, we clear the onClickListener OnClickListener listener = null; if (pendingIntent != null) { listener = new OnClickListener() { public void onClick(View v) { // Find target view location in screen coordinates and // fill into PendingIntent before sending. final Rect rect = getSourceBounds(v); final Intent intent = new Intent(); intent.setSourceBounds(rect); handler.onClickHandler(v, pendingIntent, intent); } }; } target.setTagInternal(R.id.pending_intent_tag, pendingIntent); target.setOnClickListener(listener); } @Override public int getActionTag() { return SET_ON_CLICK_PENDING_INTENT_TAG; } PendingIntent pendingIntent; } private static Rect getSourceBounds(View v) { final float appScale = v.getContext().getResources() .getCompatibilityInfo().applicationScale; final int[] pos = new int[2]; v.getLocationOnScreen(pos); final Rect rect = new Rect(); rect.left = (int) (pos[0] * appScale + 0.5f); rect.top = (int) (pos[1] * appScale + 0.5f); rect.right = (int) ((pos[0] + v.getWidth()) * appScale + 0.5f); rect.bottom = (int) ((pos[1] + v.getHeight()) * appScale + 0.5f); return rect; } private MethodHandle getMethod(View view, String methodName, Class<?> paramType, boolean async) { MethodArgs result; Class<? extends View> klass = view.getClass(); synchronized (sMethods) { // The key is defined by the view class, param class and method name. sLookupKey.set(klass, paramType, methodName); result = sMethods.get(sLookupKey); if (result == null) { Method method; try { if (paramType == null) { method = klass.getMethod(methodName); } else { method = klass.getMethod(methodName, paramType); } if (!method.isAnnotationPresent(RemotableViewMethod.class)) { throw new ActionException("view: " + klass.getName() + " can't use method with RemoteViews: " + methodName + getParameters(paramType)); } result = new MethodArgs(); result.syncMethod = MethodHandles.publicLookup().unreflect(method); result.asyncMethodName = method.getAnnotation(RemotableViewMethod.class).asyncImpl(); } catch (NoSuchMethodException | IllegalAccessException ex) { throw new ActionException("view: " + klass.getName() + " doesn't have method: " + methodName + getParameters(paramType)); } MethodKey key = new MethodKey(); key.set(klass, paramType, methodName); sMethods.put(key, result); } if (!async) { return result.syncMethod; } // Check this so see if async method is implemented or not. if (result.asyncMethodName.isEmpty()) { return null; } // Async method is lazily loaded. If it is not yet loaded, load now. if (result.asyncMethod == null) { MethodType asyncType = result.syncMethod.type() .dropParameterTypes(0, 1).changeReturnType(Runnable.class); try { result.asyncMethod = MethodHandles.publicLookup().findVirtual( klass, result.asyncMethodName, asyncType); } catch (NoSuchMethodException | IllegalAccessException ex) { throw new ActionException("Async implementation declared as " + result.asyncMethodName + " but not defined for " + methodName + ": public Runnable " + result.asyncMethodName + " (" + TextUtils.join(",", asyncType.parameterArray()) + ")"); } } return result.asyncMethod; } } private static String getParameters(Class<?> paramType) { if (paramType == null) return "()"; return "(" + paramType + ")"; }
Equivalent to calling Drawable.setColorFilter(int, Mode), on the Drawable of a given view.

The operation will be performed on the Drawable returned by the target View.getBackground() by default. If targetBackground is false, we assume the target is an ImageView and try applying the operations to ImageView.getDrawable().

/** * Equivalent to calling * {@link Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)}, * on the {@link Drawable} of a given view. * <p> * The operation will be performed on the {@link Drawable} returned by the * target {@link View#getBackground()} by default. If targetBackground is false, * we assume the target is an {@link ImageView} and try applying the operations * to {@link ImageView#getDrawable()}. * <p> */
private class SetDrawableTint extends Action { SetDrawableTint(int id, boolean targetBackground, int colorFilter, @NonNull PorterDuff.Mode mode) { this.viewId = id; this.targetBackground = targetBackground; this.colorFilter = colorFilter; this.filterMode = mode; } SetDrawableTint(Parcel parcel) { viewId = parcel.readInt(); targetBackground = parcel.readInt() != 0; colorFilter = parcel.readInt(); filterMode = PorterDuff.intToMode(parcel.readInt()); } public void writeToParcel(Parcel dest, int flags) { dest.writeInt(viewId); dest.writeInt(targetBackground ? 1 : 0); dest.writeInt(colorFilter); dest.writeInt(PorterDuff.modeToInt(filterMode)); } @Override public void apply(View root, ViewGroup rootParent, OnClickHandler handler) { final View target = root.findViewById(viewId); if (target == null) return; // Pick the correct drawable to modify for this view Drawable targetDrawable = null; if (targetBackground) { targetDrawable = target.getBackground(); } else if (target instanceof ImageView) { ImageView imageView = (ImageView) target; targetDrawable = imageView.getDrawable(); } if (targetDrawable != null) { targetDrawable.mutate().setColorFilter(colorFilter, filterMode); } } @Override public int getActionTag() { return SET_DRAWABLE_TINT_TAG; } boolean targetBackground; int colorFilter; PorterDuff.Mode filterMode; } private final class ViewContentNavigation extends Action { final boolean mNext; ViewContentNavigation(int viewId, boolean next) { this.viewId = viewId; this.mNext = next; } ViewContentNavigation(Parcel in) { this.viewId = in.readInt(); this.mNext = in.readBoolean(); } public void writeToParcel(Parcel out, int flags) { out.writeInt(this.viewId); out.writeBoolean(this.mNext); } @Override public void apply(View root, ViewGroup rootParent, OnClickHandler handler) { final View view = root.findViewById(viewId); if (view == null) return; try { getMethod(view, mNext ? "showNext" : "showPrevious", null, false /* async */).invoke(view); } catch (Throwable ex) { throw new ActionException(ex); } } public int mergeBehavior() { return MERGE_IGNORE; } @Override public int getActionTag() { return VIEW_CONTENT_NAVIGATION_TAG; } } private static class BitmapCache { ArrayList<Bitmap> mBitmaps; int mBitmapMemory = -1; public BitmapCache() { mBitmaps = new ArrayList<>(); } public BitmapCache(Parcel source) { mBitmaps = source.createTypedArrayList(Bitmap.CREATOR); } public int getBitmapId(Bitmap b) { if (b == null) { return -1; } else { if (mBitmaps.contains(b)) { return mBitmaps.indexOf(b); } else { mBitmaps.add(b); mBitmapMemory = -1; return (mBitmaps.size() - 1); } } } public Bitmap getBitmapForId(int id) { if (id == -1 || id >= mBitmaps.size()) { return null; } else { return mBitmaps.get(id); } } public void writeBitmapsToParcel(Parcel dest, int flags) { dest.writeTypedList(mBitmaps, flags); } public int getBitmapMemory() { if (mBitmapMemory < 0) { mBitmapMemory = 0; int count = mBitmaps.size(); for (int i = 0; i < count; i++) { mBitmapMemory += mBitmaps.get(i).getAllocationByteCount(); } } return mBitmapMemory; } } private class BitmapReflectionAction extends Action { int bitmapId; Bitmap bitmap; String methodName; BitmapReflectionAction(int viewId, String methodName, Bitmap bitmap) { this.bitmap = bitmap; this.viewId = viewId; this.methodName = methodName; bitmapId = mBitmapCache.getBitmapId(bitmap); } BitmapReflectionAction(Parcel in) { viewId = in.readInt(); methodName = in.readString(); bitmapId = in.readInt(); bitmap = mBitmapCache.getBitmapForId(bitmapId); } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(viewId); dest.writeString(methodName); dest.writeInt(bitmapId); } @Override public void apply(View root, ViewGroup rootParent, OnClickHandler handler) throws ActionException { ReflectionAction ra = new ReflectionAction(viewId, methodName, ReflectionAction.BITMAP, bitmap); ra.apply(root, rootParent, handler); } @Override public void setBitmapCache(BitmapCache bitmapCache) { bitmapId = bitmapCache.getBitmapId(bitmap); } @Override public int getActionTag() { return BITMAP_REFLECTION_ACTION_TAG; } }
Base class for the reflection actions.
/** * Base class for the reflection actions. */
private final class ReflectionAction extends Action { static final int BOOLEAN = 1; static final int BYTE = 2; static final int SHORT = 3; static final int INT = 4; static final int LONG = 5; static final int FLOAT = 6; static final int DOUBLE = 7; static final int CHAR = 8; static final int STRING = 9; static final int CHAR_SEQUENCE = 10; static final int URI = 11; // BITMAP actions are never stored in the list of actions. They are only used locally // to implement BitmapReflectionAction, which eliminates duplicates using BitmapCache. static final int BITMAP = 12; static final int BUNDLE = 13; static final int INTENT = 14; static final int COLOR_STATE_LIST = 15; static final int ICON = 16; String methodName; int type; Object value; ReflectionAction(int viewId, String methodName, int type, Object value) { this.viewId = viewId; this.methodName = methodName; this.type = type; this.value = value; } ReflectionAction(Parcel in) { this.viewId = in.readInt(); this.methodName = in.readString(); this.type = in.readInt(); //noinspection ConstantIfStatement if (false) { Log.d(LOG_TAG, "read viewId=0x" + Integer.toHexString(this.viewId) + " methodName=" + this.methodName + " type=" + this.type); } // For some values that may have been null, we first check a flag to see if they were // written to the parcel. switch (this.type) { case BOOLEAN: this.value = in.readBoolean(); break; case BYTE: this.value = in.readByte(); break; case SHORT: this.value = (short)in.readInt(); break; case INT: this.value = in.readInt(); break; case LONG: this.value = in.readLong(); break; case FLOAT: this.value = in.readFloat(); break; case DOUBLE: this.value = in.readDouble(); break; case CHAR: this.value = (char)in.readInt(); break; case STRING: this.value = in.readString(); break; case CHAR_SEQUENCE: this.value = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); break; case URI: this.value = in.readTypedObject(Uri.CREATOR); break; case BITMAP: this.value = in.readTypedObject(Bitmap.CREATOR); break; case BUNDLE: this.value = in.readBundle(); break; case INTENT: this.value = in.readTypedObject(Intent.CREATOR); break; case COLOR_STATE_LIST: this.value = in.readTypedObject(ColorStateList.CREATOR); break; case ICON: this.value = in.readTypedObject(Icon.CREATOR); default: break; } } public void writeToParcel(Parcel out, int flags) { out.writeInt(this.viewId); out.writeString(this.methodName); out.writeInt(this.type); //noinspection ConstantIfStatement if (false) { Log.d(LOG_TAG, "write viewId=0x" + Integer.toHexString(this.viewId) + " methodName=" + this.methodName + " type=" + this.type); } // For some values which are null, we record an integer flag to indicate whether // we have written a valid value to the parcel. switch (this.type) { case BOOLEAN: out.writeBoolean((Boolean) this.value); break; case BYTE: out.writeByte((Byte) this.value); break; case SHORT: out.writeInt((Short) this.value); break; case INT: out.writeInt((Integer) this.value); break; case LONG: out.writeLong((Long) this.value); break; case FLOAT: out.writeFloat((Float) this.value); break; case DOUBLE: out.writeDouble((Double) this.value); break; case CHAR: out.writeInt((int)((Character)this.value).charValue()); break; case STRING: out.writeString((String)this.value); break; case CHAR_SEQUENCE: TextUtils.writeToParcel((CharSequence)this.value, out, flags); break; case BUNDLE: out.writeBundle((Bundle) this.value); break; case URI: case BITMAP: case INTENT: case COLOR_STATE_LIST: case ICON: out.writeTypedObject((Parcelable) this.value, flags); break; default: break; } } private Class<?> getParameterType() { switch (this.type) { case BOOLEAN: return boolean.class; case BYTE: return byte.class; case SHORT: return short.class; case INT: return int.class; case LONG: return long.class; case FLOAT: return float.class; case DOUBLE: return double.class; case CHAR: return char.class; case STRING: return String.class; case CHAR_SEQUENCE: return CharSequence.class; case URI: return Uri.class; case BITMAP: return Bitmap.class; case BUNDLE: return Bundle.class; case INTENT: return Intent.class; case COLOR_STATE_LIST: return ColorStateList.class; case ICON: return Icon.class; default: return null; } } @Override public void apply(View root, ViewGroup rootParent, OnClickHandler handler) { final View view = root.findViewById(viewId); if (view == null) return; Class<?> param = getParameterType(); if (param == null) { throw new ActionException("bad type: " + this.type); } try { getMethod(view, this.methodName, param, false /* async */).invoke(view, this.value); } catch (Throwable ex) { throw new ActionException(ex); } } @Override public Action initActionAsync(ViewTree root, ViewGroup rootParent, OnClickHandler handler) { final View view = root.findViewById(viewId); if (view == null) return ACTION_NOOP; Class<?> param = getParameterType(); if (param == null) { throw new ActionException("bad type: " + this.type); } try { MethodHandle method = getMethod(view, this.methodName, param, true /* async */); if (method != null) { Runnable endAction = (Runnable) method.invoke(view, this.value); if (endAction == null) { return ACTION_NOOP; } else { // Special case view stub if (endAction instanceof ViewStub.ViewReplaceRunnable) { root.createTree(); // Replace child tree root.findViewTreeById(viewId).replaceView( ((ViewStub.ViewReplaceRunnable) endAction).view); } return new RunnableAction(endAction); } } } catch (Throwable ex) { throw new ActionException(ex); } return this; } public int mergeBehavior() { // smoothScrollBy is cumulative, everything else overwites. if (methodName.equals("smoothScrollBy")) { return MERGE_APPEND; } else { return MERGE_REPLACE; } } @Override public int getActionTag() { return REFLECTION_ACTION_TAG; } @Override public String getUniqueKey() { // Each type of reflection action corresponds to a setter, so each should be seen as // unique from the standpoint of merging. return super.getUniqueKey() + this.methodName + this.type; } @Override public boolean prefersAsyncApply() { return this.type == URI || this.type == ICON; } @Override public void visitUris(@NonNull Consumer<Uri> visitor) { switch (this.type) { case URI: final Uri uri = (Uri) this.value; visitor.accept(uri); break; case ICON: final Icon icon = (Icon) this.value; visitIconUri(icon, visitor); break; } } }
This is only used for async execution of actions and it not parcelable.
/** * This is only used for async execution of actions and it not parcelable. */
private static final class RunnableAction extends RuntimeAction { private final Runnable mRunnable; RunnableAction(Runnable r) { mRunnable = r; } @Override public void apply(View root, ViewGroup rootParent, OnClickHandler handler) { mRunnable.run(); } } private void configureRemoteViewsAsChild(RemoteViews rv) { rv.setBitmapCache(mBitmapCache); rv.setNotRoot(); } void setNotRoot() { mIsRoot = false; }
ViewGroup methods that are related to adding Views.
/** * ViewGroup methods that are related to adding Views. */
private class ViewGroupActionAdd extends Action { private RemoteViews mNestedViews; private int mIndex; ViewGroupActionAdd(int viewId, RemoteViews nestedViews) { this(viewId, nestedViews, -1 /* index */); } ViewGroupActionAdd(int viewId, RemoteViews nestedViews, int index) { this.viewId = viewId; mNestedViews = nestedViews; mIndex = index; if (nestedViews != null) { configureRemoteViewsAsChild(nestedViews); } } ViewGroupActionAdd(Parcel parcel, BitmapCache bitmapCache, ApplicationInfo info, int depth, Map<Class, Object> classCookies) { viewId = parcel.readInt(); mIndex = parcel.readInt(); mNestedViews = new RemoteViews(parcel, bitmapCache, info, depth, classCookies); } public void writeToParcel(Parcel dest, int flags) { dest.writeInt(viewId); dest.writeInt(mIndex); mNestedViews.writeToParcel(dest, flags); } @Override public boolean hasSameAppInfo(ApplicationInfo parentInfo) { return mNestedViews.hasSameAppInfo(parentInfo); } @Override public void apply(View root, ViewGroup rootParent, OnClickHandler handler) { final Context context = root.getContext(); final ViewGroup target = root.findViewById(viewId); if (target == null) { return; } // Inflate nested views and add as children target.addView(mNestedViews.apply(context, target, handler), mIndex); } @Override public Action initActionAsync(ViewTree root, ViewGroup rootParent, OnClickHandler handler) { // In the async implementation, update the view tree so that subsequent calls to // findViewById return the current view. root.createTree(); ViewTree target = root.findViewTreeById(viewId); if ((target == null) || !(target.mRoot instanceof ViewGroup)) { return ACTION_NOOP; } final ViewGroup targetVg = (ViewGroup) target.mRoot; // Inflate nested views and perform all the async tasks for the child remoteView. final Context context = root.mRoot.getContext(); final AsyncApplyTask task = mNestedViews.getAsyncApplyTask( context, targetVg, null, handler); final ViewTree tree = task.doInBackground(); if (tree == null) { throw new ActionException(task.mError); } // Update the global view tree, so that next call to findViewTreeById // goes through the subtree as well. target.addChild(tree, mIndex); return new RuntimeAction() { @Override public void apply(View root, ViewGroup rootParent, OnClickHandler handler) throws ActionException { task.onPostExecute(tree); targetVg.addView(task.mResult, mIndex); } }; } @Override public void setBitmapCache(BitmapCache bitmapCache) { mNestedViews.setBitmapCache(bitmapCache); } @Override public int mergeBehavior() { return MERGE_APPEND; } @Override public boolean prefersAsyncApply() { return mNestedViews.prefersAsyncApply(); } @Override public int getActionTag() { return VIEW_GROUP_ACTION_ADD_TAG; } }
ViewGroup methods related to removing child views.
/** * ViewGroup methods related to removing child views. */
private class ViewGroupActionRemove extends Action {
Id that indicates that all child views of the affected ViewGroup should be removed.

Using -2 because the default id is -1. This avoids accidentally matching that.

/** * Id that indicates that all child views of the affected ViewGroup should be removed. * * <p>Using -2 because the default id is -1. This avoids accidentally matching that. */
private static final int REMOVE_ALL_VIEWS_ID = -2; private int mViewIdToKeep; ViewGroupActionRemove(int viewId) { this(viewId, REMOVE_ALL_VIEWS_ID); } ViewGroupActionRemove(int viewId, int viewIdToKeep) { this.viewId = viewId; mViewIdToKeep = viewIdToKeep; } ViewGroupActionRemove(Parcel parcel) { viewId = parcel.readInt(); mViewIdToKeep = parcel.readInt(); } public void writeToParcel(Parcel dest, int flags) { dest.writeInt(viewId); dest.writeInt(mViewIdToKeep); } @Override public void apply(View root, ViewGroup rootParent, OnClickHandler handler) { final ViewGroup target = root.findViewById(viewId); if (target == null) { return; } if (mViewIdToKeep == REMOVE_ALL_VIEWS_ID) { target.removeAllViews(); return; } removeAllViewsExceptIdToKeep(target); } @Override public Action initActionAsync(ViewTree root, ViewGroup rootParent, OnClickHandler handler) { // In the async implementation, update the view tree so that subsequent calls to // findViewById return the current view. root.createTree(); ViewTree target = root.findViewTreeById(viewId); if ((target == null) || !(target.mRoot instanceof ViewGroup)) { return ACTION_NOOP; } final ViewGroup targetVg = (ViewGroup) target.mRoot; // Clear all children when nested views omitted target.mChildren = null; return new RuntimeAction() { @Override public void apply(View root, ViewGroup rootParent, OnClickHandler handler) throws ActionException { if (mViewIdToKeep == REMOVE_ALL_VIEWS_ID) { targetVg.removeAllViews(); return; } removeAllViewsExceptIdToKeep(targetVg); } }; }
Iterates through the children in the given ViewGroup and removes all the views that do not have an id of mViewIdToKeep.
/** * Iterates through the children in the given ViewGroup and removes all the views that * do not have an id of {@link #mViewIdToKeep}. */
private void removeAllViewsExceptIdToKeep(ViewGroup viewGroup) { // Otherwise, remove all the views that do not match the id to keep. int index = viewGroup.getChildCount() - 1; while (index >= 0) { if (viewGroup.getChildAt(index).getId() != mViewIdToKeep) { viewGroup.removeViewAt(index); } index--; } } @Override public int getActionTag() { return VIEW_GROUP_ACTION_REMOVE_TAG; } @Override public int mergeBehavior() { return MERGE_APPEND; } }
Helper action to set compound drawables on a TextView. Supports relative (s/t/e/b) or cardinal (l/t/r/b) arrangement.
/** * Helper action to set compound drawables on a TextView. Supports relative * (s/t/e/b) or cardinal (l/t/r/b) arrangement. */
private class TextViewDrawableAction extends Action { public TextViewDrawableAction(int viewId, boolean isRelative, int d1, int d2, int d3, int d4) { this.viewId = viewId; this.isRelative = isRelative; this.useIcons = false; this.d1 = d1; this.d2 = d2; this.d3 = d3; this.d4 = d4; } public TextViewDrawableAction(int viewId, boolean isRelative, Icon i1, Icon i2, Icon i3, Icon i4) { this.viewId = viewId; this.isRelative = isRelative; this.useIcons = true; this.i1 = i1; this.i2 = i2; this.i3 = i3; this.i4 = i4; } public TextViewDrawableAction(Parcel parcel) { viewId = parcel.readInt(); isRelative = (parcel.readInt() != 0); useIcons = (parcel.readInt() != 0); if (useIcons) { i1 = parcel.readTypedObject(Icon.CREATOR); i2 = parcel.readTypedObject(Icon.CREATOR); i3 = parcel.readTypedObject(Icon.CREATOR); i4 = parcel.readTypedObject(Icon.CREATOR); } else { d1 = parcel.readInt(); d2 = parcel.readInt(); d3 = parcel.readInt(); d4 = parcel.readInt(); } } public void writeToParcel(Parcel dest, int flags) { dest.writeInt(viewId); dest.writeInt(isRelative ? 1 : 0); dest.writeInt(useIcons ? 1 : 0); if (useIcons) { dest.writeTypedObject(i1, 0); dest.writeTypedObject(i2, 0); dest.writeTypedObject(i3, 0); dest.writeTypedObject(i4, 0); } else { dest.writeInt(d1); dest.writeInt(d2); dest.writeInt(d3); dest.writeInt(d4); } } @Override public void apply(View root, ViewGroup rootParent, OnClickHandler handler) { final TextView target = root.findViewById(viewId); if (target == null) return; if (drawablesLoaded) { if (isRelative) { target.setCompoundDrawablesRelativeWithIntrinsicBounds(id1, id2, id3, id4); } else { target.setCompoundDrawablesWithIntrinsicBounds(id1, id2, id3, id4); } } else if (useIcons) { final Context ctx = target.getContext(); final Drawable id1 = i1 == null ? null : i1.loadDrawable(ctx); final Drawable id2 = i2 == null ? null : i2.loadDrawable(ctx); final Drawable id3 = i3 == null ? null : i3.loadDrawable(ctx); final Drawable id4 = i4 == null ? null : i4.loadDrawable(ctx); if (isRelative) { target.setCompoundDrawablesRelativeWithIntrinsicBounds(id1, id2, id3, id4); } else { target.setCompoundDrawablesWithIntrinsicBounds(id1, id2, id3, id4); } } else { if (isRelative) { target.setCompoundDrawablesRelativeWithIntrinsicBounds(d1, d2, d3, d4); } else { target.setCompoundDrawablesWithIntrinsicBounds(d1, d2, d3, d4); } } } @Override public Action initActionAsync(ViewTree root, ViewGroup rootParent, OnClickHandler handler) { final TextView target = root.findViewById(viewId); if (target == null) return ACTION_NOOP; TextViewDrawableAction copy = useIcons ? new TextViewDrawableAction(viewId, isRelative, i1, i2, i3, i4) : new TextViewDrawableAction(viewId, isRelative, d1, d2, d3, d4); // Load the drawables on the background thread. copy.drawablesLoaded = true; final Context ctx = target.getContext(); if (useIcons) { copy.id1 = i1 == null ? null : i1.loadDrawable(ctx); copy.id2 = i2 == null ? null : i2.loadDrawable(ctx); copy.id3 = i3 == null ? null : i3.loadDrawable(ctx); copy.id4 = i4 == null ? null : i4.loadDrawable(ctx); } else { copy.id1 = d1 == 0 ? null : ctx.getDrawable(d1); copy.id2 = d2 == 0 ? null : ctx.getDrawable(d2); copy.id3 = d3 == 0 ? null : ctx.getDrawable(d3); copy.id4 = d4 == 0 ? null : ctx.getDrawable(d4); } return copy; } @Override public boolean prefersAsyncApply() { return useIcons; } @Override public int getActionTag() { return TEXT_VIEW_DRAWABLE_ACTION_TAG; } @Override public void visitUris(@NonNull Consumer<Uri> visitor) { if (useIcons) { visitIconUri(i1, visitor); visitIconUri(i2, visitor); visitIconUri(i3, visitor); visitIconUri(i4, visitor); } } boolean isRelative = false; boolean useIcons = false; int d1, d2, d3, d4; Icon i1, i2, i3, i4; boolean drawablesLoaded = false; Drawable id1, id2, id3, id4; }
Helper action to set text size on a TextView in any supported units.
/** * Helper action to set text size on a TextView in any supported units. */
private class TextViewSizeAction extends Action { public TextViewSizeAction(int viewId, int units, float size) { this.viewId = viewId; this.units = units; this.size = size; } public TextViewSizeAction(Parcel parcel) { viewId = parcel.readInt(); units = parcel.readInt(); size = parcel.readFloat(); } public void writeToParcel(Parcel dest, int flags) { dest.writeInt(viewId); dest.writeInt(units); dest.writeFloat(size); } @Override public void apply(View root, ViewGroup rootParent, OnClickHandler handler) { final TextView target = root.findViewById(viewId); if (target == null) return; target.setTextSize(units, size); } @Override public int getActionTag() { return TEXT_VIEW_SIZE_ACTION_TAG; } int units; float size; }
Helper action to set padding on a View.
/** * Helper action to set padding on a View. */
private class ViewPaddingAction extends Action { public ViewPaddingAction(int viewId, int left, int top, int right, int bottom) { this.viewId = viewId; this.left = left; this.top = top; this.right = right; this.bottom = bottom; } public ViewPaddingAction(Parcel parcel) { viewId = parcel.readInt(); left = parcel.readInt(); top = parcel.readInt(); right = parcel.readInt(); bottom = parcel.readInt(); } public void writeToParcel(Parcel dest, int flags) { dest.writeInt(viewId); dest.writeInt(left); dest.writeInt(top); dest.writeInt(right); dest.writeInt(bottom); } @Override public void apply(View root, ViewGroup rootParent, OnClickHandler handler) { final View target = root.findViewById(viewId); if (target == null) return; target.setPadding(left, top, right, bottom); } @Override public int getActionTag() { return VIEW_PADDING_ACTION_TAG; } int left, top, right, bottom; }
Helper action to set layout params on a View.
/** * Helper action to set layout params on a View. */
private static class LayoutParamAction extends Action {
Set marginEnd
/** Set marginEnd */
public static final int LAYOUT_MARGIN_END_DIMEN = 1;
Set width
/** Set width */
public static final int LAYOUT_WIDTH = 2; public static final int LAYOUT_MARGIN_BOTTOM_DIMEN = 3; public static final int LAYOUT_MARGIN_END = 4; final int mProperty; final int mValue;
Params:
  • viewId – ID of the view alter
  • property – which layout parameter to alter
  • value – new value of the layout parameter
/** * @param viewId ID of the view alter * @param property which layout parameter to alter * @param value new value of the layout parameter */
public LayoutParamAction(int viewId, int property, int value) { this.viewId = viewId; this.mProperty = property; this.mValue = value; } public LayoutParamAction(Parcel parcel) { viewId = parcel.readInt(); mProperty = parcel.readInt(); mValue = parcel.readInt(); } public void writeToParcel(Parcel dest, int flags) { dest.writeInt(viewId); dest.writeInt(mProperty); dest.writeInt(mValue); } @Override public void apply(View root, ViewGroup rootParent, OnClickHandler handler) { final View target = root.findViewById(viewId); if (target == null) { return; } ViewGroup.LayoutParams layoutParams = target.getLayoutParams(); if (layoutParams == null) { return; } int value = mValue; switch (mProperty) { case LAYOUT_MARGIN_END_DIMEN: value = resolveDimenPixelOffset(target, mValue); // fall-through case LAYOUT_MARGIN_END: if (layoutParams instanceof ViewGroup.MarginLayoutParams) { ((ViewGroup.MarginLayoutParams) layoutParams).setMarginEnd(value); target.setLayoutParams(layoutParams); } break; case LAYOUT_MARGIN_BOTTOM_DIMEN: if (layoutParams instanceof ViewGroup.MarginLayoutParams) { int resolved = resolveDimenPixelOffset(target, mValue); ((ViewGroup.MarginLayoutParams) layoutParams).bottomMargin = resolved; target.setLayoutParams(layoutParams); } break; case LAYOUT_WIDTH: layoutParams.width = mValue; target.setLayoutParams(layoutParams); break; default: throw new IllegalArgumentException("Unknown property " + mProperty); } } private static int resolveDimenPixelOffset(View target, int value) { if (value == 0) { return 0; } return target.getContext().getResources().getDimensionPixelOffset(value); } @Override public int getActionTag() { return LAYOUT_PARAM_ACTION_TAG; } @Override public String getUniqueKey() { return super.getUniqueKey() + mProperty; } }
Helper action to add a view tag with RemoteInputs.
/** * Helper action to add a view tag with RemoteInputs. */
private class SetRemoteInputsAction extends Action { public SetRemoteInputsAction(int viewId, RemoteInput[] remoteInputs) { this.viewId = viewId; this.remoteInputs = remoteInputs; } public SetRemoteInputsAction(Parcel parcel) { viewId = parcel.readInt(); remoteInputs = parcel.createTypedArray(RemoteInput.CREATOR); } public void writeToParcel(Parcel dest, int flags) { dest.writeInt(viewId); dest.writeTypedArray(remoteInputs, flags); } @Override public void apply(View root, ViewGroup rootParent, OnClickHandler handler) { final View target = root.findViewById(viewId); if (target == null) return; target.setTagInternal(R.id.remote_input_tag, remoteInputs); } @Override public int getActionTag() { return SET_REMOTE_INPUTS_ACTION_TAG; } final Parcelable[] remoteInputs; }
Helper action to override all textViewColors
/** * Helper action to override all textViewColors */
private class OverrideTextColorsAction extends Action { private final int textColor; public OverrideTextColorsAction(int textColor) { this.textColor = textColor; } public OverrideTextColorsAction(Parcel parcel) { textColor = parcel.readInt(); } public void writeToParcel(Parcel dest, int flags) { dest.writeInt(textColor); } @Override public void apply(View root, ViewGroup rootParent, OnClickHandler handler) { // Let's traverse the viewtree and override all textColors! Stack<View> viewsToProcess = new Stack<>(); viewsToProcess.add(root); while (!viewsToProcess.isEmpty()) { View v = viewsToProcess.pop(); if (v instanceof TextView) { TextView textView = (TextView) v; textView.setText(NotificationColorUtil.clearColorSpans(textView.getText())); textView.setTextColor(textColor); } if (v instanceof ViewGroup) { ViewGroup viewGroup = (ViewGroup) v; for (int i = 0; i < viewGroup.getChildCount(); i++) { viewsToProcess.push(viewGroup.getChildAt(i)); } } } } @Override public int getActionTag() { return OVERRIDE_TEXT_COLORS_TAG; } }
Create a new RemoteViews object that will display the views contained in the specified layout file.
Params:
  • packageName – Name of the package that contains the layout resource
  • layoutId – The id of the layout resource
/** * Create a new RemoteViews object that will display the views contained * in the specified layout file. * * @param packageName Name of the package that contains the layout resource * @param layoutId The id of the layout resource */
public RemoteViews(String packageName, int layoutId) { this(getApplicationInfo(packageName, UserHandle.myUserId()), layoutId); }
Create a new RemoteViews object that will display the views contained in the specified layout file.
Params:
  • packageName – Name of the package that contains the layout resource.
  • userId – The user under which the package is running.
  • layoutId – The id of the layout resource.
@hide
/** * Create a new RemoteViews object that will display the views contained * in the specified layout file. * * @param packageName Name of the package that contains the layout resource. * @param userId The user under which the package is running. * @param layoutId The id of the layout resource. * * @hide */
public RemoteViews(String packageName, int userId, int layoutId) { this(getApplicationInfo(packageName, userId), layoutId); }
Create a new RemoteViews object that will display the views contained in the specified layout file.
Params:
  • application – The application whose content is shown by the views.
  • layoutId – The id of the layout resource.
@hide
/** * Create a new RemoteViews object that will display the views contained * in the specified layout file. * * @param application The application whose content is shown by the views. * @param layoutId The id of the layout resource. * * @hide */
protected RemoteViews(ApplicationInfo application, int layoutId) { mApplication = application; mLayoutId = layoutId; mBitmapCache = new BitmapCache(); mClassCookies = null; } private boolean hasLandscapeAndPortraitLayouts() { return (mLandscape != null) && (mPortrait != null); }
Create a new RemoteViews object that will inflate as the specified landspace or portrait RemoteViews, depending on the current configuration.
Params:
  • landscape – The RemoteViews to inflate in landscape configuration
  • portrait – The RemoteViews to inflate in portrait configuration
/** * Create a new RemoteViews object that will inflate as the specified * landspace or portrait RemoteViews, depending on the current configuration. * * @param landscape The RemoteViews to inflate in landscape configuration * @param portrait The RemoteViews to inflate in portrait configuration */
public RemoteViews(RemoteViews landscape, RemoteViews portrait) { if (landscape == null || portrait == null) { throw new RuntimeException("Both RemoteViews must be non-null"); } if (!landscape.hasSameAppInfo(portrait.mApplication)) { throw new RuntimeException("Both RemoteViews must share the same package and user"); } mApplication = portrait.mApplication; mLayoutId = portrait.getLayoutId(); mLandscape = landscape; mPortrait = portrait; mBitmapCache = new BitmapCache(); configureRemoteViewsAsChild(landscape); configureRemoteViewsAsChild(portrait); mClassCookies = (portrait.mClassCookies != null) ? portrait.mClassCookies : landscape.mClassCookies; }
Creates a copy of another RemoteViews.
/** * Creates a copy of another RemoteViews. */
public RemoteViews(RemoteViews src) { mBitmapCache = src.mBitmapCache; mApplication = src.mApplication; mIsRoot = src.mIsRoot; mLayoutId = src.mLayoutId; mIsWidgetCollectionChild = src.mIsWidgetCollectionChild; mReapplyDisallowed = src.mReapplyDisallowed; mClassCookies = src.mClassCookies; if (src.hasLandscapeAndPortraitLayouts()) { mLandscape = new RemoteViews(src.mLandscape); mPortrait = new RemoteViews(src.mPortrait); } if (src.mActions != null) { Parcel p = Parcel.obtain(); p.putClassCookies(mClassCookies); src.writeActionsToParcel(p); p.setDataPosition(0); // Since src is already in memory, we do not care about stack overflow as it has // already been read once. readActionsFromParcel(p, 0); p.recycle(); } // Now that everything is initialized and duplicated, setting a new BitmapCache will // re-initialize the cache. setBitmapCache(new BitmapCache()); }
Reads a RemoteViews object from a parcel.
Params:
  • parcel –
/** * Reads a RemoteViews object from a parcel. * * @param parcel */
public RemoteViews(Parcel parcel) { this(parcel, null, null, 0, null); } private RemoteViews(Parcel parcel, BitmapCache bitmapCache, ApplicationInfo info, int depth, Map<Class, Object> classCookies) { if (depth > MAX_NESTED_VIEWS && (UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID)) { throw new IllegalArgumentException("Too many nested views."); } depth++; int mode = parcel.readInt(); // We only store a bitmap cache in the root of the RemoteViews. if (bitmapCache == null) { mBitmapCache = new BitmapCache(parcel); // Store the class cookies such that they are available when we clone this RemoteView. mClassCookies = parcel.copyClassCookies(); } else { setBitmapCache(bitmapCache); mClassCookies = classCookies; setNotRoot(); } if (mode == MODE_NORMAL) { mApplication = parcel.readInt() == 0 ? info : ApplicationInfo.CREATOR.createFromParcel(parcel); mLayoutId = parcel.readInt(); mIsWidgetCollectionChild = parcel.readInt() == 1; readActionsFromParcel(parcel, depth); } else { // MODE_HAS_LANDSCAPE_AND_PORTRAIT mLandscape = new RemoteViews(parcel, mBitmapCache, info, depth, mClassCookies); mPortrait = new RemoteViews(parcel, mBitmapCache, mLandscape.mApplication, depth, mClassCookies); mApplication = mPortrait.mApplication; mLayoutId = mPortrait.getLayoutId(); } mReapplyDisallowed = parcel.readInt() == 0; } private void readActionsFromParcel(Parcel parcel, int depth) { int count = parcel.readInt(); if (count > 0) { mActions = new ArrayList<>(count); for (int i = 0; i < count; i++) { mActions.add(getActionFromParcel(parcel, depth)); } } } private Action getActionFromParcel(Parcel parcel, int depth) { int tag = parcel.readInt(); switch (tag) { case SET_ON_CLICK_PENDING_INTENT_TAG: return new SetOnClickPendingIntent(parcel); case SET_DRAWABLE_TINT_TAG: return new SetDrawableTint(parcel); case REFLECTION_ACTION_TAG: return new ReflectionAction(parcel); case VIEW_GROUP_ACTION_ADD_TAG: return new ViewGroupActionAdd(parcel, mBitmapCache, mApplication, depth, mClassCookies); case VIEW_GROUP_ACTION_REMOVE_TAG: return new ViewGroupActionRemove(parcel); case VIEW_CONTENT_NAVIGATION_TAG: return new ViewContentNavigation(parcel); case SET_EMPTY_VIEW_ACTION_TAG: return new SetEmptyView(parcel); case SET_PENDING_INTENT_TEMPLATE_TAG: return new SetPendingIntentTemplate(parcel); case SET_ON_CLICK_FILL_IN_INTENT_TAG: return new SetOnClickFillInIntent(parcel); case SET_REMOTE_VIEW_ADAPTER_INTENT_TAG: return new SetRemoteViewsAdapterIntent(parcel); case TEXT_VIEW_DRAWABLE_ACTION_TAG: return new TextViewDrawableAction(parcel); case TEXT_VIEW_SIZE_ACTION_TAG: return new TextViewSizeAction(parcel); case VIEW_PADDING_ACTION_TAG: return new ViewPaddingAction(parcel); case BITMAP_REFLECTION_ACTION_TAG: return new BitmapReflectionAction(parcel); case SET_REMOTE_VIEW_ADAPTER_LIST_TAG: return new SetRemoteViewsAdapterList(parcel); case SET_REMOTE_INPUTS_ACTION_TAG: return new SetRemoteInputsAction(parcel); case LAYOUT_PARAM_ACTION_TAG: return new LayoutParamAction(parcel); case OVERRIDE_TEXT_COLORS_TAG: return new OverrideTextColorsAction(parcel); default: throw new ActionException("Tag " + tag + " not found"); } };
Returns a deep copy of the RemoteViews object. The RemoteView may not be attached to another RemoteView -- it must be the root of a hierarchy.
Throws:
Deprecated:use RemoteViews(RemoteViews) instead.
/** * Returns a deep copy of the RemoteViews object. The RemoteView may not be * attached to another RemoteView -- it must be the root of a hierarchy. * * @deprecated use {@link #RemoteViews(RemoteViews)} instead. * @throws IllegalStateException if this is not the root of a RemoteView * hierarchy */
@Override @Deprecated public RemoteViews clone() { Preconditions.checkState(mIsRoot, "RemoteView has been attached to another RemoteView. " + "May only clone the root of a RemoteView hierarchy."); return new RemoteViews(this); } public String getPackage() { return (mApplication != null) ? mApplication.packageName : null; }
Returns the layout id of the root layout associated with this RemoteViews. In the case that the RemoteViews has both a landscape and portrait root, this will return the layout id associated with the portrait layout.
Returns:the layout id.
/** * Returns the layout id of the root layout associated with this RemoteViews. In the case * that the RemoteViews has both a landscape and portrait root, this will return the layout * id associated with the portrait layout. * * @return the layout id. */
public int getLayoutId() { return mLayoutId; } /* * This flag indicates whether this RemoteViews object is being created from a * RemoteViewsService for use as a child of a widget collection. This flag is used * to determine whether or not certain features are available, in particular, * setting on click extras and setting on click pending intents. The former is enabled, * and the latter disabled when this flag is true. */ void setIsWidgetCollectionChild(boolean isWidgetCollectionChild) { mIsWidgetCollectionChild = isWidgetCollectionChild; }
Recursively sets BitmapCache in the hierarchy and update the bitmap ids.
/** * Recursively sets BitmapCache in the hierarchy and update the bitmap ids. */
private void setBitmapCache(BitmapCache bitmapCache) { mBitmapCache = bitmapCache; if (!hasLandscapeAndPortraitLayouts()) { if (mActions != null) { final int count = mActions.size(); for (int i= 0; i < count; ++i) { mActions.get(i).setBitmapCache(bitmapCache); } } } else { mLandscape.setBitmapCache(bitmapCache); mPortrait.setBitmapCache(bitmapCache); } } /** * Returns an estimate of the bitmap heap memory usage for this RemoteViews. */
@hide
/** @hide */
public int estimateMemoryUsage() { return mBitmapCache.getBitmapMemory(); }
Add an action to be executed on the remote side when apply is called.
Params:
  • a – The action to add
/** * Add an action to be executed on the remote side when apply is called. * * @param a The action to add */
private void addAction(Action a) { if (hasLandscapeAndPortraitLayouts()) { throw new RuntimeException("RemoteViews specifying separate landscape and portrait" + " layouts cannot be modified. Instead, fully configure the landscape and" + " portrait layouts individually before constructing the combined layout."); } if (mActions == null) { mActions = new ArrayList<>(); } mActions.add(a); }
Equivalent to calling ViewGroup.addView(View) after inflating the given RemoteViews. This allows users to build "nested" RemoteViews. In cases where consumers of RemoteViews may recycle layouts, use removeAllViews(int) to clear any existing children.
Params:
  • viewId – The id of the parent ViewGroup to add child into.
  • nestedView – RemoteViews that describes the child.
/** * Equivalent to calling {@link ViewGroup#addView(View)} after inflating the * given {@link RemoteViews}. This allows users to build "nested" * {@link RemoteViews}. In cases where consumers of {@link RemoteViews} may * recycle layouts, use {@link #removeAllViews(int)} to clear any existing * children. * * @param viewId The id of the parent {@link ViewGroup} to add child into. * @param nestedView {@link RemoteViews} that describes the child. */
public void addView(int viewId, RemoteViews nestedView) { addAction(nestedView == null ? new ViewGroupActionRemove(viewId) : new ViewGroupActionAdd(viewId, nestedView)); }
Equivalent to calling ViewGroup.addView(View, int) after inflating the given RemoteViews.
Params:
  • viewId – The id of the parent ViewGroup to add the child into.
  • nestedView – RemoteViews of the child to add.
  • index – The position at which to add the child.
@hide
/** * Equivalent to calling {@link ViewGroup#addView(View, int)} after inflating the * given {@link RemoteViews}. * * @param viewId The id of the parent {@link ViewGroup} to add the child into. * @param nestedView {@link RemoteViews} of the child to add. * @param index The position at which to add the child. * * @hide */
public void addView(int viewId, RemoteViews nestedView, int index) { addAction(new ViewGroupActionAdd(viewId, nestedView, index)); }
Equivalent to calling ViewGroup.removeAllViews().
Params:
  • viewId – The id of the parent ViewGroup to remove all children from.
/** * Equivalent to calling {@link ViewGroup#removeAllViews()}. * * @param viewId The id of the parent {@link ViewGroup} to remove all * children from. */
public void removeAllViews(int viewId) { addAction(new ViewGroupActionRemove(viewId)); }
Removes all views in the ViewGroup specified by the viewId except for any child that has the viewIdToKeep as its id.
Params:
  • viewId – The id of the parent ViewGroup to remove children from.
  • viewIdToKeep – The id of a child that should not be removed.
@hide
/** * Removes all views in the {@link ViewGroup} specified by the {@code viewId} except for any * child that has the {@code viewIdToKeep} as its id. * * @param viewId The id of the parent {@link ViewGroup} to remove children from. * @param viewIdToKeep The id of a child that should not be removed. * * @hide */
public void removeAllViewsExceptId(int viewId, int viewIdToKeep) { addAction(new ViewGroupActionRemove(viewId, viewIdToKeep)); }
Equivalent to calling AdapterViewAnimator.showNext()
Params:
/** * Equivalent to calling {@link AdapterViewAnimator#showNext()} * * @param viewId The id of the view on which to call {@link AdapterViewAnimator#showNext()} */
public void showNext(int viewId) { addAction(new ViewContentNavigation(viewId, true /* next */)); }
Equivalent to calling AdapterViewAnimator.showPrevious()
Params:
/** * Equivalent to calling {@link AdapterViewAnimator#showPrevious()} * * @param viewId The id of the view on which to call {@link AdapterViewAnimator#showPrevious()} */
public void showPrevious(int viewId) { addAction(new ViewContentNavigation(viewId, false /* next */)); }
Params:
/** * Equivalent to calling {@link AdapterViewAnimator#setDisplayedChild(int)} * * @param viewId The id of the view on which to call * {@link AdapterViewAnimator#setDisplayedChild(int)} */
public void setDisplayedChild(int viewId, int childIndex) { setInt(viewId, "setDisplayedChild", childIndex); }
Equivalent to calling View.setVisibility(int)
Params:
  • viewId – The id of the view whose visibility should change
  • visibility – The new visibility for the view
/** * Equivalent to calling {@link View#setVisibility(int)} * * @param viewId The id of the view whose visibility should change * @param visibility The new visibility for the view */
public void setViewVisibility(int viewId, int visibility) { setInt(viewId, "setVisibility", visibility); }
Equivalent to calling TextView.setText(CharSequence)
Params:
  • viewId – The id of the view whose text should change
  • text – The new text for the view
/** * Equivalent to calling {@link TextView#setText(CharSequence)} * * @param viewId The id of the view whose text should change * @param text The new text for the view */
public void setTextViewText(int viewId, CharSequence text) { setCharSequence(viewId, "setText", text); }
Equivalent to calling TextView.setTextSize(int, float)
Params:
  • viewId – The id of the view whose text size should change
  • units – The units of size (e.g. COMPLEX_UNIT_SP)
  • size – The size of the text
/** * Equivalent to calling {@link TextView#setTextSize(int, float)} * * @param viewId The id of the view whose text size should change * @param units The units of size (e.g. COMPLEX_UNIT_SP) * @param size The size of the text */
public void setTextViewTextSize(int viewId, int units, float size) { addAction(new TextViewSizeAction(viewId, units, size)); }
Params:
  • viewId – The id of the view whose text should change
  • left – The id of a drawable to place to the left of the text, or 0
  • top – The id of a drawable to place above the text, or 0
  • right – The id of a drawable to place to the right of the text, or 0
  • bottom – The id of a drawable to place below the text, or 0
/** * Equivalent to calling * {@link TextView#setCompoundDrawablesWithIntrinsicBounds(int, int, int, int)}. * * @param viewId The id of the view whose text should change * @param left The id of a drawable to place to the left of the text, or 0 * @param top The id of a drawable to place above the text, or 0 * @param right The id of a drawable to place to the right of the text, or 0 * @param bottom The id of a drawable to place below the text, or 0 */
public void setTextViewCompoundDrawables(int viewId, int left, int top, int right, int bottom) { addAction(new TextViewDrawableAction(viewId, false, left, top, right, bottom)); }
Params:
  • viewId – The id of the view whose text should change
  • start – The id of a drawable to place before the text (relative to the layout direction), or 0
  • top – The id of a drawable to place above the text, or 0
  • end – The id of a drawable to place after the text, or 0
  • bottom – The id of a drawable to place below the text, or 0
/** * Equivalent to calling {@link * TextView#setCompoundDrawablesRelativeWithIntrinsicBounds(int, int, int, int)}. * * @param viewId The id of the view whose text should change * @param start The id of a drawable to place before the text (relative to the * layout direction), or 0 * @param top The id of a drawable to place above the text, or 0 * @param end The id of a drawable to place after the text, or 0 * @param bottom The id of a drawable to place below the text, or 0 */
public void setTextViewCompoundDrawablesRelative(int viewId, int start, int top, int end, int bottom) { addAction(new TextViewDrawableAction(viewId, true, start, top, end, bottom)); }
Params:
  • viewId – The id of the view whose text should change
  • left – an Icon to place to the left of the text, or 0
  • top – an Icon to place above the text, or 0
  • right – an Icon to place to the right of the text, or 0
  • bottom – an Icon to place below the text, or 0
@hide
/** * Equivalent to calling {@link * TextView#setCompoundDrawablesWithIntrinsicBounds(Drawable, Drawable, Drawable, Drawable)} * using the drawables yielded by {@link Icon#loadDrawable(Context)}. * * @param viewId The id of the view whose text should change * @param left an Icon to place to the left of the text, or 0 * @param top an Icon to place above the text, or 0 * @param right an Icon to place to the right of the text, or 0 * @param bottom an Icon to place below the text, or 0 * * @hide */
public void setTextViewCompoundDrawables(int viewId, Icon left, Icon top, Icon right, Icon bottom) { addAction(new TextViewDrawableAction(viewId, false, left, top, right, bottom)); }
Params:
  • viewId – The id of the view whose text should change
  • start – an Icon to place before the text (relative to the layout direction), or 0
  • top – an Icon to place above the text, or 0
  • end – an Icon to place after the text, or 0
  • bottom – an Icon to place below the text, or 0
@hide
/** * Equivalent to calling {@link * TextView#setCompoundDrawablesRelativeWithIntrinsicBounds(Drawable, Drawable, Drawable, Drawable)} * using the drawables yielded by {@link Icon#loadDrawable(Context)}. * * @param viewId The id of the view whose text should change * @param start an Icon to place before the text (relative to the * layout direction), or 0 * @param top an Icon to place above the text, or 0 * @param end an Icon to place after the text, or 0 * @param bottom an Icon to place below the text, or 0 * * @hide */
public void setTextViewCompoundDrawablesRelative(int viewId, Icon start, Icon top, Icon end, Icon bottom) { addAction(new TextViewDrawableAction(viewId, true, start, top, end, bottom)); }
Equivalent to calling ImageView.setImageResource(int)
Params:
  • viewId – The id of the view whose drawable should change
  • srcId – The new resource id for the drawable
/** * Equivalent to calling {@link ImageView#setImageResource(int)} * * @param viewId The id of the view whose drawable should change * @param srcId The new resource id for the drawable */
public void setImageViewResource(int viewId, int srcId) { setInt(viewId, "setImageResource", srcId); }
Equivalent to calling ImageView.setImageURI(Uri)
Params:
  • viewId – The id of the view whose drawable should change
  • uri – The Uri for the image
/** * Equivalent to calling {@link ImageView#setImageURI(Uri)} * * @param viewId The id of the view whose drawable should change * @param uri The Uri for the image */
public void setImageViewUri(int viewId, Uri uri) { setUri(viewId, "setImageURI", uri); }
Equivalent to calling ImageView.setImageBitmap(Bitmap)
Params:
  • viewId – The id of the view whose bitmap should change
  • bitmap – The new Bitmap for the drawable
/** * Equivalent to calling {@link ImageView#setImageBitmap(Bitmap)} * * @param viewId The id of the view whose bitmap should change * @param bitmap The new Bitmap for the drawable */
public void setImageViewBitmap(int viewId, Bitmap bitmap) { setBitmap(viewId, "setImageBitmap", bitmap); }
Equivalent to calling ImageView.setImageIcon(Icon)
Params:
  • viewId – The id of the view whose bitmap should change
  • icon – The new Icon for the ImageView
/** * Equivalent to calling {@link ImageView#setImageIcon(Icon)} * * @param viewId The id of the view whose bitmap should change * @param icon The new Icon for the ImageView */
public void setImageViewIcon(int viewId, Icon icon) { setIcon(viewId, "setImageIcon", icon); }
Equivalent to calling AdapterView.setEmptyView(View)
Params:
  • viewId – The id of the view on which to set the empty view
  • emptyViewId – The view id of the empty view
/** * Equivalent to calling {@link AdapterView#setEmptyView(View)} * * @param viewId The id of the view on which to set the empty view * @param emptyViewId The view id of the empty view */
public void setEmptyView(int viewId, int emptyViewId) { addAction(new SetEmptyView(viewId, emptyViewId)); }
Params:
  • viewId – The id of the Chronometer to change
  • base – The time at which the timer would have read 0:00. This time should be based off of SystemClock.elapsedRealtime().
  • format – The Chronometer format string, or null to simply display the timer value.
  • started – True if you want the clock to be started, false if not.
See Also:
/** * Equivalent to calling {@link Chronometer#setBase Chronometer.setBase}, * {@link Chronometer#setFormat Chronometer.setFormat}, * and {@link Chronometer#start Chronometer.start()} or * {@link Chronometer#stop Chronometer.stop()}. * * @param viewId The id of the {@link Chronometer} to change * @param base The time at which the timer would have read 0:00. This * time should be based off of * {@link android.os.SystemClock#elapsedRealtime SystemClock.elapsedRealtime()}. * @param format The Chronometer format string, or null to * simply display the timer value. * @param started True if you want the clock to be started, false if not. * * @see #setChronometerCountDown(int, boolean) */
public void setChronometer(int viewId, long base, String format, boolean started) { setLong(viewId, "setBase", base); setString(viewId, "setFormat", format); setBoolean(viewId, "setStarted", started); }
Equivalent to calling Chronometer.setCountDown on the chronometer with the given viewId.
Params:
  • viewId – The id of the Chronometer to change
  • isCountDown – True if you want the chronometer to count down to base instead of counting up.
/** * Equivalent to calling {@link Chronometer#setCountDown(boolean) Chronometer.setCountDown} on * the chronometer with the given viewId. * * @param viewId The id of the {@link Chronometer} to change * @param isCountDown True if you want the chronometer to count down to base instead of * counting up. */
public void setChronometerCountDown(int viewId, boolean isCountDown) { setBoolean(viewId, "setCountDown", isCountDown); }
Equivalent to calling ProgressBar.setMax, ProgressBar.setProgress, and ProgressBar.setIndeterminate If indeterminate is true, then the values for max and progress are ignored.
Params:
  • viewId – The id of the ProgressBar to change
  • max – The 100% value for the progress bar
  • progress – The current value of the progress bar.
  • indeterminate – True if the progress bar is indeterminate, false if not.
/** * Equivalent to calling {@link ProgressBar#setMax ProgressBar.setMax}, * {@link ProgressBar#setProgress ProgressBar.setProgress}, and * {@link ProgressBar#setIndeterminate ProgressBar.setIndeterminate} * * If indeterminate is true, then the values for max and progress are ignored. * * @param viewId The id of the {@link ProgressBar} to change * @param max The 100% value for the progress bar * @param progress The current value of the progress bar. * @param indeterminate True if the progress bar is indeterminate, * false if not. */
public void setProgressBar(int viewId, int max, int progress, boolean indeterminate) { setBoolean(viewId, "setIndeterminate", indeterminate); if (!indeterminate) { setInt(viewId, "setMax", max); setInt(viewId, "setProgress", progress); } }
Equivalent to calling View.setOnClickListener(OnClickListener) to launch the provided PendingIntent. The source bounds (Intent.getSourceBounds()) of the intent will be set to the bounds of the clicked view in screen space. Note that any activity options associated with the pendingIntent may get overridden before starting the intent. When setting the on-click action of items within collections (eg. ListView, StackView etc.), this method will not work. Instead, use setPendingIntentTemplate(int, PendingIntent) in conjunction with setOnClickFillInIntent(int, Intent).
Params:
  • viewId – The id of the view that will trigger the PendingIntent when clicked
  • pendingIntent – The PendingIntent to send when user clicks
/** * Equivalent to calling * {@link android.view.View#setOnClickListener(android.view.View.OnClickListener)} * to launch the provided {@link PendingIntent}. The source bounds * ({@link Intent#getSourceBounds()}) of the intent will be set to the bounds of the clicked * view in screen space. * Note that any activity options associated with the pendingIntent may get overridden * before starting the intent. * * When setting the on-click action of items within collections (eg. {@link ListView}, * {@link StackView} etc.), this method will not work. Instead, use {@link * RemoteViews#setPendingIntentTemplate(int, PendingIntent)} in conjunction with * {@link RemoteViews#setOnClickFillInIntent(int, Intent)}. * * @param viewId The id of the view that will trigger the {@link PendingIntent} when clicked * @param pendingIntent The {@link PendingIntent} to send when user clicks */
public void setOnClickPendingIntent(int viewId, PendingIntent pendingIntent) { addAction(new SetOnClickPendingIntent(viewId, pendingIntent)); }
When using collections (eg. ListView, StackView etc.) in widgets, it is very costly to set PendingIntents on the individual items, and is hence not permitted. Instead this method should be used to set a single PendingIntent template on the collection, and individual items can differentiate their on-click behavior using setOnClickFillInIntent(int, Intent).
Params:
  • viewId – The id of the collection who's children will use this PendingIntent template when clicked
  • pendingIntentTemplate – The PendingIntent to be combined with extras specified by a child of viewId and executed when that child is clicked
/** * When using collections (eg. {@link ListView}, {@link StackView} etc.) in widgets, it is very * costly to set PendingIntents on the individual items, and is hence not permitted. Instead * this method should be used to set a single PendingIntent template on the collection, and * individual items can differentiate their on-click behavior using * {@link RemoteViews#setOnClickFillInIntent(int, Intent)}. * * @param viewId The id of the collection who's children will use this PendingIntent template * when clicked * @param pendingIntentTemplate The {@link PendingIntent} to be combined with extras specified * by a child of viewId and executed when that child is clicked */
public void setPendingIntentTemplate(int viewId, PendingIntent pendingIntentTemplate) { addAction(new SetPendingIntentTemplate(viewId, pendingIntentTemplate)); }
When using collections (eg. ListView, StackView etc.) in widgets, it is very costly to set PendingIntents on the individual items, and is hence not permitted. Instead a single PendingIntent template can be set on the collection, see setPendingIntentTemplate(int, PendingIntent), and the individual on-click action of a given item can be distinguished by setting a fillInIntent on that item. The fillInIntent is then combined with the PendingIntent template in order to determine the final intent which will be executed when the item is clicked. This works as follows: any fields which are left blank in the PendingIntent template, but are provided by the fillInIntent will be overwritten, and the resulting PendingIntent will be used. The rest of the PendingIntent template will then be filled in with the associated fields that are set in fillInIntent. See Intent.fillIn(Intent, int) for more details.
Params:
  • viewId – The id of the view on which to set the fillInIntent
  • fillInIntent – The intent which will be combined with the parent's PendingIntent in order to determine the on-click behavior of the view specified by viewId
/** * When using collections (eg. {@link ListView}, {@link StackView} etc.) in widgets, it is very * costly to set PendingIntents on the individual items, and is hence not permitted. Instead * a single PendingIntent template can be set on the collection, see {@link * RemoteViews#setPendingIntentTemplate(int, PendingIntent)}, and the individual on-click * action of a given item can be distinguished by setting a fillInIntent on that item. The * fillInIntent is then combined with the PendingIntent template in order to determine the final * intent which will be executed when the item is clicked. This works as follows: any fields * which are left blank in the PendingIntent template, but are provided by the fillInIntent * will be overwritten, and the resulting PendingIntent will be used. The rest * of the PendingIntent template will then be filled in with the associated fields that are * set in fillInIntent. See {@link Intent#fillIn(Intent, int)} for more details. * * @param viewId The id of the view on which to set the fillInIntent * @param fillInIntent The intent which will be combined with the parent's PendingIntent * in order to determine the on-click behavior of the view specified by viewId */
public void setOnClickFillInIntent(int viewId, Intent fillInIntent) { addAction(new SetOnClickFillInIntent(viewId, fillInIntent)); }
Params:
  • viewId – The id of the view that contains the target Drawable
  • targetBackground – If true, apply these parameters to the Drawable returned by View.getBackground(). Otherwise, assume the target view is an ImageView and apply them to ImageView.getDrawable().
  • colorFilter – Specify a color for a ColorFilter for this drawable. This will be ignored if mode is null.
  • mode – Specify a PorterDuff mode for this drawable, or null to leave unchanged.
@hide Equivalent to calling Drawable.setColorFilter(int, Mode), on the Drawable of a given view.

/** * @hide * Equivalent to calling * {@link Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)}, * on the {@link Drawable} of a given view. * <p> * * @param viewId The id of the view that contains the target * {@link Drawable} * @param targetBackground If true, apply these parameters to the * {@link Drawable} returned by * {@link android.view.View#getBackground()}. Otherwise, assume * the target view is an {@link ImageView} and apply them to * {@link ImageView#getDrawable()}. * @param colorFilter Specify a color for a * {@link android.graphics.ColorFilter} for this drawable. This will be ignored if * {@code mode} is {@code null}. * @param mode Specify a PorterDuff mode for this drawable, or null to leave * unchanged. */
public void setDrawableTint(int viewId, boolean targetBackground, int colorFilter, @NonNull PorterDuff.Mode mode) { addAction(new SetDrawableTint(viewId, targetBackground, colorFilter, mode)); }
Params:
  • viewId – The id of the view whose tint should change
  • tint – the tint to apply, may be null to clear tint
@hide Equivalent to calling ProgressBar.setProgressTintList.
/** * @hide * Equivalent to calling {@link android.widget.ProgressBar#setProgressTintList}. * * @param viewId The id of the view whose tint should change * @param tint the tint to apply, may be {@code null} to clear tint */
public void setProgressTintList(int viewId, ColorStateList tint) { addAction(new ReflectionAction(viewId, "setProgressTintList", ReflectionAction.COLOR_STATE_LIST, tint)); }
Params:
  • viewId – The id of the view whose tint should change
  • tint – the tint to apply, may be null to clear tint
@hide Equivalent to calling ProgressBar.setProgressBackgroundTintList.
/** * @hide * Equivalent to calling {@link android.widget.ProgressBar#setProgressBackgroundTintList}. * * @param viewId The id of the view whose tint should change * @param tint the tint to apply, may be {@code null} to clear tint */
public void setProgressBackgroundTintList(int viewId, ColorStateList tint) { addAction(new ReflectionAction(viewId, "setProgressBackgroundTintList", ReflectionAction.COLOR_STATE_LIST, tint)); }
Params:
  • viewId – The id of the view whose tint should change
  • tint – the tint to apply, may be null to clear tint
@hide Equivalent to calling ProgressBar.setIndeterminateTintList.
/** * @hide * Equivalent to calling {@link android.widget.ProgressBar#setIndeterminateTintList}. * * @param viewId The id of the view whose tint should change * @param tint the tint to apply, may be {@code null} to clear tint */
public void setProgressIndeterminateTintList(int viewId, ColorStateList tint) { addAction(new ReflectionAction(viewId, "setIndeterminateTintList", ReflectionAction.COLOR_STATE_LIST, tint)); }
Equivalent to calling TextView.setTextColor(int).
Params:
  • viewId – The id of the view whose text color should change
  • color – Sets the text color for all the states (normal, selected, focused) to be this color.
/** * Equivalent to calling {@link android.widget.TextView#setTextColor(int)}. * * @param viewId The id of the view whose text color should change * @param color Sets the text color for all the states (normal, selected, * focused) to be this color. */
public void setTextColor(int viewId, @ColorInt int color) { setInt(viewId, "setTextColor", color); }
Params:
  • viewId – The id of the view whose text color should change
  • colors – the text colors to set
@hide Equivalent to calling TextView.setTextColor(ColorStateList).
/** * @hide * Equivalent to calling {@link android.widget.TextView#setTextColor(ColorStateList)}. * * @param viewId The id of the view whose text color should change * @param colors the text colors to set */
public void setTextColor(int viewId, @ColorInt ColorStateList colors) { addAction(new ReflectionAction(viewId, "setTextColor", ReflectionAction.COLOR_STATE_LIST, colors)); }
Params:
  • appWidgetId – The id of the app widget which contains the specified view. (This parameter is ignored in this deprecated method)
  • viewId – The id of the AdapterView
  • intent – The intent of the service which will be providing data to the RemoteViewsAdapter
Deprecated:This method has been deprecated. See setRemoteAdapter(int, Intent)
/** * Equivalent to calling {@link android.widget.AbsListView#setRemoteViewsAdapter(Intent)}. * * @param appWidgetId The id of the app widget which contains the specified view. (This * parameter is ignored in this deprecated method) * @param viewId The id of the {@link AdapterView} * @param intent The intent of the service which will be * providing data to the RemoteViewsAdapter * @deprecated This method has been deprecated. See * {@link android.widget.RemoteViews#setRemoteAdapter(int, Intent)} */
@Deprecated public void setRemoteAdapter(int appWidgetId, int viewId, Intent intent) { setRemoteAdapter(viewId, intent); }
Equivalent to calling AbsListView.setRemoteViewsAdapter(Intent). Can only be used for App Widgets.
Params:
  • viewId – The id of the AdapterView
  • intent – The intent of the service which will be providing data to the RemoteViewsAdapter
/** * Equivalent to calling {@link android.widget.AbsListView#setRemoteViewsAdapter(Intent)}. * Can only be used for App Widgets. * * @param viewId The id of the {@link AdapterView} * @param intent The intent of the service which will be * providing data to the RemoteViewsAdapter */
public void setRemoteAdapter(int viewId, Intent intent) { addAction(new SetRemoteViewsAdapterIntent(viewId, intent)); }
Creates a simple Adapter for the viewId specified. The viewId must point to an AdapterView, ie. ListView, GridView, StackView or AdapterViewAnimator. This is a simpler but less flexible approach to populating collection widgets. Its use is encouraged for most scenarios, as long as the total memory within the list of RemoteViews is relatively small (ie. doesn't contain large or numerous Bitmaps, see setImageViewBitmap). In the case of numerous images, the use of API is still possible by setting image URIs instead of Bitmaps, see setImageViewUri. This API is supported in the compatibility library for previous API levels, see RemoteViewsCompat.
Params:
  • viewId – The id of the AdapterView
  • list – The list of RemoteViews which will populate the view specified by viewId.
  • viewTypeCount – The maximum number of unique layout id's used to construct the list of RemoteViews. This count cannot change during the life-cycle of a given widget, so this parameter should account for the maximum possible number of types that may appear in the See Adapter.getViewTypeCount().
@hide
/** * Creates a simple Adapter for the viewId specified. The viewId must point to an AdapterView, * ie. {@link ListView}, {@link GridView}, {@link StackView} or {@link AdapterViewAnimator}. * This is a simpler but less flexible approach to populating collection widgets. Its use is * encouraged for most scenarios, as long as the total memory within the list of RemoteViews * is relatively small (ie. doesn't contain large or numerous Bitmaps, see {@link * RemoteViews#setImageViewBitmap}). In the case of numerous images, the use of API is still * possible by setting image URIs instead of Bitmaps, see {@link RemoteViews#setImageViewUri}. * * This API is supported in the compatibility library for previous API levels, see * RemoteViewsCompat. * * @param viewId The id of the {@link AdapterView} * @param list The list of RemoteViews which will populate the view specified by viewId. * @param viewTypeCount The maximum number of unique layout id's used to construct the list of * RemoteViews. This count cannot change during the life-cycle of a given widget, so this * parameter should account for the maximum possible number of types that may appear in the * See {@link Adapter#getViewTypeCount()}. * * @hide */
public void setRemoteAdapter(int viewId, ArrayList<RemoteViews> list, int viewTypeCount) { addAction(new SetRemoteViewsAdapterList(viewId, list, viewTypeCount)); }
Equivalent to calling ListView.smoothScrollToPosition(int).
Params:
  • viewId – The id of the view to change
  • position – Scroll to this adapter position
/** * Equivalent to calling {@link ListView#smoothScrollToPosition(int)}. * * @param viewId The id of the view to change * @param position Scroll to this adapter position */
public void setScrollPosition(int viewId, int position) { setInt(viewId, "smoothScrollToPosition", position); }
Equivalent to calling ListView.smoothScrollByOffset(int).
Params:
  • viewId – The id of the view to change
  • offset – Scroll by this adapter position offset
/** * Equivalent to calling {@link ListView#smoothScrollByOffset(int)}. * * @param viewId The id of the view to change * @param offset Scroll by this adapter position offset */
public void setRelativeScrollPosition(int viewId, int offset) { setInt(viewId, "smoothScrollByOffset", offset); }
Equivalent to calling View.setPadding(int, int, int, int).
Params:
  • viewId – The id of the view to change
  • left – the left padding in pixels
  • top – the top padding in pixels
  • right – the right padding in pixels
  • bottom – the bottom padding in pixels
/** * Equivalent to calling {@link android.view.View#setPadding(int, int, int, int)}. * * @param viewId The id of the view to change * @param left the left padding in pixels * @param top the top padding in pixels * @param right the right padding in pixels * @param bottom the bottom padding in pixels */
public void setViewPadding(int viewId, int left, int top, int right, int bottom) { addAction(new ViewPaddingAction(viewId, left, top, right, bottom)); }
Params:
  • viewId – The id of the view to change
  • endMarginDimen – a dimen resource to read the margin from or 0 to clear the margin.
@hide Equivalent to calling MarginLayoutParams.setMarginEnd(int). Only works if the View.getLayoutParams() supports margins. Hidden for now since we don't want to support this for all different layout margins yet.
/** * @hide * Equivalent to calling {@link android.view.ViewGroup.MarginLayoutParams#setMarginEnd(int)}. * Only works if the {@link View#getLayoutParams()} supports margins. * Hidden for now since we don't want to support this for all different layout margins yet. * * @param viewId The id of the view to change * @param endMarginDimen a dimen resource to read the margin from or 0 to clear the margin. */
public void setViewLayoutMarginEndDimen(int viewId, @DimenRes int endMarginDimen) { addAction(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_MARGIN_END_DIMEN, endMarginDimen)); }
Equivalent to calling MarginLayoutParams.setMarginEnd(int). Only works if the View.getLayoutParams() supports margins. Hidden for now since we don't want to support this for all different layout margins yet.
Params:
  • viewId – The id of the view to change
  • endMargin – a value in pixels for the end margin.
@hide
/** * Equivalent to calling {@link android.view.ViewGroup.MarginLayoutParams#setMarginEnd(int)}. * Only works if the {@link View#getLayoutParams()} supports margins. * Hidden for now since we don't want to support this for all different layout margins yet. * * @param viewId The id of the view to change * @param endMargin a value in pixels for the end margin. * @hide */
public void setViewLayoutMarginEnd(int viewId, @DimenRes int endMargin) { addAction(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_MARGIN_END, endMargin)); }
Equivalent to setting MarginLayoutParams.bottomMargin.
Params:
  • bottomMarginDimen – a dimen resource to read the margin from or 0 to clear the margin.
@hide
/** * Equivalent to setting {@link android.view.ViewGroup.MarginLayoutParams#bottomMargin}. * * @param bottomMarginDimen a dimen resource to read the margin from or 0 to clear the margin. * @hide */
public void setViewLayoutMarginBottomDimen(int viewId, @DimenRes int bottomMarginDimen) { addAction(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_MARGIN_BOTTOM_DIMEN, bottomMarginDimen)); }
Equivalent to setting LayoutParams.width.
Params:
  • layoutWidth – one of 0, MATCH_PARENT or WRAP_CONTENT. Other sizes are not allowed because they behave poorly when the density changes.
@hide
/** * Equivalent to setting {@link android.view.ViewGroup.LayoutParams#width}. * * @param layoutWidth one of 0, MATCH_PARENT or WRAP_CONTENT. Other sizes are not allowed * because they behave poorly when the density changes. * @hide */
public void setViewLayoutWidth(int viewId, int layoutWidth) { if (layoutWidth != 0 && layoutWidth != ViewGroup.LayoutParams.MATCH_PARENT && layoutWidth != ViewGroup.LayoutParams.WRAP_CONTENT) { throw new IllegalArgumentException("Only supports 0, WRAP_CONTENT and MATCH_PARENT"); } mActions.add(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_WIDTH, layoutWidth)); }
Call a method taking one boolean on a view in the layout for this RemoteViews.
Params:
  • viewId – The id of the view on which to call the method.
  • methodName – The name of the method to call.
  • value – The value to pass to the method.
/** * Call a method taking one boolean on a view in the layout for this RemoteViews. * * @param viewId The id of the view on which to call the method. * @param methodName The name of the method to call. * @param value The value to pass to the method. */
public void setBoolean(int viewId, String methodName, boolean value) { addAction(new ReflectionAction(viewId, methodName, ReflectionAction.BOOLEAN, value)); }
Call a method taking one byte on a view in the layout for this RemoteViews.
Params:
  • viewId – The id of the view on which to call the method.
  • methodName – The name of the method to call.
  • value – The value to pass to the method.
/** * Call a method taking one byte on a view in the layout for this RemoteViews. * * @param viewId The id of the view on which to call the method. * @param methodName The name of the method to call. * @param value The value to pass to the method. */
public void setByte(int viewId, String methodName, byte value) { addAction(new ReflectionAction(viewId, methodName, ReflectionAction.BYTE, value)); }
Call a method taking one short on a view in the layout for this RemoteViews.
Params:
  • viewId – The id of the view on which to call the method.
  • methodName – The name of the method to call.
  • value – The value to pass to the method.
/** * Call a method taking one short on a view in the layout for this RemoteViews. * * @param viewId The id of the view on which to call the method. * @param methodName The name of the method to call. * @param value The value to pass to the method. */
public void setShort(int viewId, String methodName, short value) { addAction(new ReflectionAction(viewId, methodName, ReflectionAction.SHORT, value)); }
Call a method taking one int on a view in the layout for this RemoteViews.
Params:
  • viewId – The id of the view on which to call the method.
  • methodName – The name of the method to call.
  • value – The value to pass to the method.
/** * Call a method taking one int on a view in the layout for this RemoteViews. * * @param viewId The id of the view on which to call the method. * @param methodName The name of the method to call. * @param value The value to pass to the method. */
public void setInt(int viewId, String methodName, int value) { addAction(new ReflectionAction(viewId, methodName, ReflectionAction.INT, value)); }
Call a method taking one ColorStateList on a view in the layout for this RemoteViews.
Params:
  • viewId – The id of the view on which to call the method.
  • methodName – The name of the method to call.
  • value – The value to pass to the method.
@hide
/** * Call a method taking one ColorStateList on a view in the layout for this RemoteViews. * * @param viewId The id of the view on which to call the method. * @param methodName The name of the method to call. * @param value The value to pass to the method. * * @hide */
public void setColorStateList(int viewId, String methodName, ColorStateList value) { addAction(new ReflectionAction(viewId, methodName, ReflectionAction.COLOR_STATE_LIST, value)); }
Call a method taking one long on a view in the layout for this RemoteViews.
Params:
  • viewId – The id of the view on which to call the method.
  • methodName – The name of the method to call.
  • value – The value to pass to the method.
/** * Call a method taking one long on a view in the layout for this RemoteViews. * * @param viewId The id of the view on which to call the method. * @param methodName The name of the method to call. * @param value The value to pass to the method. */
public void setLong(int viewId, String methodName, long value) { addAction(new ReflectionAction(viewId, methodName, ReflectionAction.LONG, value)); }
Call a method taking one float on a view in the layout for this RemoteViews.
Params:
  • viewId – The id of the view on which to call the method.
  • methodName – The name of the method to call.
  • value – The value to pass to the method.
/** * Call a method taking one float on a view in the layout for this RemoteViews. * * @param viewId The id of the view on which to call the method. * @param methodName The name of the method to call. * @param value The value to pass to the method. */
public void setFloat(int viewId, String methodName, float value) { addAction(new ReflectionAction(viewId, methodName, ReflectionAction.FLOAT, value)); }
Call a method taking one double on a view in the layout for this RemoteViews.
Params:
  • viewId – The id of the view on which to call the method.
  • methodName – The name of the method to call.
  • value – The value to pass to the method.
/** * Call a method taking one double on a view in the layout for this RemoteViews. * * @param viewId The id of the view on which to call the method. * @param methodName The name of the method to call. * @param value The value to pass to the method. */
public void setDouble(int viewId, String methodName, double value) { addAction(new ReflectionAction(viewId, methodName, ReflectionAction.DOUBLE, value)); }
Call a method taking one char on a view in the layout for this RemoteViews.
Params:
  • viewId – The id of the view on which to call the method.
  • methodName – The name of the method to call.
  • value – The value to pass to the method.
/** * Call a method taking one char on a view in the layout for this RemoteViews. * * @param viewId The id of the view on which to call the method. * @param methodName The name of the method to call. * @param value The value to pass to the method. */
public void setChar(int viewId, String methodName, char value) { addAction(new ReflectionAction(viewId, methodName, ReflectionAction.CHAR, value)); }
Call a method taking one String on a view in the layout for this RemoteViews.
Params:
  • viewId – The id of the view on which to call the method.
  • methodName – The name of the method to call.
  • value – The value to pass to the method.
/** * Call a method taking one String on a view in the layout for this RemoteViews. * * @param viewId The id of the view on which to call the method. * @param methodName The name of the method to call. * @param value The value to pass to the method. */
public void setString(int viewId, String methodName, String value) { addAction(new ReflectionAction(viewId, methodName, ReflectionAction.STRING, value)); }
Call a method taking one CharSequence on a view in the layout for this RemoteViews.
Params:
  • viewId – The id of the view on which to call the method.
  • methodName – The name of the method to call.
  • value – The value to pass to the method.
/** * Call a method taking one CharSequence on a view in the layout for this RemoteViews. * * @param viewId The id of the view on which to call the method. * @param methodName The name of the method to call. * @param value The value to pass to the method. */
public void setCharSequence(int viewId, String methodName, CharSequence value) { addAction(new ReflectionAction(viewId, methodName, ReflectionAction.CHAR_SEQUENCE, value)); }
Call a method taking one Uri on a view in the layout for this RemoteViews.
Params:
  • viewId – The id of the view on which to call the method.
  • methodName – The name of the method to call.
  • value – The value to pass to the method.
/** * Call a method taking one Uri on a view in the layout for this RemoteViews. * * @param viewId The id of the view on which to call the method. * @param methodName The name of the method to call. * @param value The value to pass to the method. */
public void setUri(int viewId, String methodName, Uri value) { if (value != null) { // Resolve any filesystem path before sending remotely value = value.getCanonicalUri(); if (StrictMode.vmFileUriExposureEnabled()) { value.checkFileUriExposed("RemoteViews.setUri()"); } } addAction(new ReflectionAction(viewId, methodName, ReflectionAction.URI, value)); }
Call a method taking one Bitmap on a view in the layout for this RemoteViews.
Params:
  • viewId – The id of the view on which to call the method.
  • methodName – The name of the method to call.
  • value – The value to pass to the method.
@more

The bitmap will be flattened into the parcel if this object is sent across processes, so it may end up using a lot of memory, and may be fairly slow.

/** * Call a method taking one Bitmap on a view in the layout for this RemoteViews. * @more * <p class="note">The bitmap will be flattened into the parcel if this object is * sent across processes, so it may end up using a lot of memory, and may be fairly slow.</p> * * @param viewId The id of the view on which to call the method. * @param methodName The name of the method to call. * @param value The value to pass to the method. */
public void setBitmap(int viewId, String methodName, Bitmap value) { addAction(new BitmapReflectionAction(viewId, methodName, value)); }
Call a method taking one Bundle on a view in the layout for this RemoteViews.
Params:
  • viewId – The id of the view on which to call the method.
  • methodName – The name of the method to call.
  • value – The value to pass to the method.
/** * Call a method taking one Bundle on a view in the layout for this RemoteViews. * * @param viewId The id of the view on which to call the method. * @param methodName The name of the method to call. * @param value The value to pass to the method. */
public void setBundle(int viewId, String methodName, Bundle value) { addAction(new ReflectionAction(viewId, methodName, ReflectionAction.BUNDLE, value)); }
Call a method taking one Intent on a view in the layout for this RemoteViews.
Params:
  • viewId – The id of the view on which to call the method.
  • methodName – The name of the method to call.
  • value – The Intent to pass the method.
/** * Call a method taking one Intent on a view in the layout for this RemoteViews. * * @param viewId The id of the view on which to call the method. * @param methodName The name of the method to call. * @param value The {@link android.content.Intent} to pass the method. */
public void setIntent(int viewId, String methodName, Intent value) { addAction(new ReflectionAction(viewId, methodName, ReflectionAction.INTENT, value)); }
Call a method taking one Icon on a view in the layout for this RemoteViews.
Params:
  • viewId – The id of the view on which to call the method.
  • methodName – The name of the method to call.
  • value – The Icon to pass the method.
/** * Call a method taking one Icon on a view in the layout for this RemoteViews. * * @param viewId The id of the view on which to call the method. * @param methodName The name of the method to call. * @param value The {@link android.graphics.drawable.Icon} to pass the method. */
public void setIcon(int viewId, String methodName, Icon value) { addAction(new ReflectionAction(viewId, methodName, ReflectionAction.ICON, value)); }
Equivalent to calling View.setContentDescription(CharSequence).
Params:
  • viewId – The id of the view whose content description should change.
  • contentDescription – The new content description for the view.
/** * Equivalent to calling View.setContentDescription(CharSequence). * * @param viewId The id of the view whose content description should change. * @param contentDescription The new content description for the view. */
public void setContentDescription(int viewId, CharSequence contentDescription) { setCharSequence(viewId, "setContentDescription", contentDescription); }
Params:
  • viewId – The id of the view whose before view in accessibility traversal to set.
  • nextId – The id of the next in the accessibility traversal.
/** * Equivalent to calling {@link android.view.View#setAccessibilityTraversalBefore(int)}. * * @param viewId The id of the view whose before view in accessibility traversal to set. * @param nextId The id of the next in the accessibility traversal. **/
public void setAccessibilityTraversalBefore(int viewId, int nextId) { setInt(viewId, "setAccessibilityTraversalBefore", nextId); }
Params:
  • viewId – The id of the view whose after view in accessibility traversal to set.
  • nextId – The id of the next in the accessibility traversal.
/** * Equivalent to calling {@link android.view.View#setAccessibilityTraversalAfter(int)}. * * @param viewId The id of the view whose after view in accessibility traversal to set. * @param nextId The id of the next in the accessibility traversal. **/
public void setAccessibilityTraversalAfter(int viewId, int nextId) { setInt(viewId, "setAccessibilityTraversalAfter", nextId); }
Equivalent to calling View.setLabelFor(int).
Params:
  • viewId – The id of the view whose property to set.
  • labeledId – The id of a view for which this view serves as a label.
/** * Equivalent to calling {@link View#setLabelFor(int)}. * * @param viewId The id of the view whose property to set. * @param labeledId The id of a view for which this view serves as a label. */
public void setLabelFor(int viewId, int labeledId) { setInt(viewId, "setLabelFor", labeledId); } private RemoteViews getRemoteViewsToApply(Context context) { if (hasLandscapeAndPortraitLayouts()) { int orientation = context.getResources().getConfiguration().orientation; if (orientation == Configuration.ORIENTATION_LANDSCAPE) { return mLandscape; } else { return mPortrait; } } return this; }
Set the theme used in apply() and applyASync().
@hide
/** * Set the theme used in apply() and applyASync(). * @hide */
public void setApplyTheme(@StyleRes int themeResId) { mApplyThemeResId = themeResId; }
Inflates the view hierarchy represented by this object and applies all of the actions.

Caller beware: this may throw

Params:
  • context – Default context to use
  • parent – Parent that the resulting view hierarchy will be attached to. This method does not attach the hierarchy. The caller should do so when appropriate.
Returns:The inflated view hierarchy
/** * Inflates the view hierarchy represented by this object and applies * all of the actions. * * <p><strong>Caller beware: this may throw</strong> * * @param context Default context to use * @param parent Parent that the resulting view hierarchy will be attached to. This method * does <strong>not</strong> attach the hierarchy. The caller should do so when appropriate. * @return The inflated view hierarchy */
public View apply(Context context, ViewGroup parent) { return apply(context, parent, null); }
@hide
/** @hide */
public View apply(Context context, ViewGroup parent, OnClickHandler handler) { RemoteViews rvToApply = getRemoteViewsToApply(context); View result = inflateView(context, rvToApply, parent); loadTransitionOverride(context, handler); rvToApply.performApply(result, parent, handler); return result; } private View inflateView(Context context, RemoteViews rv, ViewGroup parent) { // RemoteViews may be built by an application installed in another // user. So build a context that loads resources from that user but // still returns the current users userId so settings like data / time formats // are loaded without requiring cross user persmissions. final Context contextForResources = getContextForResources(context); Context inflationContext = new RemoteViewsContextWrapper(context, contextForResources); // If mApplyThemeResId is not given, Theme.DeviceDefault will be used. if (mApplyThemeResId != 0) { inflationContext = new ContextThemeWrapper(inflationContext, mApplyThemeResId); } LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); // Clone inflater so we load resources from correct context and // we don't add a filter to the static version returned by getSystemService. inflater = inflater.cloneInContext(inflationContext); inflater.setFilter(this); View v = inflater.inflate(rv.getLayoutId(), parent, false); v.setTagInternal(R.id.widget_frame, rv.getLayoutId()); return v; } private static void loadTransitionOverride(Context context, RemoteViews.OnClickHandler handler) { if (handler != null && context.getResources().getBoolean( com.android.internal.R.bool.config_overrideRemoteViewsActivityTransition)) { TypedArray windowStyle = context.getTheme().obtainStyledAttributes( com.android.internal.R.styleable.Window); int windowAnimations = windowStyle.getResourceId( com.android.internal.R.styleable.Window_windowAnimationStyle, 0); TypedArray windowAnimationStyle = context.obtainStyledAttributes( windowAnimations, com.android.internal.R.styleable.WindowAnimation); handler.setEnterAnimationId(windowAnimationStyle.getResourceId( com.android.internal.R.styleable. WindowAnimation_activityOpenRemoteViewsEnterAnimation, 0)); windowStyle.recycle(); windowAnimationStyle.recycle(); } }
Implement this interface to receive a callback when applyAsync or reapplyAsync is finished.
@hide
/** * Implement this interface to receive a callback when * {@link #applyAsync} or {@link #reapplyAsync} is finished. * @hide */
public interface OnViewAppliedListener { void onViewApplied(View v); void onError(Exception e); }
Applies the views asynchronously, moving as much of the task on the background thread as possible.
Params:
  • context – Default context to use
  • parent – Parent that the resulting view hierarchy will be attached to. This method does not attach the hierarchy. The caller should do so when appropriate.
  • listener – the callback to run when all actions have been applied. May be null.
  • executor – The executor to use. If null AsyncTask.THREAD_POOL_EXECUTOR is used.
See Also:
  • apply(Context, ViewGroup)
Returns:CancellationSignal
@hide
/** * Applies the views asynchronously, moving as much of the task on the background * thread as possible. * * @see #apply(Context, ViewGroup) * @param context Default context to use * @param parent Parent that the resulting view hierarchy will be attached to. This method * does <strong>not</strong> attach the hierarchy. The caller should do so when appropriate. * @param listener the callback to run when all actions have been applied. May be null. * @param executor The executor to use. If null {@link AsyncTask#THREAD_POOL_EXECUTOR} is used. * @return CancellationSignal * @hide */
public CancellationSignal applyAsync( Context context, ViewGroup parent, Executor executor, OnViewAppliedListener listener) { return applyAsync(context, parent, executor, listener, null); } private CancellationSignal startTaskOnExecutor(AsyncApplyTask task, Executor executor) { CancellationSignal cancelSignal = new CancellationSignal(); cancelSignal.setOnCancelListener(task); task.executeOnExecutor(executor == null ? AsyncTask.THREAD_POOL_EXECUTOR : executor); return cancelSignal; }
@hide
/** @hide */
public CancellationSignal applyAsync(Context context, ViewGroup parent, Executor executor, OnViewAppliedListener listener, OnClickHandler handler) { return startTaskOnExecutor(getAsyncApplyTask(context, parent, listener, handler), executor); } private AsyncApplyTask getAsyncApplyTask(Context context, ViewGroup parent, OnViewAppliedListener listener, OnClickHandler handler) { return new AsyncApplyTask(getRemoteViewsToApply(context), parent, context, listener, handler, null); } private class AsyncApplyTask extends AsyncTask<Void, Void, ViewTree> implements CancellationSignal.OnCancelListener { final RemoteViews mRV; final ViewGroup mParent; final Context mContext; final OnViewAppliedListener mListener; final OnClickHandler mHandler; private View mResult; private ViewTree mTree; private Action[] mActions; private Exception mError; private AsyncApplyTask( RemoteViews rv, ViewGroup parent, Context context, OnViewAppliedListener listener, OnClickHandler handler, View result) { mRV = rv; mParent = parent; mContext = context; mListener = listener; mHandler = handler; mResult = result; loadTransitionOverride(context, handler); } @Override protected ViewTree doInBackground(Void... params) { try { if (mResult == null) { mResult = inflateView(mContext, mRV, mParent); } mTree = new ViewTree(mResult); if (mRV.mActions != null) { int count = mRV.mActions.size(); mActions = new Action[count]; for (int i = 0; i < count && !isCancelled(); i++) { // TODO: check if isCancelled in nested views. mActions[i] = mRV.mActions.get(i).initActionAsync(mTree, mParent, mHandler); } } else { mActions = null; } return mTree; } catch (Exception e) { mError = e; return null; } } @Override protected void onPostExecute(ViewTree viewTree) { if (mError == null) { try { if (mActions != null) { OnClickHandler handler = mHandler == null ? DEFAULT_ON_CLICK_HANDLER : mHandler; for (Action a : mActions) { a.apply(viewTree.mRoot, mParent, handler); } } } catch (Exception e) { mError = e; } } if (mListener != null) { if (mError != null) { mListener.onError(mError); } else { mListener.onViewApplied(viewTree.mRoot); } } else if (mError != null) { if (mError instanceof ActionException) { throw (ActionException) mError; } else { throw new ActionException(mError); } } } @Override public void onCancel() { cancel(true); } }
Applies all of the actions to the provided view.

Caller beware: this may throw

Params:
/** * Applies all of the actions to the provided view. * * <p><strong>Caller beware: this may throw</strong> * * @param v The view to apply the actions to. This should be the result of * the {@link #apply(Context,ViewGroup)} call. */
public void reapply(Context context, View v) { reapply(context, v, null); }
@hide
/** @hide */
public void reapply(Context context, View v, OnClickHandler handler) { RemoteViews rvToApply = getRemoteViewsToApply(context); // In the case that a view has this RemoteViews applied in one orientation, is persisted // across orientation change, and has the RemoteViews re-applied in the new orientation, // we throw an exception, since the layouts may be completely unrelated. if (hasLandscapeAndPortraitLayouts()) { if ((Integer) v.getTag(R.id.widget_frame) != rvToApply.getLayoutId()) { throw new RuntimeException("Attempting to re-apply RemoteViews to a view that" + " that does not share the same root layout id."); } } rvToApply.performApply(v, (ViewGroup) v.getParent(), handler); }
Applies all the actions to the provided view, moving as much of the task on the background thread as possible.
Params:
  • context – Default context to use
  • v – The view to apply the actions to. This should be the result of the apply(Context, ViewGroup) call.
  • listener – the callback to run when all actions have been applied. May be null.
  • executor – The executor to use. If null AsyncTask.THREAD_POOL_EXECUTOR is used
See Also:
  • reapply(Context, View)
Returns:CancellationSignal
@hide
/** * Applies all the actions to the provided view, moving as much of the task on the background * thread as possible. * * @see #reapply(Context, View) * @param context Default context to use * @param v The view to apply the actions to. This should be the result of * the {@link #apply(Context,ViewGroup)} call. * @param listener the callback to run when all actions have been applied. May be null. * @param executor The executor to use. If null {@link AsyncTask#THREAD_POOL_EXECUTOR} is used * @return CancellationSignal * @hide */
public CancellationSignal reapplyAsync( Context context, View v, Executor executor, OnViewAppliedListener listener) { return reapplyAsync(context, v, executor, listener, null); }
@hide
/** @hide */
public CancellationSignal reapplyAsync(Context context, View v, Executor executor, OnViewAppliedListener listener, OnClickHandler handler) { RemoteViews rvToApply = getRemoteViewsToApply(context); // In the case that a view has this RemoteViews applied in one orientation, is persisted // across orientation change, and has the RemoteViews re-applied in the new orientation, // we throw an exception, since the layouts may be completely unrelated. if (hasLandscapeAndPortraitLayouts()) { if ((Integer) v.getTag(R.id.widget_frame) != rvToApply.getLayoutId()) { throw new RuntimeException("Attempting to re-apply RemoteViews to a view that" + " that does not share the same root layout id."); } } return startTaskOnExecutor(new AsyncApplyTask(rvToApply, (ViewGroup) v.getParent(), context, listener, handler, v), executor); } private void performApply(View v, ViewGroup parent, OnClickHandler handler) { if (mActions != null) { handler = handler == null ? DEFAULT_ON_CLICK_HANDLER : handler; final int count = mActions.size(); for (int i = 0; i < count; i++) { Action a = mActions.get(i); a.apply(v, parent, handler); } } }
Returns true if the RemoteViews contains potentially costly operations and should be applied asynchronously.
@hide
/** * Returns true if the RemoteViews contains potentially costly operations and should be * applied asynchronously. * * @hide */
public boolean prefersAsyncApply() { if (mActions != null) { final int count = mActions.size(); for (int i = 0; i < count; i++) { if (mActions.get(i).prefersAsyncApply()) { return true; } } } return false; } private Context getContextForResources(Context context) { if (mApplication != null) { if (context.getUserId() == UserHandle.getUserId(mApplication.uid) && context.getPackageName().equals(mApplication.packageName)) { return context; } try { return context.createApplicationContext(mApplication, Context.CONTEXT_RESTRICTED); } catch (NameNotFoundException e) { Log.e(LOG_TAG, "Package name " + mApplication.packageName + " not found"); } } return context; }
Returns the number of actions in this RemoteViews. Can be used as a sequence number.
@hide
/** * Returns the number of actions in this RemoteViews. Can be used as a sequence number. * * @hide */
public int getSequenceNumber() { return (mActions == null) ? 0 : mActions.size(); } /* (non-Javadoc) * Used to restrict the views which can be inflated * * @see android.view.LayoutInflater.Filter#onLoadClass(java.lang.Class) */ public boolean onLoadClass(Class clazz) { return clazz.isAnnotationPresent(RemoteView.class); } public int describeContents() { return 0; } public void writeToParcel(Parcel dest, int flags) { if (!hasLandscapeAndPortraitLayouts()) { dest.writeInt(MODE_NORMAL); // We only write the bitmap cache if we are the root RemoteViews, as this cache // is shared by all children. if (mIsRoot) { mBitmapCache.writeBitmapsToParcel(dest, flags); } if (!mIsRoot && (flags & PARCELABLE_ELIDE_DUPLICATES) != 0) { dest.writeInt(0); } else { dest.writeInt(1); mApplication.writeToParcel(dest, flags); } dest.writeInt(mLayoutId); dest.writeInt(mIsWidgetCollectionChild ? 1 : 0); writeActionsToParcel(dest); } else { dest.writeInt(MODE_HAS_LANDSCAPE_AND_PORTRAIT); // We only write the bitmap cache if we are the root RemoteViews, as this cache // is shared by all children. if (mIsRoot) { mBitmapCache.writeBitmapsToParcel(dest, flags); } mLandscape.writeToParcel(dest, flags); // Both RemoteViews already share the same package and user mPortrait.writeToParcel(dest, flags | PARCELABLE_ELIDE_DUPLICATES); } dest.writeInt(mReapplyDisallowed ? 1 : 0); } private void writeActionsToParcel(Parcel parcel) { int count; if (mActions != null) { count = mActions.size(); } else { count = 0; } parcel.writeInt(count); for (int i = 0; i < count; i++) { Action a = mActions.get(i); parcel.writeInt(a.getActionTag()); a.writeToParcel(parcel, a.hasSameAppInfo(mApplication) ? PARCELABLE_ELIDE_DUPLICATES : 0); } } private static ApplicationInfo getApplicationInfo(String packageName, int userId) { if (packageName == null) { return null; } // Get the application for the passed in package and user. Application application = ActivityThread.currentApplication(); if (application == null) { throw new IllegalStateException("Cannot create remote views out of an aplication."); } ApplicationInfo applicationInfo = application.getApplicationInfo(); if (UserHandle.getUserId(applicationInfo.uid) != userId || !applicationInfo.packageName.equals(packageName)) { try { Context context = application.getBaseContext().createPackageContextAsUser( packageName, 0, new UserHandle(userId)); applicationInfo = context.getApplicationInfo(); } catch (NameNotFoundException nnfe) { throw new IllegalArgumentException("No such package " + packageName); } } return applicationInfo; }
Returns true if the mApplication is same as the provided info.
@hide
/** * Returns true if the {@link #mApplication} is same as the provided info. * * @hide */
public boolean hasSameAppInfo(ApplicationInfo info) { return mApplication.packageName.equals(info.packageName) && mApplication.uid == info.uid; }
Parcelable.Creator that instantiates RemoteViews objects
/** * Parcelable.Creator that instantiates RemoteViews objects */
public static final Parcelable.Creator<RemoteViews> CREATOR = new Parcelable.Creator<RemoteViews>() { public RemoteViews createFromParcel(Parcel parcel) { return new RemoteViews(parcel); } public RemoteViews[] newArray(int size) { return new RemoteViews[size]; } };
A representation of the view hierarchy. Only views which have a valid ID are added and can be searched.
/** * A representation of the view hierarchy. Only views which have a valid ID are added * and can be searched. */
private static class ViewTree { private static final int INSERT_AT_END_INDEX = -1; private View mRoot; private ArrayList<ViewTree> mChildren; private ViewTree(View root) { mRoot = root; } public void createTree() { if (mChildren != null) { return; } mChildren = new ArrayList<>(); if (mRoot instanceof ViewGroup) { ViewGroup vg = (ViewGroup) mRoot; int count = vg.getChildCount(); for (int i = 0; i < count; i++) { addViewChild(vg.getChildAt(i)); } } } public ViewTree findViewTreeById(int id) { if (mRoot.getId() == id) { return this; } if (mChildren == null) { return null; } for (ViewTree tree : mChildren) { ViewTree result = tree.findViewTreeById(id); if (result != null) { return result; } } return null; } public void replaceView(View v) { mRoot = v; mChildren = null; createTree(); } public <T extends View> T findViewById(int id) { if (mChildren == null) { return mRoot.findViewById(id); } ViewTree tree = findViewTreeById(id); return tree == null ? null : (T) tree.mRoot; } public void addChild(ViewTree child) { addChild(child, INSERT_AT_END_INDEX); }
Adds the given ViewTree as a child at the given index.
Params:
  • index – The position at which to add the child or -1 to add last.
/** * Adds the given {@link ViewTree} as a child at the given index. * * @param index The position at which to add the child or -1 to add last. */
public void addChild(ViewTree child, int index) { if (mChildren == null) { mChildren = new ArrayList<>(); } child.createTree(); if (index == INSERT_AT_END_INDEX) { mChildren.add(child); return; } mChildren.add(index, child); } private void addViewChild(View v) { // ViewTree only contains Views which can be found using findViewById. // If isRootNamespace is true, this view is skipped. // @see ViewGroup#findViewTraversal(int) if (v.isRootNamespace()) { return; } final ViewTree target; // If the view has a valid id, i.e., if can be found using findViewById, add it to the // tree, otherwise skip this view and add its children instead. if (v.getId() != 0) { ViewTree tree = new ViewTree(v); mChildren.add(tree); target = tree; } else { target = this; } if (v instanceof ViewGroup) { if (target.mChildren == null) { target.mChildren = new ArrayList<>(); ViewGroup vg = (ViewGroup) v; int count = vg.getChildCount(); for (int i = 0; i < count; i++) { target.addViewChild(vg.getChildAt(i)); } } } } } }