blob: c571737cec8fd309379a87ee2352d5b3c55b4050 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.widget;
18
Tor Norbye80756e32015-03-02 09:39:27 -080019import android.annotation.ColorInt;
Adrian Roos2d5dbba2016-06-08 17:11:53 -070020import android.annotation.DimenRes;
Sunny Goyalc12d31c2018-11-12 16:29:18 -080021import android.annotation.IntDef;
22import android.annotation.LayoutRes;
Sunny Goyal5b153922017-09-21 21:00:36 -070023import android.annotation.NonNull;
Dake Gu36b86c22018-04-16 12:49:30 -070024import android.annotation.StyleRes;
Mathew Inwood978c6e22018-08-21 15:58:55 +010025import android.annotation.UnsupportedAppUsage;
Sunny Goyal43c97042018-08-23 15:21:26 -070026import android.app.Activity;
Winson Chungdc6f79b2012-04-17 17:27:31 -070027import android.app.ActivityOptions;
Svetoslav976e8bd2014-07-16 15:12:03 -070028import android.app.ActivityThread;
Svet Ganov0da85b62014-08-06 14:11:37 -070029import android.app.Application;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080030import android.app.PendingIntent;
Adrian Roosfe84e1f2015-11-04 15:55:39 -080031import android.app.RemoteInput;
Adam Cohen1480fdd2010-08-25 17:24:53 -070032import android.appwidget.AppWidgetHostView;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080033import android.content.Context;
Kenny Guy77320062014-08-27 21:37:15 +010034import android.content.ContextWrapper;
Dianne Hackbornfa82f222009-09-17 15:14:12 -070035import android.content.Intent;
36import android.content.IntentSender;
Adam Cohenffc46a52012-04-30 19:54:39 -070037import android.content.pm.ApplicationInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080038import android.content.pm.PackageManager.NameNotFoundException;
Jorim Jaggief72a192014-08-26 21:57:46 +020039import android.content.res.ColorStateList;
Adam Cohen5d200642012-04-24 10:43:31 -070040import android.content.res.Configuration;
Kenny Guy77320062014-08-27 21:37:15 +010041import android.content.res.Resources;
Gus Prevas1ed322b2015-09-17 17:34:46 -040042import android.content.res.TypedArray;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080043import android.graphics.Bitmap;
44import android.graphics.PorterDuff;
Joe Onorato75970652009-12-02 23:04:55 -080045import android.graphics.Rect;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080046import android.graphics.drawable.Drawable;
Dan Sandlera22a3802015-05-13 00:12:47 -040047import android.graphics.drawable.Icon;
Gus Prevas9cc96602018-10-11 11:24:23 -040048import android.graphics.drawable.RippleDrawable;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080049import android.net.Uri;
Sunny Goyaldd292f42015-12-02 14:29:27 -080050import android.os.AsyncTask;
Sunny Goyal692f8c92016-11-11 09:19:14 -080051import android.os.Binder;
Adam Cohenffc46a52012-04-30 19:54:39 -070052import android.os.Build;
Bjorn Bringertd755b062010-01-06 17:15:37 +000053import android.os.Bundle;
Sunny Goyaldd292f42015-12-02 14:29:27 -080054import android.os.CancellationSignal;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080055import android.os.Parcel;
56import android.os.Parcelable;
Sunny Goyal692f8c92016-11-11 09:19:14 -080057import android.os.Process;
Jeff Sharkeya14acd22013-04-02 18:27:45 -070058import android.os.StrictMode;
Jeff Sharkey6d515712012-09-20 16:06:08 -070059import android.os.UserHandle;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080060import android.text.TextUtils;
Romain Guye4d4e202013-07-22 13:02:02 -070061import android.util.ArrayMap;
Sunny Goyal43c97042018-08-23 15:21:26 -070062import android.util.IntArray;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080063import android.util.Log;
Sunny Goyal43c97042018-08-23 15:21:26 -070064import android.util.Pair;
Dake Gu36b86c22018-04-16 12:49:30 -070065import android.view.ContextThemeWrapper;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080066import android.view.LayoutInflater;
Winson Chungdc6f79b2012-04-17 17:27:31 -070067import android.view.LayoutInflater.Filter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080068import android.view.RemotableViewMethod;
69import android.view.View;
Winson Chungdc6f79b2012-04-17 17:27:31 -070070import android.view.ViewGroup;
Sunny Goyal7b0e2c72016-11-03 14:48:05 -070071import android.view.ViewStub;
Adam Cohena32edd42010-10-26 10:35:01 -070072import android.widget.AdapterView.OnItemClickListener;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080073
Gus Prevas1ed322b2015-09-17 17:34:46 -040074import com.android.internal.R;
Lucas Dupina291d192018-06-07 13:59:42 -070075import com.android.internal.util.ContrastColorUtil;
Adrian Roos7da889d2016-03-16 18:38:58 -070076import com.android.internal.util.Preconditions;
Gus Prevas1ed322b2015-09-17 17:34:46 -040077
Winson Chungdc6f79b2012-04-17 17:27:31 -070078import java.lang.annotation.ElementType;
79import java.lang.annotation.Retention;
80import java.lang.annotation.RetentionPolicy;
81import java.lang.annotation.Target;
Sunny Goyal271e3222017-08-29 16:05:47 -070082import java.lang.invoke.MethodHandle;
83import java.lang.invoke.MethodHandles;
84import java.lang.invoke.MethodType;
Winson Chungdc6f79b2012-04-17 17:27:31 -070085import java.lang.reflect.Method;
86import java.util.ArrayList;
Adam Cohenfbe44b72012-09-19 20:36:23 -070087import java.util.HashMap;
Adrian Roosfb921842017-10-26 14:49:56 +020088import java.util.Map;
Narayan Kamath607223f2018-02-19 14:09:02 +000089import java.util.Objects;
Selim Cinek87c31532017-08-18 18:53:44 -070090import java.util.Stack;
Sunny Goyaldd292f42015-12-02 14:29:27 -080091import java.util.concurrent.Executor;
Jeff Sharkey23b31182018-04-18 21:32:12 -060092import java.util.function.Consumer;
Winson Chungdc6f79b2012-04-17 17:27:31 -070093
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080094/**
95 * A class that describes a view hierarchy that can be displayed in
96 * another process. The hierarchy is inflated from a layout resource
97 * file, and this class provides some basic operations for modifying
98 * the content of the inflated hierarchy.
Scott Main5b7903c2017-11-20 18:16:01 -080099 *
100 * <p>{@code RemoteViews} is limited to support for the following layouts:</p>
101 * <ul>
102 * <li>{@link android.widget.AdapterViewFlipper}</li>
103 * <li>{@link android.widget.FrameLayout}</li>
104 * <li>{@link android.widget.GridLayout}</li>
105 * <li>{@link android.widget.GridView}</li>
106 * <li>{@link android.widget.LinearLayout}</li>
107 * <li>{@link android.widget.ListView}</li>
108 * <li>{@link android.widget.RelativeLayout}</li>
109 * <li>{@link android.widget.StackView}</li>
110 * <li>{@link android.widget.ViewFlipper}</li>
111 * </ul>
112 * <p>And the following widgets:</p>
113 * <ul>
114 * <li>{@link android.widget.AnalogClock}</li>
115 * <li>{@link android.widget.Button}</li>
116 * <li>{@link android.widget.Chronometer}</li>
117 * <li>{@link android.widget.ImageButton}</li>
118 * <li>{@link android.widget.ImageView}</li>
119 * <li>{@link android.widget.ProgressBar}</li>
120 * <li>{@link android.widget.TextClock}</li>
121 * <li>{@link android.widget.TextView}</li>
122 * </ul>
123 * <p>Descendants of these classes are not supported.</p>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800124 */
125public class RemoteViews implements Parcelable, Filter {
Jim Millere667a7a2012-08-09 19:22:32 -0700126
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800127 private static final String LOG_TAG = "RemoteViews";
Jim Millere667a7a2012-08-09 19:22:32 -0700128
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800129 /**
Winson Chung81f39eb2011-01-11 18:05:01 -0800130 * The intent extra that contains the appWidgetId.
131 * @hide
132 */
133 static final String EXTRA_REMOTEADAPTER_APPWIDGET_ID = "remoteAdapterAppWidgetId";
134
135 /**
Sunny Goyalc12d31c2018-11-12 16:29:18 -0800136 * The intent extra that contains {@code true} if inflating as dak text theme.
137 * @hide
138 */
139 static final String EXTRA_REMOTEADAPTER_ON_LIGHT_BACKGROUND = "remoteAdapterOnLightBackground";
140
141 /**
Sunny Goyal43c97042018-08-23 15:21:26 -0700142 * The intent extra that contains the bounds for all shared elements.
143 */
144 public static final String EXTRA_SHARED_ELEMENT_BOUNDS =
145 "android.widget.extra.SHARED_ELEMENT_BOUNDS";
146
147 /**
Sunny Goyal692f8c92016-11-11 09:19:14 -0800148 * Maximum depth of nested views calls from {@link #addView(int, RemoteViews)} and
149 * {@link #RemoteViews(RemoteViews, RemoteViews)}.
150 */
151 private static final int MAX_NESTED_VIEWS = 10;
152
Anthony Chen8f5f3582017-04-11 11:18:37 -0700153 // The unique identifiers for each custom {@link Action}.
Sunny Goyal43c97042018-08-23 15:21:26 -0700154 private static final int SET_ON_CLICK_RESPONSE_TAG = 1;
Anthony Chen8f5f3582017-04-11 11:18:37 -0700155 private static final int REFLECTION_ACTION_TAG = 2;
Sunny Goyal5b153922017-09-21 21:00:36 -0700156 private static final int SET_DRAWABLE_TINT_TAG = 3;
Anthony Chen8f5f3582017-04-11 11:18:37 -0700157 private static final int VIEW_GROUP_ACTION_ADD_TAG = 4;
Sunny Goyal5b153922017-09-21 21:00:36 -0700158 private static final int VIEW_CONTENT_NAVIGATION_TAG = 5;
Anthony Chen8f5f3582017-04-11 11:18:37 -0700159 private static final int SET_EMPTY_VIEW_ACTION_TAG = 6;
160 private static final int VIEW_GROUP_ACTION_REMOVE_TAG = 7;
161 private static final int SET_PENDING_INTENT_TEMPLATE_TAG = 8;
Anthony Chen8f5f3582017-04-11 11:18:37 -0700162 private static final int SET_REMOTE_VIEW_ADAPTER_INTENT_TAG = 10;
163 private static final int TEXT_VIEW_DRAWABLE_ACTION_TAG = 11;
164 private static final int BITMAP_REFLECTION_ACTION_TAG = 12;
165 private static final int TEXT_VIEW_SIZE_ACTION_TAG = 13;
166 private static final int VIEW_PADDING_ACTION_TAG = 14;
167 private static final int SET_REMOTE_VIEW_ADAPTER_LIST_TAG = 15;
Anthony Chen8f5f3582017-04-11 11:18:37 -0700168 private static final int SET_REMOTE_INPUTS_ACTION_TAG = 18;
169 private static final int LAYOUT_PARAM_ACTION_TAG = 19;
Selim Cinek87c31532017-08-18 18:53:44 -0700170 private static final int OVERRIDE_TEXT_COLORS_TAG = 20;
Gus Prevas9cc96602018-10-11 11:24:23 -0400171 private static final int SET_RIPPLE_DRAWABLE_COLOR_TAG = 21;
Tony Mak7d4b3a52018-11-27 17:29:36 +0000172 private static final int SET_INT_TAG_TAG = 22;
Anthony Chen8f5f3582017-04-11 11:18:37 -0700173
Sunny Goyalc12d31c2018-11-12 16:29:18 -0800174 /** @hide **/
175 @IntDef(flag = true, value = {
176 FLAG_REAPPLY_DISALLOWED,
177 FLAG_WIDGET_IS_COLLECTION_CHILD,
178 FLAG_USE_LIGHT_BACKGROUND_LAYOUT
179 })
180 @Retention(RetentionPolicy.SOURCE)
181 public @interface ApplyFlags {}
182 /**
183 * Whether reapply is disallowed on this remoteview. This maybe be true if some actions modify
184 * the layout in a way that isn't recoverable, since views are being removed.
185 * @hide
186 */
187 public static final int FLAG_REAPPLY_DISALLOWED = 1;
188 /**
189 * This flag indicates whether this RemoteViews object is being created from a
190 * RemoteViewsService for use as a child of a widget collection. This flag is used
191 * to determine whether or not certain features are available, in particular,
192 * setting on click extras and setting on click pending intents. The former is enabled,
193 * and the latter disabled when this flag is true.
194 * @hide
195 */
196 public static final int FLAG_WIDGET_IS_COLLECTION_CHILD = 2;
197 /**
198 * When this flag is set, the views is inflated with {@link #mLightBackgroundLayoutId} instead
199 * of {link #mLayoutId}
200 * @hide
201 */
202 public static final int FLAG_USE_LIGHT_BACKGROUND_LAYOUT = 4;
203
Sunny Goyal692f8c92016-11-11 09:19:14 -0800204 /**
Zhen Zhang6e9e5fa2019-11-12 10:51:00 -0800205 * Used to restrict the views which can be inflated
206 *
207 * @see android.view.LayoutInflater.Filter#onLoadClass(java.lang.Class)
208 */
209 private static final LayoutInflater.Filter INFLATER_FILTER =
210 (clazz) -> clazz.isAnnotationPresent(RemoteViews.RemoteView.class);
211
212 /**
Svetoslav976e8bd2014-07-16 15:12:03 -0700213 * Application that hosts the remote views.
214 *
215 * @hide
Jeff Sharkey6d515712012-09-20 16:06:08 -0700216 */
Mathew Inwood3a75f262019-06-27 12:47:38 +0100217 @UnsupportedAppUsage
Sunny Goyaldd60f4d2017-10-18 15:22:42 -0700218 public ApplicationInfo mApplication;
Jeff Sharkey6d515712012-09-20 16:06:08 -0700219
220 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800221 * The resource ID of the layout file. (Added to the parcel)
222 */
Mathew Inwood3a75f262019-06-27 12:47:38 +0100223 @UnsupportedAppUsage
Gilles Debunne30301932010-06-16 18:32:00 -0700224 private final int mLayoutId;
Romain Guya5475592009-07-01 17:20:08 -0700225
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800226 /**
Sunny Goyalc12d31c2018-11-12 16:29:18 -0800227 * The resource ID of the layout file in dark text mode. (Added to the parcel)
228 */
229 private int mLightBackgroundLayoutId = 0;
230
231 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800232 * An array of actions to perform on the view tree once it has been
233 * inflated
234 */
Mathew Inwood3a75f262019-06-27 12:47:38 +0100235 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800236 private ArrayList<Action> mActions;
Jim Millere667a7a2012-08-09 19:22:32 -0700237
Winson Chung3ec9a452010-09-23 16:40:28 -0700238 /**
Adam Cohen5d200642012-04-24 10:43:31 -0700239 * Maps bitmaps to unique indicies to avoid Bitmap duplication.
240 */
Mathew Inwood3a75f262019-06-27 12:47:38 +0100241 @UnsupportedAppUsage
Adam Cohen5d200642012-04-24 10:43:31 -0700242 private BitmapCache mBitmapCache;
243
244 /**
245 * Indicates whether or not this RemoteViews object is contained as a child of any other
246 * RemoteViews.
247 */
248 private boolean mIsRoot = true;
249
250 /**
251 * Constants to whether or not this RemoteViews is composed of a landscape and portrait
252 * RemoteViews.
253 */
254 private static final int MODE_NORMAL = 0;
255 private static final int MODE_HAS_LANDSCAPE_AND_PORTRAIT = 1;
256
257 /**
258 * Used in conjunction with the special constructor
259 * {@link #RemoteViews(RemoteViews, RemoteViews)} to keep track of the landscape and portrait
260 * RemoteViews.
261 */
262 private RemoteViews mLandscape = null;
Mathew Inwood3a75f262019-06-27 12:47:38 +0100263 @UnsupportedAppUsage
Adam Cohen5d200642012-04-24 10:43:31 -0700264 private RemoteViews mPortrait = null;
265
Sunny Goyalc12d31c2018-11-12 16:29:18 -0800266 @ApplyFlags
267 private int mApplyFlags = 0;
Dianne Hackborn1927ae82012-06-22 15:21:36 -0700268
Adrian Roosfb921842017-10-26 14:49:56 +0200269 /** Class cookies of the Parcel this instance was read from. */
270 private final Map<Class, Object> mClassCookies;
271
Sunny Goyal43c97042018-08-23 15:21:26 -0700272 private static final OnClickHandler DEFAULT_ON_CLICK_HANDLER = (view, pendingIntent, response)
273 -> startPendingIntent(view, pendingIntent, response.getLaunchOptions(view));
Adam Cohenca6fd842010-09-03 18:10:35 -0700274
Sunny Goyal271e3222017-08-29 16:05:47 -0700275 private static final ArrayMap<MethodKey, MethodArgs> sMethods = new ArrayMap<>();
Sunny Goyaldd292f42015-12-02 14:29:27 -0800276
Sunny Goyal271e3222017-08-29 16:05:47 -0700277 /**
278 * This key is used to perform lookups in sMethods without causing allocations.
279 */
280 private static final MethodKey sLookupKey = new MethodKey();
Romain Guye4d4e202013-07-22 13:02:02 -0700281
Adam Cohenca6fd842010-09-03 18:10:35 -0700282 /**
Adrian Roosfe84e1f2015-11-04 15:55:39 -0800283 * @hide
284 */
285 public void setRemoteInputs(int viewId, RemoteInput[] remoteInputs) {
286 mActions.add(new SetRemoteInputsAction(viewId, remoteInputs));
287 }
288
289 /**
Selim Cinekd0426622017-07-11 13:19:59 +0200290 * Reduces all images and ensures that they are all below the given sizes.
291 *
292 * @param maxWidth the maximum width allowed
293 * @param maxHeight the maximum height allowed
294 *
295 * @hide
296 */
297 public void reduceImageSizes(int maxWidth, int maxHeight) {
298 ArrayList<Bitmap> cache = mBitmapCache.mBitmaps;
299 for (int i = 0; i < cache.size(); i++) {
300 Bitmap bitmap = cache.get(i);
301 cache.set(i, Icon.scaleDownIfNecessary(bitmap, maxWidth, maxHeight));
302 }
303 }
304
305 /**
Selim Cinek87c31532017-08-18 18:53:44 -0700306 * Override all text colors in this layout and replace them by the given text color.
307 *
308 * @param textColor The color to use.
309 *
310 * @hide
311 */
312 public void overrideTextColors(int textColor) {
313 addAction(new OverrideTextColorsAction(textColor));
314 }
315
316 /**
Tony Mak7d4b3a52018-11-27 17:29:36 +0000317 * Sets an integer tag to the view.
318 *
319 * @hide
320 */
321 public void setIntTag(int viewId, int key, int tag) {
322 addAction(new SetIntTagAction(viewId, key, tag));
323 }
324
325 /**
Selim Cinekfc8073c2017-08-16 17:50:20 -0700326 * Set that it is disallowed to reapply another remoteview with the same layout as this view.
327 * This should be done if an action is destroying the view tree of the base layout.
328 *
329 * @hide
330 */
Sunny Goyalc12d31c2018-11-12 16:29:18 -0800331 public void addFlags(@ApplyFlags int flags) {
332 mApplyFlags = mApplyFlags | flags;
Selim Cinekfc8073c2017-08-16 17:50:20 -0700333 }
334
335 /**
Selim Cinekfc8073c2017-08-16 17:50:20 -0700336 * @hide
337 */
Sunny Goyalc12d31c2018-11-12 16:29:18 -0800338 public boolean hasFlags(@ApplyFlags int flag) {
339 return (mApplyFlags & flag) == flag;
Selim Cinekfc8073c2017-08-16 17:50:20 -0700340 }
341
342 /**
Sunny Goyal271e3222017-08-29 16:05:47 -0700343 * Stores information related to reflection method lookup.
Romain Guy484f4d62013-07-22 16:39:16 -0700344 */
Sunny Goyal271e3222017-08-29 16:05:47 -0700345 static class MethodKey {
346 public Class targetClass;
347 public Class paramClass;
348 public String methodName;
Romain Guy484f4d62013-07-22 16:39:16 -0700349
350 @Override
351 public boolean equals(Object o) {
Sunny Goyal271e3222017-08-29 16:05:47 -0700352 if (!(o instanceof MethodKey)) {
Romain Guy484f4d62013-07-22 16:39:16 -0700353 return false;
354 }
Sunny Goyal271e3222017-08-29 16:05:47 -0700355 MethodKey p = (MethodKey) o;
Narayan Kamath607223f2018-02-19 14:09:02 +0000356 return Objects.equals(p.targetClass, targetClass)
357 && Objects.equals(p.paramClass, paramClass)
358 && Objects.equals(p.methodName, methodName);
Romain Guy484f4d62013-07-22 16:39:16 -0700359 }
360
361 @Override
362 public int hashCode() {
Sunny Goyal271e3222017-08-29 16:05:47 -0700363 return Objects.hashCode(targetClass) ^ Objects.hashCode(paramClass)
364 ^ Objects.hashCode(methodName);
365 }
366
367 public void set(Class targetClass, Class paramClass, String methodName) {
368 this.targetClass = targetClass;
369 this.paramClass = paramClass;
370 this.methodName = methodName;
Romain Guy484f4d62013-07-22 16:39:16 -0700371 }
372 }
373
Sunny Goyal271e3222017-08-29 16:05:47 -0700374
Romain Guy484f4d62013-07-22 16:39:16 -0700375 /**
Sunny Goyal271e3222017-08-29 16:05:47 -0700376 * Stores information related to reflection method lookup result.
Romain Guy484f4d62013-07-22 16:39:16 -0700377 */
Sunny Goyal271e3222017-08-29 16:05:47 -0700378 static class MethodArgs {
379 public MethodHandle syncMethod;
380 public MethodHandle asyncMethod;
381 public String asyncMethodName;
382 }
383
Romain Guy484f4d62013-07-22 16:39:16 -0700384 /**
Kirill Grouchnikov9091df02016-06-28 11:24:04 -0400385 * This annotation indicates that a subclass of View is allowed to be used
Romain Guya5475592009-07-01 17:20:08 -0700386 * with the {@link RemoteViews} mechanism.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800387 */
388 @Target({ ElementType.TYPE })
389 @Retention(RetentionPolicy.RUNTIME)
390 public @interface RemoteView {
391 }
392
393 /**
394 * Exception to send when something goes wrong executing an action
395 *
396 */
397 public static class ActionException extends RuntimeException {
398 public ActionException(Exception ex) {
399 super(ex);
400 }
401 public ActionException(String message) {
402 super(message);
403 }
Sunny Goyal271e3222017-08-29 16:05:47 -0700404 /**
405 * @hide
406 */
407 public ActionException(Throwable t) {
408 super(t);
409 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800410 }
Adam Cohena32edd42010-10-26 10:35:01 -0700411
Dianne Hackborn1927ae82012-06-22 15:21:36 -0700412 /** @hide */
Sunny Goyal43c97042018-08-23 15:21:26 -0700413 public interface OnClickHandler {
Gus Prevas1ed322b2015-09-17 17:34:46 -0400414
Sunny Goyal02794532018-08-22 15:18:37 -0700415 /** @hide */
Sunny Goyal43c97042018-08-23 15:21:26 -0700416 boolean onClickHandler(View view, PendingIntent pendingIntent, RemoteResponse response);
Dianne Hackborn1927ae82012-06-22 15:21:36 -0700417 }
418
419 /**
420 * Base class for all actions that can be performed on an
421 * inflated view.
422 *
423 * SUBCLASSES MUST BE IMMUTABLE SO CLONE WORKS!!!!!
424 */
425 private abstract static class Action implements Parcelable {
Dianne Hackborna1940212012-06-28 16:07:22 -0700426 public abstract void apply(View root, ViewGroup rootParent,
427 OnClickHandler handler) throws ActionException;
Dianne Hackborn1927ae82012-06-22 15:21:36 -0700428
Adam Cohenfbe44b72012-09-19 20:36:23 -0700429 public static final int MERGE_REPLACE = 0;
430 public static final int MERGE_APPEND = 1;
431 public static final int MERGE_IGNORE = 2;
432
Dianne Hackborn1927ae82012-06-22 15:21:36 -0700433 public int describeContents() {
434 return 0;
435 }
436
Adam Cohen5d200642012-04-24 10:43:31 -0700437 public void setBitmapCache(BitmapCache bitmapCache) {
438 // Do nothing
439 }
Adam Cohenfbe44b72012-09-19 20:36:23 -0700440
Mathew Inwood3a75f262019-06-27 12:47:38 +0100441 @UnsupportedAppUsage
Adam Cohenfbe44b72012-09-19 20:36:23 -0700442 public int mergeBehavior() {
443 return MERGE_REPLACE;
444 }
445
Sunny Goyal5b153922017-09-21 21:00:36 -0700446 public abstract int getActionTag();
Adam Cohenfbe44b72012-09-19 20:36:23 -0700447
448 public String getUniqueKey() {
Sunny Goyal5b153922017-09-21 21:00:36 -0700449 return (getActionTag() + "_" + viewId);
Adam Cohenfbe44b72012-09-19 20:36:23 -0700450 }
451
Sunny Goyaldd292f42015-12-02 14:29:27 -0800452 /**
453 * This is called on the background thread. It should perform any non-ui computations
454 * and return the final action which will run on the UI thread.
455 * Override this if some of the tasks can be performed async.
456 */
457 public Action initActionAsync(ViewTree root, ViewGroup rootParent, OnClickHandler handler) {
458 return this;
459 }
460
Sunny Goyal5c022632016-02-17 16:30:41 -0800461 public boolean prefersAsyncApply() {
462 return false;
463 }
464
Sunny Goyal5d8bcdf2016-10-27 16:11:29 -0700465 /**
466 * Overridden by subclasses which have (or inherit) an ApplicationInfo instance
467 * as member variable
468 */
469 public boolean hasSameAppInfo(ApplicationInfo parentInfo) {
470 return true;
471 }
472
Jeff Sharkey23b31182018-04-18 21:32:12 -0600473 public void visitUris(@NonNull Consumer<Uri> visitor) {
474 // Nothing to visit by default
475 }
476
Mathew Inwood3a75f262019-06-27 12:47:38 +0100477 @UnsupportedAppUsage
Adam Cohenfbe44b72012-09-19 20:36:23 -0700478 int viewId;
479 }
480
Adam Cohenbd1e0072012-09-21 16:51:50 -0700481 /**
Sunny Goyaldd292f42015-12-02 14:29:27 -0800482 * Action class used during async inflation of RemoteViews. Subclasses are not parcelable.
483 */
484 private static abstract class RuntimeAction extends Action {
485 @Override
Sunny Goyal5b153922017-09-21 21:00:36 -0700486 public final int getActionTag() {
487 return 0;
Sunny Goyaldd292f42015-12-02 14:29:27 -0800488 }
489
490 @Override
491 public final void writeToParcel(Parcel dest, int flags) {
492 throw new UnsupportedOperationException();
493 }
494 }
495
496 // Constant used during async execution. It is not parcelable.
497 private static final Action ACTION_NOOP = new RuntimeAction() {
498 @Override
499 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) { }
500 };
501
502 /**
Adam Cohenbd1e0072012-09-21 16:51:50 -0700503 * Merges the passed RemoteViews actions with this RemoteViews actions according to
504 * action-specific merge rules.
Svetoslav976e8bd2014-07-16 15:12:03 -0700505 *
Adam Cohenbd1e0072012-09-21 16:51:50 -0700506 * @param newRv
Svetoslav976e8bd2014-07-16 15:12:03 -0700507 *
Adam Cohenbd1e0072012-09-21 16:51:50 -0700508 * @hide
509 */
Mathew Inwood3a75f262019-06-27 12:47:38 +0100510 @UnsupportedAppUsage
Adam Cohenfbe44b72012-09-19 20:36:23 -0700511 public void mergeRemoteViews(RemoteViews newRv) {
Adam Cohen3ff2d862012-09-26 14:07:57 -0700512 if (newRv == null) return;
Adam Cohenfbe44b72012-09-19 20:36:23 -0700513 // We first copy the new RemoteViews, as the process of merging modifies the way the actions
514 // reference the bitmap cache. We don't want to modify the object as it may need to
515 // be merged and applied multiple times.
Sunny Goyal56333a82017-08-29 13:46:29 -0700516 RemoteViews copy = new RemoteViews(newRv);
Adam Cohenfbe44b72012-09-19 20:36:23 -0700517
518 HashMap<String, Action> map = new HashMap<String, Action>();
519 if (mActions == null) {
520 mActions = new ArrayList<Action>();
521 }
522
523 int count = mActions.size();
524 for (int i = 0; i < count; i++) {
525 Action a = mActions.get(i);
526 map.put(a.getUniqueKey(), a);
527 }
528
529 ArrayList<Action> newActions = copy.mActions;
530 if (newActions == null) return;
531 count = newActions.size();
532 for (int i = 0; i < count; i++) {
533 Action a = newActions.get(i);
534 String key = newActions.get(i).getUniqueKey();
Adam Cohen3ff2d862012-09-26 14:07:57 -0700535 int mergeBehavior = newActions.get(i).mergeBehavior();
Adam Cohenfbe44b72012-09-19 20:36:23 -0700536 if (map.containsKey(key) && mergeBehavior == Action.MERGE_REPLACE) {
537 mActions.remove(map.get(key));
538 map.remove(key);
539 }
540
541 // If the merge behavior is ignore, we don't bother keeping the extra action
542 if (mergeBehavior == Action.MERGE_REPLACE || mergeBehavior == Action.MERGE_APPEND) {
543 mActions.add(a);
544 }
545 }
546
547 // Because pruning can remove the need for bitmaps, we reconstruct the bitmap cache
548 mBitmapCache = new BitmapCache();
549 setBitmapCache(mBitmapCache);
Romain Guya5475592009-07-01 17:20:08 -0700550 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800551
Jeff Sharkey23b31182018-04-18 21:32:12 -0600552 /**
553 * Note all {@link Uri} that are referenced internally, with the expectation
554 * that Uri permission grants will need to be issued to ensure the recipient
555 * of this object is able to render its contents.
556 *
557 * @hide
558 */
559 public void visitUris(@NonNull Consumer<Uri> visitor) {
560 if (mActions != null) {
561 for (int i = 0; i < mActions.size(); i++) {
562 mActions.get(i).visitUris(visitor);
563 }
564 }
565 }
566
567 private static void visitIconUri(Icon icon, @NonNull Consumer<Uri> visitor) {
568 if (icon != null && icon.getType() == Icon.TYPE_URI) {
569 visitor.accept(icon.getUri());
570 }
571 }
572
Adrian Roos83fad0a2017-04-24 16:58:26 -0700573 private static class RemoteViewsContextWrapper extends ContextWrapper {
574 private final Context mContextForResources;
575
576 RemoteViewsContextWrapper(Context context, Context contextForResources) {
577 super(context);
578 mContextForResources = contextForResources;
579 }
580
581 @Override
582 public Resources getResources() {
583 return mContextForResources.getResources();
584 }
585
586 @Override
587 public Resources.Theme getTheme() {
588 return mContextForResources.getTheme();
589 }
590
591 @Override
592 public String getPackageName() {
593 return mContextForResources.getPackageName();
594 }
595 }
596
Adam Cohen1480fdd2010-08-25 17:24:53 -0700597 private class SetEmptyView extends Action {
Adam Cohen1480fdd2010-08-25 17:24:53 -0700598 int emptyViewId;
599
Adam Cohen1480fdd2010-08-25 17:24:53 -0700600 SetEmptyView(int viewId, int emptyViewId) {
601 this.viewId = viewId;
602 this.emptyViewId = emptyViewId;
603 }
604
605 SetEmptyView(Parcel in) {
606 this.viewId = in.readInt();
607 this.emptyViewId = in.readInt();
608 }
609
610 public void writeToParcel(Parcel out, int flags) {
Adam Cohen1480fdd2010-08-25 17:24:53 -0700611 out.writeInt(this.viewId);
612 out.writeInt(this.emptyViewId);
613 }
614
615 @Override
Dianne Hackborna1940212012-06-28 16:07:22 -0700616 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
Adam Cohen1480fdd2010-08-25 17:24:53 -0700617 final View view = root.findViewById(viewId);
618 if (!(view instanceof AdapterView<?>)) return;
619
620 AdapterView<?> adapterView = (AdapterView<?>) view;
621
622 final View emptyView = root.findViewById(emptyViewId);
623 if (emptyView == null) return;
624
625 adapterView.setEmptyView(emptyView);
626 }
Adam Cohenfbe44b72012-09-19 20:36:23 -0700627
Sunny Goyal5b153922017-09-21 21:00:36 -0700628 @Override
629 public int getActionTag() {
630 return SET_EMPTY_VIEW_ACTION_TAG;
Adam Cohenfbe44b72012-09-19 20:36:23 -0700631 }
Adam Cohen1480fdd2010-08-25 17:24:53 -0700632 }
633
Adam Cohenca6fd842010-09-03 18:10:35 -0700634 private class SetPendingIntentTemplate extends Action {
635 public SetPendingIntentTemplate(int id, PendingIntent pendingIntentTemplate) {
636 this.viewId = id;
637 this.pendingIntentTemplate = pendingIntentTemplate;
638 }
639
640 public SetPendingIntentTemplate(Parcel parcel) {
641 viewId = parcel.readInt();
642 pendingIntentTemplate = PendingIntent.readPendingIntentOrNullFromParcel(parcel);
643 }
644
645 public void writeToParcel(Parcel dest, int flags) {
Adam Cohenca6fd842010-09-03 18:10:35 -0700646 dest.writeInt(viewId);
Sunny Goyal5b153922017-09-21 21:00:36 -0700647 PendingIntent.writePendingIntentOrNullToParcel(pendingIntentTemplate, dest);
Adam Cohenca6fd842010-09-03 18:10:35 -0700648 }
649
650 @Override
Dianne Hackborna1940212012-06-28 16:07:22 -0700651 public void apply(View root, ViewGroup rootParent, final OnClickHandler handler) {
Adam Cohenca6fd842010-09-03 18:10:35 -0700652 final View target = root.findViewById(viewId);
Joe Onorato2b69ce42010-10-31 11:35:41 -0700653 if (target == null) return;
Adam Cohenca6fd842010-09-03 18:10:35 -0700654
655 // If the view isn't an AdapterView, setting a PendingIntent template doesn't make sense
656 if (target instanceof AdapterView<?>) {
Adam Cohena32edd42010-10-26 10:35:01 -0700657 AdapterView<?> av = (AdapterView<?>) target;
Adam Cohenca6fd842010-09-03 18:10:35 -0700658 // The PendingIntent template is stored in the view's tag.
Adam Cohena32edd42010-10-26 10:35:01 -0700659 OnItemClickListener listener = new OnItemClickListener() {
660 public void onItemClick(AdapterView<?> parent, View view,
661 int position, long id) {
662 // The view should be a frame layout
663 if (view instanceof ViewGroup) {
664 ViewGroup vg = (ViewGroup) view;
665
666 // AdapterViews contain their children in a frame
667 // so we need to go one layer deeper here.
668 if (parent instanceof AdapterViewAnimator) {
669 vg = (ViewGroup) vg.getChildAt(0);
670 }
671 if (vg == null) return;
672
Sunny Goyal43c97042018-08-23 15:21:26 -0700673 RemoteResponse response = null;
Adam Cohena32edd42010-10-26 10:35:01 -0700674 int childCount = vg.getChildCount();
675 for (int i = 0; i < childCount; i++) {
676 Object tag = vg.getChildAt(i).getTag(com.android.internal.R.id.fillInIntent);
Sunny Goyal43c97042018-08-23 15:21:26 -0700677 if (tag instanceof RemoteResponse) {
678 response = (RemoteResponse) tag;
Adam Cohena32edd42010-10-26 10:35:01 -0700679 break;
680 }
681 }
Sunny Goyal43c97042018-08-23 15:21:26 -0700682 if (response == null) return;
683 response.handleViewClick(view, handler);
Adam Cohena32edd42010-10-26 10:35:01 -0700684 }
685 }
686 };
687 av.setOnItemClickListener(listener);
688 av.setTag(pendingIntentTemplate);
Adam Cohenca6fd842010-09-03 18:10:35 -0700689 } else {
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800690 Log.e(LOG_TAG, "Cannot setPendingIntentTemplate on a view which is not" +
Adam Cohen35ae9ca2010-09-20 15:20:41 -0700691 "an AdapterView (id: " + viewId + ")");
Adam Cohenca6fd842010-09-03 18:10:35 -0700692 return;
693 }
694 }
695
Sunny Goyal5b153922017-09-21 21:00:36 -0700696 @Override
697 public int getActionTag() {
698 return SET_PENDING_INTENT_TEMPLATE_TAG;
Adam Cohenfbe44b72012-09-19 20:36:23 -0700699 }
700
Mathew Inwood3a75f262019-06-27 12:47:38 +0100701 @UnsupportedAppUsage
Adam Cohenca6fd842010-09-03 18:10:35 -0700702 PendingIntent pendingIntentTemplate;
Adam Cohenca6fd842010-09-03 18:10:35 -0700703 }
704
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800705 private class SetRemoteViewsAdapterList extends Action {
Adam Cohenb00d9f02013-01-10 14:12:52 -0800706 public SetRemoteViewsAdapterList(int id, ArrayList<RemoteViews> list, int viewTypeCount) {
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800707 this.viewId = id;
708 this.list = list;
Adam Cohenb00d9f02013-01-10 14:12:52 -0800709 this.viewTypeCount = viewTypeCount;
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800710 }
711
712 public SetRemoteViewsAdapterList(Parcel parcel) {
713 viewId = parcel.readInt();
Adam Cohenb00d9f02013-01-10 14:12:52 -0800714 viewTypeCount = parcel.readInt();
Sunny Goyal5b153922017-09-21 21:00:36 -0700715 list = parcel.createTypedArrayList(RemoteViews.CREATOR);
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800716 }
717
718 public void writeToParcel(Parcel dest, int flags) {
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800719 dest.writeInt(viewId);
Adam Cohenb00d9f02013-01-10 14:12:52 -0800720 dest.writeInt(viewTypeCount);
Sunny Goyal5b153922017-09-21 21:00:36 -0700721 dest.writeTypedList(list, flags);
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800722 }
723
724 @Override
725 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
726 final View target = root.findViewById(viewId);
727 if (target == null) return;
728
729 // Ensure that we are applying to an AppWidget root
730 if (!(rootParent instanceof AppWidgetHostView)) {
731 Log.e(LOG_TAG, "SetRemoteViewsAdapterIntent action can only be used for " +
732 "AppWidgets (root id: " + viewId + ")");
733 return;
734 }
735 // Ensure that we are calling setRemoteAdapter on an AdapterView that supports it
736 if (!(target instanceof AbsListView) && !(target instanceof AdapterViewAnimator)) {
737 Log.e(LOG_TAG, "Cannot setRemoteViewsAdapter on a view which is not " +
738 "an AbsListView or AdapterViewAnimator (id: " + viewId + ")");
739 return;
740 }
741
742 if (target instanceof AbsListView) {
743 AbsListView v = (AbsListView) target;
744 Adapter a = v.getAdapter();
Adam Cohenb00d9f02013-01-10 14:12:52 -0800745 if (a instanceof RemoteViewsListAdapter && viewTypeCount <= a.getViewTypeCount()) {
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800746 ((RemoteViewsListAdapter) a).setViewsList(list);
747 } else {
Adam Cohenb00d9f02013-01-10 14:12:52 -0800748 v.setAdapter(new RemoteViewsListAdapter(v.getContext(), list, viewTypeCount));
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800749 }
750 } else if (target instanceof AdapterViewAnimator) {
751 AdapterViewAnimator v = (AdapterViewAnimator) target;
752 Adapter a = v.getAdapter();
Adam Cohenb00d9f02013-01-10 14:12:52 -0800753 if (a instanceof RemoteViewsListAdapter && viewTypeCount <= a.getViewTypeCount()) {
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800754 ((RemoteViewsListAdapter) a).setViewsList(list);
755 } else {
Adam Cohenb00d9f02013-01-10 14:12:52 -0800756 v.setAdapter(new RemoteViewsListAdapter(v.getContext(), list, viewTypeCount));
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800757 }
758 }
759 }
760
Sunny Goyal5b153922017-09-21 21:00:36 -0700761 @Override
762 public int getActionTag() {
763 return SET_REMOTE_VIEW_ADAPTER_LIST_TAG;
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800764 }
765
Adam Cohenb00d9f02013-01-10 14:12:52 -0800766 int viewTypeCount;
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800767 ArrayList<RemoteViews> list;
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800768 }
769
Winson Chung037300b2011-03-29 15:40:16 -0700770 private class SetRemoteViewsAdapterIntent extends Action {
771 public SetRemoteViewsAdapterIntent(int id, Intent intent) {
772 this.viewId = id;
773 this.intent = intent;
774 }
775
776 public SetRemoteViewsAdapterIntent(Parcel parcel) {
777 viewId = parcel.readInt();
Sunny Goyal5b153922017-09-21 21:00:36 -0700778 intent = parcel.readTypedObject(Intent.CREATOR);
Winson Chung037300b2011-03-29 15:40:16 -0700779 }
780
781 public void writeToParcel(Parcel dest, int flags) {
Winson Chung037300b2011-03-29 15:40:16 -0700782 dest.writeInt(viewId);
Sunny Goyal5b153922017-09-21 21:00:36 -0700783 dest.writeTypedObject(intent, flags);
Winson Chung037300b2011-03-29 15:40:16 -0700784 }
785
786 @Override
Dianne Hackborna1940212012-06-28 16:07:22 -0700787 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
Winson Chung037300b2011-03-29 15:40:16 -0700788 final View target = root.findViewById(viewId);
789 if (target == null) return;
790
791 // Ensure that we are applying to an AppWidget root
792 if (!(rootParent instanceof AppWidgetHostView)) {
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800793 Log.e(LOG_TAG, "SetRemoteViewsAdapterIntent action can only be used for " +
Winson Chung037300b2011-03-29 15:40:16 -0700794 "AppWidgets (root id: " + viewId + ")");
795 return;
796 }
797 // Ensure that we are calling setRemoteAdapter on an AdapterView that supports it
798 if (!(target instanceof AbsListView) && !(target instanceof AdapterViewAnimator)) {
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800799 Log.e(LOG_TAG, "Cannot setRemoteViewsAdapter on a view which is not " +
Winson Chung037300b2011-03-29 15:40:16 -0700800 "an AbsListView or AdapterViewAnimator (id: " + viewId + ")");
801 return;
802 }
803
804 // Embed the AppWidget Id for use in RemoteViewsAdapter when connecting to the intent
805 // RemoteViewsService
806 AppWidgetHostView host = (AppWidgetHostView) rootParent;
Sunny Goyalc12d31c2018-11-12 16:29:18 -0800807 intent.putExtra(EXTRA_REMOTEADAPTER_APPWIDGET_ID, host.getAppWidgetId())
808 .putExtra(EXTRA_REMOTEADAPTER_ON_LIGHT_BACKGROUND,
809 hasFlags(FLAG_USE_LIGHT_BACKGROUND_LAYOUT));
810
Winson Chung037300b2011-03-29 15:40:16 -0700811 if (target instanceof AbsListView) {
812 AbsListView v = (AbsListView) target;
Sunny Goyal5c022632016-02-17 16:30:41 -0800813 v.setRemoteViewsAdapter(intent, isAsync);
Adam Cohena6a4cbc2012-09-26 17:36:40 -0700814 v.setRemoteViewsOnClickHandler(handler);
Winson Chung037300b2011-03-29 15:40:16 -0700815 } else if (target instanceof AdapterViewAnimator) {
816 AdapterViewAnimator v = (AdapterViewAnimator) target;
Sunny Goyal5c022632016-02-17 16:30:41 -0800817 v.setRemoteViewsAdapter(intent, isAsync);
Adam Cohena6a4cbc2012-09-26 17:36:40 -0700818 v.setRemoteViewsOnClickHandler(handler);
Winson Chung037300b2011-03-29 15:40:16 -0700819 }
820 }
821
Sunny Goyal5c022632016-02-17 16:30:41 -0800822 @Override
823 public Action initActionAsync(ViewTree root, ViewGroup rootParent,
824 OnClickHandler handler) {
825 SetRemoteViewsAdapterIntent copy = new SetRemoteViewsAdapterIntent(viewId, intent);
826 copy.isAsync = true;
827 return copy;
828 }
829
Sunny Goyal5b153922017-09-21 21:00:36 -0700830 @Override
831 public int getActionTag() {
832 return SET_REMOTE_VIEW_ADAPTER_INTENT_TAG;
Adam Cohenfbe44b72012-09-19 20:36:23 -0700833 }
834
Winson Chung037300b2011-03-29 15:40:16 -0700835 Intent intent;
Sunny Goyal5c022632016-02-17 16:30:41 -0800836 boolean isAsync = false;
Winson Chung037300b2011-03-29 15:40:16 -0700837 }
838
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800839 /**
840 * Equivalent to calling
841 * {@link android.view.View#setOnClickListener(android.view.View.OnClickListener)}
842 * to launch the provided {@link PendingIntent}.
843 */
Sunny Goyal43c97042018-08-23 15:21:26 -0700844 private class SetOnClickResponse extends Action {
845
846 SetOnClickResponse(int id, RemoteResponse response) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800847 this.viewId = id;
Sunny Goyal43c97042018-08-23 15:21:26 -0700848 this.mResponse = response;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800849 }
Adam Cohenca6fd842010-09-03 18:10:35 -0700850
Sunny Goyal43c97042018-08-23 15:21:26 -0700851 SetOnClickResponse(Parcel parcel) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800852 viewId = parcel.readInt();
Sunny Goyal43c97042018-08-23 15:21:26 -0700853 mResponse = new RemoteResponse();
854 mResponse.readFromParcel(parcel);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800855 }
Adam Cohenca6fd842010-09-03 18:10:35 -0700856
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800857 public void writeToParcel(Parcel dest, int flags) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800858 dest.writeInt(viewId);
Sunny Goyal43c97042018-08-23 15:21:26 -0700859 mResponse.writeToParcel(dest, flags);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800860 }
Adam Cohenca6fd842010-09-03 18:10:35 -0700861
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800862 @Override
Dianne Hackborna1940212012-06-28 16:07:22 -0700863 public void apply(View root, ViewGroup rootParent, final OnClickHandler handler) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800864 final View target = root.findViewById(viewId);
Joe Onorato2b69ce42010-10-31 11:35:41 -0700865 if (target == null) return;
Adam Cohenca6fd842010-09-03 18:10:35 -0700866
Sunny Goyal43c97042018-08-23 15:21:26 -0700867 if (mResponse.mPendingIntent != null) {
868 // If the view is an AdapterView, setting a PendingIntent on click doesn't make
869 // much sense, do they mean to set a PendingIntent template for the
870 // AdapterView's children?
Sunny Goyalc12d31c2018-11-12 16:29:18 -0800871 if (hasFlags(FLAG_WIDGET_IS_COLLECTION_CHILD)) {
Sunny Goyal43c97042018-08-23 15:21:26 -0700872 Log.w(LOG_TAG, "Cannot SetOnClickResponse for collection item "
873 + "(id: " + viewId + ")");
874 ApplicationInfo appInfo = root.getContext().getApplicationInfo();
Adam Cohenffc46a52012-04-30 19:54:39 -0700875
Sunny Goyal43c97042018-08-23 15:21:26 -0700876 // We let this slide for HC and ICS so as to not break compatibility. It should
877 // have been disabled from the outset, but was left open by accident.
878 if (appInfo != null
879 && appInfo.targetSdkVersion >= Build.VERSION_CODES.JELLY_BEAN) {
880 return;
881 }
882 }
883 target.setTagInternal(R.id.pending_intent_tag, mResponse.mPendingIntent);
884 } else if (mResponse.mFillIntent != null) {
Sunny Goyalc12d31c2018-11-12 16:29:18 -0800885 if (!hasFlags(FLAG_WIDGET_IS_COLLECTION_CHILD)) {
Sunny Goyal43c97042018-08-23 15:21:26 -0700886 Log.e(LOG_TAG, "The method setOnClickFillInIntent is available "
887 + "only from RemoteViewsFactory (ie. on collection items).");
Adam Cohenffc46a52012-04-30 19:54:39 -0700888 return;
889 }
Sunny Goyal43c97042018-08-23 15:21:26 -0700890 if (target == root) {
891 // Target is a root node of an AdapterView child. Set the response in the tag.
892 // Actual click handling is done by OnItemClickListener in
893 // SetPendingIntentTemplate, which uses this tag information.
894 target.setTagInternal(com.android.internal.R.id.fillInIntent, mResponse);
895 return;
896 }
897 } else {
898 // No intent to apply
899 target.setOnClickListener(null);
900 return;
Adam Cohenca6fd842010-09-03 18:10:35 -0700901 }
Sunny Goyal43c97042018-08-23 15:21:26 -0700902 target.setOnClickListener(v -> mResponse.handleViewClick(v, handler));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800903 }
Adam Cohenc6151f22012-02-02 21:02:31 -0800904
Sunny Goyal5b153922017-09-21 21:00:36 -0700905 @Override
906 public int getActionTag() {
Sunny Goyal43c97042018-08-23 15:21:26 -0700907 return SET_ON_CLICK_RESPONSE_TAG;
Adam Cohenfbe44b72012-09-19 20:36:23 -0700908 }
909
Sunny Goyal43c97042018-08-23 15:21:26 -0700910 final RemoteResponse mResponse;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800911 }
912
Sunny Goyal43c97042018-08-23 15:21:26 -0700913 /** @hide **/
914 public static Rect getSourceBounds(View v) {
Romain Guye4d4e202013-07-22 13:02:02 -0700915 final float appScale = v.getContext().getResources()
916 .getCompatibilityInfo().applicationScale;
917 final int[] pos = new int[2];
918 v.getLocationOnScreen(pos);
919
920 final Rect rect = new Rect();
921 rect.left = (int) (pos[0] * appScale + 0.5f);
922 rect.top = (int) (pos[1] * appScale + 0.5f);
923 rect.right = (int) ((pos[0] + v.getWidth()) * appScale + 0.5f);
924 rect.bottom = (int) ((pos[1] + v.getHeight()) * appScale + 0.5f);
925 return rect;
926 }
927
Sunny Goyal271e3222017-08-29 16:05:47 -0700928 private MethodHandle getMethod(View view, String methodName, Class<?> paramType,
929 boolean async) {
930 MethodArgs result;
Romain Guye4d4e202013-07-22 13:02:02 -0700931 Class<? extends View> klass = view.getClass();
932
Sunny Goyal271e3222017-08-29 16:05:47 -0700933 synchronized (sMethods) {
934 // The key is defined by the view class, param class and method name.
935 sLookupKey.set(klass, paramType, methodName);
936 result = sMethods.get(sLookupKey);
Romain Guye4d4e202013-07-22 13:02:02 -0700937
Sunny Goyal271e3222017-08-29 16:05:47 -0700938 if (result == null) {
939 Method method;
Romain Guye4d4e202013-07-22 13:02:02 -0700940 try {
Romain Guy9870e5c2013-07-23 13:09:51 -0700941 if (paramType == null) {
942 method = klass.getMethod(methodName);
943 } else {
944 method = klass.getMethod(methodName, paramType);
945 }
Sunny Goyal271e3222017-08-29 16:05:47 -0700946 if (!method.isAnnotationPresent(RemotableViewMethod.class)) {
947 throw new ActionException("view: " + klass.getName()
948 + " can't use method with RemoteViews: "
949 + methodName + getParameters(paramType));
950 }
951
952 result = new MethodArgs();
953 result.syncMethod = MethodHandles.publicLookup().unreflect(method);
954 result.asyncMethodName =
955 method.getAnnotation(RemotableViewMethod.class).asyncImpl();
956 } catch (NoSuchMethodException | IllegalAccessException ex) {
Romain Guye4d4e202013-07-22 13:02:02 -0700957 throw new ActionException("view: " + klass.getName() + " doesn't have method: "
958 + methodName + getParameters(paramType));
959 }
960
Sunny Goyal271e3222017-08-29 16:05:47 -0700961 MethodKey key = new MethodKey();
962 key.set(klass, paramType, methodName);
963 sMethods.put(key, result);
Sunny Goyaldd292f42015-12-02 14:29:27 -0800964 }
965
Sunny Goyal271e3222017-08-29 16:05:47 -0700966 if (!async) {
967 return result.syncMethod;
968 }
969 // Check this so see if async method is implemented or not.
970 if (result.asyncMethodName.isEmpty()) {
971 return null;
972 }
973 // Async method is lazily loaded. If it is not yet loaded, load now.
974 if (result.asyncMethod == null) {
975 MethodType asyncType = result.syncMethod.type()
976 .dropParameterTypes(0, 1).changeReturnType(Runnable.class);
Sunny Goyaldd292f42015-12-02 14:29:27 -0800977 try {
Sunny Goyal271e3222017-08-29 16:05:47 -0700978 result.asyncMethod = MethodHandles.publicLookup().findVirtual(
979 klass, result.asyncMethodName, asyncType);
980 } catch (NoSuchMethodException | IllegalAccessException ex) {
981 throw new ActionException("Async implementation declared as "
982 + result.asyncMethodName + " but not defined for " + methodName
983 + ": public Runnable " + result.asyncMethodName + " ("
984 + TextUtils.join(",", asyncType.parameterArray()) + ")");
Sunny Goyaldd292f42015-12-02 14:29:27 -0800985 }
986 }
Sunny Goyal271e3222017-08-29 16:05:47 -0700987 return result.asyncMethod;
Sunny Goyaldd292f42015-12-02 14:29:27 -0800988 }
989 }
990
Romain Guye4d4e202013-07-22 13:02:02 -0700991 private static String getParameters(Class<?> paramType) {
992 if (paramType == null) return "()";
993 return "(" + paramType + ")";
994 }
995
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800996 /**
Sunny Goyal5b153922017-09-21 21:00:36 -0700997 * Equivalent to calling
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800998 * {@link Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)},
Sunny Goyal5b153922017-09-21 21:00:36 -0700999 * on the {@link Drawable} of a given view.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001000 * <p>
Sunny Goyal5b153922017-09-21 21:00:36 -07001001 * The operation will be performed on the {@link Drawable} returned by the
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001002 * target {@link View#getBackground()} by default. If targetBackground is false,
1003 * we assume the target is an {@link ImageView} and try applying the operations
1004 * to {@link ImageView#getDrawable()}.
1005 * <p>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001006 */
Sunny Goyal5b153922017-09-21 21:00:36 -07001007 private class SetDrawableTint extends Action {
1008 SetDrawableTint(int id, boolean targetBackground,
1009 int colorFilter, @NonNull PorterDuff.Mode mode) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001010 this.viewId = id;
1011 this.targetBackground = targetBackground;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001012 this.colorFilter = colorFilter;
1013 this.filterMode = mode;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001014 }
Jim Millere667a7a2012-08-09 19:22:32 -07001015
Sunny Goyal5b153922017-09-21 21:00:36 -07001016 SetDrawableTint(Parcel parcel) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001017 viewId = parcel.readInt();
1018 targetBackground = parcel.readInt() != 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001019 colorFilter = parcel.readInt();
Sunny Goyal5b153922017-09-21 21:00:36 -07001020 filterMode = PorterDuff.intToMode(parcel.readInt());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001021 }
Jim Millere667a7a2012-08-09 19:22:32 -07001022
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001023 public void writeToParcel(Parcel dest, int flags) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001024 dest.writeInt(viewId);
1025 dest.writeInt(targetBackground ? 1 : 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001026 dest.writeInt(colorFilter);
Sunny Goyal5b153922017-09-21 21:00:36 -07001027 dest.writeInt(PorterDuff.modeToInt(filterMode));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001028 }
Jim Millere667a7a2012-08-09 19:22:32 -07001029
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001030 @Override
Dianne Hackborna1940212012-06-28 16:07:22 -07001031 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001032 final View target = root.findViewById(viewId);
Joe Onorato2b69ce42010-10-31 11:35:41 -07001033 if (target == null) return;
Jim Millere667a7a2012-08-09 19:22:32 -07001034
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001035 // Pick the correct drawable to modify for this view
1036 Drawable targetDrawable = null;
1037 if (targetBackground) {
1038 targetDrawable = target.getBackground();
1039 } else if (target instanceof ImageView) {
1040 ImageView imageView = (ImageView) target;
1041 targetDrawable = imageView.getDrawable();
1042 }
Jim Millere667a7a2012-08-09 19:22:32 -07001043
Romain Guya5475592009-07-01 17:20:08 -07001044 if (targetDrawable != null) {
Sunny Goyal5b153922017-09-21 21:00:36 -07001045 targetDrawable.mutate().setColorFilter(colorFilter, filterMode);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001046 }
1047 }
Winson Chung3ec9a452010-09-23 16:40:28 -07001048
Sunny Goyal5b153922017-09-21 21:00:36 -07001049 @Override
1050 public int getActionTag() {
1051 return SET_DRAWABLE_TINT_TAG;
Adam Cohenfbe44b72012-09-19 20:36:23 -07001052 }
1053
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001054 boolean targetBackground;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001055 int colorFilter;
1056 PorterDuff.Mode filterMode;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001057 }
Jim Millere667a7a2012-08-09 19:22:32 -07001058
Gus Prevas9cc96602018-10-11 11:24:23 -04001059 /**
1060 * Equivalent to calling
1061 * {@link RippleDrawable#setColor(ColorStateList)},
1062 * on the {@link Drawable} of a given view.
1063 * <p>
1064 * The operation will be performed on the {@link Drawable} returned by the
1065 * target {@link View#getBackground()}.
1066 * <p>
1067 */
1068 private class SetRippleDrawableColor extends Action {
1069
1070 ColorStateList mColorStateList;
1071
1072 SetRippleDrawableColor(int id, ColorStateList colorStateList) {
1073 this.viewId = id;
1074 this.mColorStateList = colorStateList;
1075 }
1076
1077 SetRippleDrawableColor(Parcel parcel) {
1078 viewId = parcel.readInt();
1079 mColorStateList = parcel.readParcelable(null);
1080 }
1081
1082 public void writeToParcel(Parcel dest, int flags) {
1083 dest.writeInt(viewId);
1084 dest.writeParcelable(mColorStateList, 0);
1085 }
1086
1087 @Override
1088 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
1089 final View target = root.findViewById(viewId);
1090 if (target == null) return;
1091
1092 // Pick the correct drawable to modify for this view
1093 Drawable targetDrawable = target.getBackground();
1094
1095 if (targetDrawable instanceof RippleDrawable) {
1096 ((RippleDrawable) targetDrawable.mutate()).setColor(mColorStateList);
1097 }
1098 }
1099
1100 @Override
1101 public int getActionTag() {
1102 return SET_RIPPLE_DRAWABLE_COLOR_TAG;
1103 }
1104 }
1105
Sunny Goyal5b153922017-09-21 21:00:36 -07001106 private final class ViewContentNavigation extends Action {
1107 final boolean mNext;
Adam Cohen2dd21972010-08-15 18:20:04 -07001108
Sunny Goyal5b153922017-09-21 21:00:36 -07001109 ViewContentNavigation(int viewId, boolean next) {
Adam Cohen2dd21972010-08-15 18:20:04 -07001110 this.viewId = viewId;
Sunny Goyal5b153922017-09-21 21:00:36 -07001111 this.mNext = next;
Adam Cohen2dd21972010-08-15 18:20:04 -07001112 }
1113
Sunny Goyal5b153922017-09-21 21:00:36 -07001114 ViewContentNavigation(Parcel in) {
Adam Cohen2dd21972010-08-15 18:20:04 -07001115 this.viewId = in.readInt();
Sunny Goyal5b153922017-09-21 21:00:36 -07001116 this.mNext = in.readBoolean();
Adam Cohen2dd21972010-08-15 18:20:04 -07001117 }
1118
1119 public void writeToParcel(Parcel out, int flags) {
Adam Cohen2dd21972010-08-15 18:20:04 -07001120 out.writeInt(this.viewId);
Sunny Goyal5b153922017-09-21 21:00:36 -07001121 out.writeBoolean(this.mNext);
Adam Cohen2dd21972010-08-15 18:20:04 -07001122 }
1123
1124 @Override
Dianne Hackborna1940212012-06-28 16:07:22 -07001125 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
Adam Cohen2dd21972010-08-15 18:20:04 -07001126 final View view = root.findViewById(viewId);
Joe Onorato2b69ce42010-10-31 11:35:41 -07001127 if (view == null) return;
Adam Cohen2dd21972010-08-15 18:20:04 -07001128
Adam Cohen2dd21972010-08-15 18:20:04 -07001129 try {
Sunny Goyal5b153922017-09-21 21:00:36 -07001130 getMethod(view,
1131 mNext ? "showNext" : "showPrevious", null, false /* async */).invoke(view);
Sunny Goyal271e3222017-08-29 16:05:47 -07001132 } catch (Throwable ex) {
Adam Cohen2dd21972010-08-15 18:20:04 -07001133 throw new ActionException(ex);
1134 }
1135 }
Adam Cohenfbe44b72012-09-19 20:36:23 -07001136
1137 public int mergeBehavior() {
Sunny Goyal5b153922017-09-21 21:00:36 -07001138 return MERGE_IGNORE;
Adam Cohenfbe44b72012-09-19 20:36:23 -07001139 }
1140
Sunny Goyal5b153922017-09-21 21:00:36 -07001141 @Override
1142 public int getActionTag() {
1143 return VIEW_CONTENT_NAVIGATION_TAG;
Adam Cohenfbe44b72012-09-19 20:36:23 -07001144 }
Adam Cohen2dd21972010-08-15 18:20:04 -07001145 }
1146
Adam Cohen5d200642012-04-24 10:43:31 -07001147 private static class BitmapCache {
Sunny Goyal56333a82017-08-29 13:46:29 -07001148
Mathew Inwood3a75f262019-06-27 12:47:38 +01001149 @UnsupportedAppUsage
Adam Cohen5d200642012-04-24 10:43:31 -07001150 ArrayList<Bitmap> mBitmaps;
Sunny Goyal56333a82017-08-29 13:46:29 -07001151 int mBitmapMemory = -1;
Adam Cohen5d200642012-04-24 10:43:31 -07001152
1153 public BitmapCache() {
Sunny Goyal56333a82017-08-29 13:46:29 -07001154 mBitmaps = new ArrayList<>();
Adam Cohen5d200642012-04-24 10:43:31 -07001155 }
1156
1157 public BitmapCache(Parcel source) {
Sunny Goyal5b153922017-09-21 21:00:36 -07001158 mBitmaps = source.createTypedArrayList(Bitmap.CREATOR);
Adam Cohen5d200642012-04-24 10:43:31 -07001159 }
1160
1161 public int getBitmapId(Bitmap b) {
1162 if (b == null) {
1163 return -1;
1164 } else {
1165 if (mBitmaps.contains(b)) {
1166 return mBitmaps.indexOf(b);
1167 } else {
1168 mBitmaps.add(b);
Sunny Goyal56333a82017-08-29 13:46:29 -07001169 mBitmapMemory = -1;
Adam Cohen5d200642012-04-24 10:43:31 -07001170 return (mBitmaps.size() - 1);
1171 }
1172 }
1173 }
1174
1175 public Bitmap getBitmapForId(int id) {
1176 if (id == -1 || id >= mBitmaps.size()) {
1177 return null;
1178 } else {
1179 return mBitmaps.get(id);
1180 }
1181 }
1182
1183 public void writeBitmapsToParcel(Parcel dest, int flags) {
Sunny Goyal5b153922017-09-21 21:00:36 -07001184 dest.writeTypedList(mBitmaps, flags);
Adam Cohen5d200642012-04-24 10:43:31 -07001185 }
1186
Sunny Goyal56333a82017-08-29 13:46:29 -07001187 public int getBitmapMemory() {
1188 if (mBitmapMemory < 0) {
1189 mBitmapMemory = 0;
1190 int count = mBitmaps.size();
1191 for (int i = 0; i < count; i++) {
1192 mBitmapMemory += mBitmaps.get(i).getAllocationByteCount();
Adam Cohen5d200642012-04-24 10:43:31 -07001193 }
1194 }
Sunny Goyal56333a82017-08-29 13:46:29 -07001195 return mBitmapMemory;
Adrian Roos7da889d2016-03-16 18:38:58 -07001196 }
Adam Cohen5d200642012-04-24 10:43:31 -07001197 }
1198
1199 private class BitmapReflectionAction extends Action {
1200 int bitmapId;
Mathew Inwood3a75f262019-06-27 12:47:38 +01001201 @UnsupportedAppUsage
Adam Cohen5d200642012-04-24 10:43:31 -07001202 Bitmap bitmap;
Mathew Inwood3a75f262019-06-27 12:47:38 +01001203 @UnsupportedAppUsage
Adam Cohen5d200642012-04-24 10:43:31 -07001204 String methodName;
1205
1206 BitmapReflectionAction(int viewId, String methodName, Bitmap bitmap) {
1207 this.bitmap = bitmap;
1208 this.viewId = viewId;
1209 this.methodName = methodName;
1210 bitmapId = mBitmapCache.getBitmapId(bitmap);
1211 }
1212
1213 BitmapReflectionAction(Parcel in) {
1214 viewId = in.readInt();
1215 methodName = in.readString();
1216 bitmapId = in.readInt();
1217 bitmap = mBitmapCache.getBitmapForId(bitmapId);
1218 }
1219
1220 @Override
1221 public void writeToParcel(Parcel dest, int flags) {
Adam Cohen5d200642012-04-24 10:43:31 -07001222 dest.writeInt(viewId);
1223 dest.writeString(methodName);
1224 dest.writeInt(bitmapId);
1225 }
1226
1227 @Override
Dianne Hackborna1940212012-06-28 16:07:22 -07001228 public void apply(View root, ViewGroup rootParent,
1229 OnClickHandler handler) throws ActionException {
Adam Cohen5d200642012-04-24 10:43:31 -07001230 ReflectionAction ra = new ReflectionAction(viewId, methodName, ReflectionAction.BITMAP,
1231 bitmap);
Dianne Hackborna1940212012-06-28 16:07:22 -07001232 ra.apply(root, rootParent, handler);
Adam Cohen5d200642012-04-24 10:43:31 -07001233 }
1234
1235 @Override
1236 public void setBitmapCache(BitmapCache bitmapCache) {
1237 bitmapId = bitmapCache.getBitmapId(bitmap);
1238 }
1239
Sunny Goyal5b153922017-09-21 21:00:36 -07001240 @Override
1241 public int getActionTag() {
1242 return BITMAP_REFLECTION_ACTION_TAG;
Adam Cohenfbe44b72012-09-19 20:36:23 -07001243 }
Adam Cohen5d200642012-04-24 10:43:31 -07001244 }
1245
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001246 /**
1247 * Base class for the reflection actions.
1248 */
Romain Guy484f4d62013-07-22 16:39:16 -07001249 private final class ReflectionAction extends Action {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001250 static final int BOOLEAN = 1;
1251 static final int BYTE = 2;
1252 static final int SHORT = 3;
1253 static final int INT = 4;
1254 static final int LONG = 5;
1255 static final int FLOAT = 6;
1256 static final int DOUBLE = 7;
1257 static final int CHAR = 8;
1258 static final int STRING = 9;
1259 static final int CHAR_SEQUENCE = 10;
1260 static final int URI = 11;
Adam Cohenfbe44b72012-09-19 20:36:23 -07001261 // BITMAP actions are never stored in the list of actions. They are only used locally
1262 // to implement BitmapReflectionAction, which eliminates duplicates using BitmapCache.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001263 static final int BITMAP = 12;
Bjorn Bringertd755b062010-01-06 17:15:37 +00001264 static final int BUNDLE = 13;
Winson Chung499cb9f2010-07-16 11:18:17 -07001265 static final int INTENT = 14;
Jorim Jaggief72a192014-08-26 21:57:46 +02001266 static final int COLOR_STATE_LIST = 15;
Dan Sandlera22a3802015-05-13 00:12:47 -04001267 static final int ICON = 16;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001268
Mathew Inwood3a75f262019-06-27 12:47:38 +01001269 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001270 String methodName;
1271 int type;
Mathew Inwood3a75f262019-06-27 12:47:38 +01001272 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001273 Object value;
1274
1275 ReflectionAction(int viewId, String methodName, int type, Object value) {
1276 this.viewId = viewId;
1277 this.methodName = methodName;
1278 this.type = type;
1279 this.value = value;
1280 }
1281
1282 ReflectionAction(Parcel in) {
1283 this.viewId = in.readInt();
1284 this.methodName = in.readString();
1285 this.type = in.readInt();
Romain Guya5475592009-07-01 17:20:08 -07001286 //noinspection ConstantIfStatement
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001287 if (false) {
Adam Cohen50f3d1b2012-12-11 18:36:07 -08001288 Log.d(LOG_TAG, "read viewId=0x" + Integer.toHexString(this.viewId)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001289 + " methodName=" + this.methodName + " type=" + this.type);
1290 }
Adam Cohenc6151f22012-02-02 21:02:31 -08001291
1292 // For some values that may have been null, we first check a flag to see if they were
1293 // written to the parcel.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001294 switch (this.type) {
1295 case BOOLEAN:
Sunny Goyal5b153922017-09-21 21:00:36 -07001296 this.value = in.readBoolean();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001297 break;
1298 case BYTE:
1299 this.value = in.readByte();
1300 break;
1301 case SHORT:
1302 this.value = (short)in.readInt();
1303 break;
1304 case INT:
1305 this.value = in.readInt();
1306 break;
1307 case LONG:
1308 this.value = in.readLong();
1309 break;
1310 case FLOAT:
1311 this.value = in.readFloat();
1312 break;
1313 case DOUBLE:
1314 this.value = in.readDouble();
1315 break;
1316 case CHAR:
1317 this.value = (char)in.readInt();
1318 break;
1319 case STRING:
1320 this.value = in.readString();
1321 break;
1322 case CHAR_SEQUENCE:
1323 this.value = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
1324 break;
1325 case URI:
Sunny Goyal5b153922017-09-21 21:00:36 -07001326 this.value = in.readTypedObject(Uri.CREATOR);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001327 break;
1328 case BITMAP:
Sunny Goyal5b153922017-09-21 21:00:36 -07001329 this.value = in.readTypedObject(Bitmap.CREATOR);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001330 break;
Bjorn Bringertd755b062010-01-06 17:15:37 +00001331 case BUNDLE:
1332 this.value = in.readBundle();
1333 break;
Winson Chung499cb9f2010-07-16 11:18:17 -07001334 case INTENT:
Sunny Goyal5b153922017-09-21 21:00:36 -07001335 this.value = in.readTypedObject(Intent.CREATOR);
Winson Chung499cb9f2010-07-16 11:18:17 -07001336 break;
Jorim Jaggief72a192014-08-26 21:57:46 +02001337 case COLOR_STATE_LIST:
Sunny Goyal5b153922017-09-21 21:00:36 -07001338 this.value = in.readTypedObject(ColorStateList.CREATOR);
Jorim Jaggief72a192014-08-26 21:57:46 +02001339 break;
Dan Sandlera22a3802015-05-13 00:12:47 -04001340 case ICON:
Sunny Goyal5b153922017-09-21 21:00:36 -07001341 this.value = in.readTypedObject(Icon.CREATOR);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001342 default:
1343 break;
1344 }
1345 }
1346
1347 public void writeToParcel(Parcel out, int flags) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001348 out.writeInt(this.viewId);
1349 out.writeString(this.methodName);
1350 out.writeInt(this.type);
Romain Guya5475592009-07-01 17:20:08 -07001351 //noinspection ConstantIfStatement
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001352 if (false) {
Adam Cohen50f3d1b2012-12-11 18:36:07 -08001353 Log.d(LOG_TAG, "write viewId=0x" + Integer.toHexString(this.viewId)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001354 + " methodName=" + this.methodName + " type=" + this.type);
1355 }
Adam Cohenc6151f22012-02-02 21:02:31 -08001356
1357 // For some values which are null, we record an integer flag to indicate whether
1358 // we have written a valid value to the parcel.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001359 switch (this.type) {
1360 case BOOLEAN:
Sunny Goyal5b153922017-09-21 21:00:36 -07001361 out.writeBoolean((Boolean) this.value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001362 break;
1363 case BYTE:
Romain Guya5475592009-07-01 17:20:08 -07001364 out.writeByte((Byte) this.value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001365 break;
1366 case SHORT:
Romain Guya5475592009-07-01 17:20:08 -07001367 out.writeInt((Short) this.value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001368 break;
1369 case INT:
Romain Guya5475592009-07-01 17:20:08 -07001370 out.writeInt((Integer) this.value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001371 break;
1372 case LONG:
Romain Guya5475592009-07-01 17:20:08 -07001373 out.writeLong((Long) this.value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001374 break;
1375 case FLOAT:
Romain Guya5475592009-07-01 17:20:08 -07001376 out.writeFloat((Float) this.value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001377 break;
1378 case DOUBLE:
Romain Guya5475592009-07-01 17:20:08 -07001379 out.writeDouble((Double) this.value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001380 break;
1381 case CHAR:
1382 out.writeInt((int)((Character)this.value).charValue());
1383 break;
1384 case STRING:
1385 out.writeString((String)this.value);
1386 break;
1387 case CHAR_SEQUENCE:
Jim Millere667a7a2012-08-09 19:22:32 -07001388 TextUtils.writeToParcel((CharSequence)this.value, out, flags);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001389 break;
Bjorn Bringertd755b062010-01-06 17:15:37 +00001390 case BUNDLE:
1391 out.writeBundle((Bundle) this.value);
1392 break;
Sunny Goyal56333a82017-08-29 13:46:29 -07001393 case URI:
1394 case BITMAP:
Winson Chung499cb9f2010-07-16 11:18:17 -07001395 case INTENT:
Jorim Jaggief72a192014-08-26 21:57:46 +02001396 case COLOR_STATE_LIST:
Dan Sandlera22a3802015-05-13 00:12:47 -04001397 case ICON:
Sunny Goyal5b153922017-09-21 21:00:36 -07001398 out.writeTypedObject((Parcelable) this.value, flags);
Dan Sandlera22a3802015-05-13 00:12:47 -04001399 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001400 default:
1401 break;
1402 }
1403 }
1404
Romain Guye4d4e202013-07-22 13:02:02 -07001405 private Class<?> getParameterType() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001406 switch (this.type) {
1407 case BOOLEAN:
1408 return boolean.class;
1409 case BYTE:
1410 return byte.class;
1411 case SHORT:
1412 return short.class;
1413 case INT:
1414 return int.class;
1415 case LONG:
1416 return long.class;
1417 case FLOAT:
1418 return float.class;
1419 case DOUBLE:
1420 return double.class;
1421 case CHAR:
1422 return char.class;
1423 case STRING:
1424 return String.class;
1425 case CHAR_SEQUENCE:
1426 return CharSequence.class;
1427 case URI:
1428 return Uri.class;
1429 case BITMAP:
1430 return Bitmap.class;
Bjorn Bringertd755b062010-01-06 17:15:37 +00001431 case BUNDLE:
1432 return Bundle.class;
Winson Chung499cb9f2010-07-16 11:18:17 -07001433 case INTENT:
1434 return Intent.class;
Jorim Jaggief72a192014-08-26 21:57:46 +02001435 case COLOR_STATE_LIST:
1436 return ColorStateList.class;
Dan Sandlera22a3802015-05-13 00:12:47 -04001437 case ICON:
1438 return Icon.class;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001439 default:
1440 return null;
1441 }
1442 }
1443
1444 @Override
Dianne Hackborna1940212012-06-28 16:07:22 -07001445 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001446 final View view = root.findViewById(viewId);
Joe Onorato2b69ce42010-10-31 11:35:41 -07001447 if (view == null) return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001448
Romain Guye4d4e202013-07-22 13:02:02 -07001449 Class<?> param = getParameterType();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001450 if (param == null) {
1451 throw new ActionException("bad type: " + this.type);
1452 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001453 try {
Sunny Goyal271e3222017-08-29 16:05:47 -07001454 getMethod(view, this.methodName, param, false /* async */).invoke(view, this.value);
1455 } catch (Throwable ex) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001456 throw new ActionException(ex);
1457 }
1458 }
Winson Chung3ec9a452010-09-23 16:40:28 -07001459
Sunny Goyaldd292f42015-12-02 14:29:27 -08001460 @Override
1461 public Action initActionAsync(ViewTree root, ViewGroup rootParent, OnClickHandler handler) {
1462 final View view = root.findViewById(viewId);
1463 if (view == null) return ACTION_NOOP;
1464
1465 Class<?> param = getParameterType();
1466 if (param == null) {
1467 throw new ActionException("bad type: " + this.type);
1468 }
1469
1470 try {
Sunny Goyal271e3222017-08-29 16:05:47 -07001471 MethodHandle method = getMethod(view, this.methodName, param, true /* async */);
Sunny Goyaldd292f42015-12-02 14:29:27 -08001472
Sunny Goyal271e3222017-08-29 16:05:47 -07001473 if (method != null) {
1474 Runnable endAction = (Runnable) method.invoke(view, this.value);
Sunny Goyaldd292f42015-12-02 14:29:27 -08001475 if (endAction == null) {
1476 return ACTION_NOOP;
1477 } else {
Sunny Goyal7b0e2c72016-11-03 14:48:05 -07001478 // Special case view stub
1479 if (endAction instanceof ViewStub.ViewReplaceRunnable) {
1480 root.createTree();
1481 // Replace child tree
1482 root.findViewTreeById(viewId).replaceView(
1483 ((ViewStub.ViewReplaceRunnable) endAction).view);
1484 }
Sunny Goyaldd292f42015-12-02 14:29:27 -08001485 return new RunnableAction(endAction);
1486 }
1487 }
Sunny Goyal271e3222017-08-29 16:05:47 -07001488 } catch (Throwable ex) {
Sunny Goyaldd292f42015-12-02 14:29:27 -08001489 throw new ActionException(ex);
1490 }
1491
1492 return this;
1493 }
1494
Adam Cohenfbe44b72012-09-19 20:36:23 -07001495 public int mergeBehavior() {
1496 // smoothScrollBy is cumulative, everything else overwites.
1497 if (methodName.equals("smoothScrollBy")) {
1498 return MERGE_APPEND;
1499 } else {
1500 return MERGE_REPLACE;
Winson Chung3ec9a452010-09-23 16:40:28 -07001501 }
1502 }
Adam Cohenfbe44b72012-09-19 20:36:23 -07001503
Sunny Goyal5b153922017-09-21 21:00:36 -07001504 @Override
1505 public int getActionTag() {
1506 return REFLECTION_ACTION_TAG;
1507 }
1508
1509 @Override
1510 public String getUniqueKey() {
Adam Cohenfbe44b72012-09-19 20:36:23 -07001511 // Each type of reflection action corresponds to a setter, so each should be seen as
1512 // unique from the standpoint of merging.
Sunny Goyal5b153922017-09-21 21:00:36 -07001513 return super.getUniqueKey() + this.methodName + this.type;
Adam Cohenfbe44b72012-09-19 20:36:23 -07001514 }
Sunny Goyal5c022632016-02-17 16:30:41 -08001515
1516 @Override
1517 public boolean prefersAsyncApply() {
1518 return this.type == URI || this.type == ICON;
1519 }
Jeff Sharkey23b31182018-04-18 21:32:12 -06001520
1521 @Override
1522 public void visitUris(@NonNull Consumer<Uri> visitor) {
1523 switch (this.type) {
1524 case URI:
1525 final Uri uri = (Uri) this.value;
1526 visitor.accept(uri);
1527 break;
1528 case ICON:
1529 final Icon icon = (Icon) this.value;
1530 visitIconUri(icon, visitor);
1531 break;
1532 }
1533 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001534 }
1535
Sunny Goyaldd292f42015-12-02 14:29:27 -08001536 /**
1537 * This is only used for async execution of actions and it not parcelable.
1538 */
1539 private static final class RunnableAction extends RuntimeAction {
1540 private final Runnable mRunnable;
1541
1542 RunnableAction(Runnable r) {
1543 mRunnable = r;
1544 }
1545
1546 @Override
1547 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
1548 mRunnable.run();
1549 }
1550 }
1551
Adam Cohen5d200642012-04-24 10:43:31 -07001552 private void configureRemoteViewsAsChild(RemoteViews rv) {
Adam Cohen5d200642012-04-24 10:43:31 -07001553 rv.setBitmapCache(mBitmapCache);
1554 rv.setNotRoot();
1555 }
1556
1557 void setNotRoot() {
1558 mIsRoot = false;
1559 }
1560
Jeff Sharkey1162fd72009-11-04 17:58:08 -08001561 /**
Anthony Chen8f5f3582017-04-11 11:18:37 -07001562 * ViewGroup methods that are related to adding Views.
Jeff Sharkey1162fd72009-11-04 17:58:08 -08001563 */
Anthony Chen8f5f3582017-04-11 11:18:37 -07001564 private class ViewGroupActionAdd extends Action {
Mathew Inwood3a75f262019-06-27 12:47:38 +01001565 @UnsupportedAppUsage
Anthony Chen8f5f3582017-04-11 11:18:37 -07001566 private RemoteViews mNestedViews;
1567 private int mIndex;
1568
1569 ViewGroupActionAdd(int viewId, RemoteViews nestedViews) {
1570 this(viewId, nestedViews, -1 /* index */);
1571 }
1572
1573 ViewGroupActionAdd(int viewId, RemoteViews nestedViews, int index) {
Jeff Sharkey1162fd72009-11-04 17:58:08 -08001574 this.viewId = viewId;
Anthony Chen8f5f3582017-04-11 11:18:37 -07001575 mNestedViews = nestedViews;
1576 mIndex = index;
Adam Cohenc431c152012-04-26 18:42:17 -07001577 if (nestedViews != null) {
1578 configureRemoteViewsAsChild(nestedViews);
1579 }
Jeff Sharkey1162fd72009-11-04 17:58:08 -08001580 }
1581
Anthony Chen8f5f3582017-04-11 11:18:37 -07001582 ViewGroupActionAdd(Parcel parcel, BitmapCache bitmapCache, ApplicationInfo info,
Adrian Roosfb921842017-10-26 14:49:56 +02001583 int depth, Map<Class, Object> classCookies) {
Jeff Sharkey1162fd72009-11-04 17:58:08 -08001584 viewId = parcel.readInt();
Anthony Chenea202f62017-07-05 11:22:25 -07001585 mIndex = parcel.readInt();
Adrian Roosfb921842017-10-26 14:49:56 +02001586 mNestedViews = new RemoteViews(parcel, bitmapCache, info, depth, classCookies);
Sunny Goyalc12d31c2018-11-12 16:29:18 -08001587 mNestedViews.addFlags(mApplyFlags);
Jeff Sharkey1162fd72009-11-04 17:58:08 -08001588 }
1589
1590 public void writeToParcel(Parcel dest, int flags) {
Jeff Sharkey1162fd72009-11-04 17:58:08 -08001591 dest.writeInt(viewId);
Anthony Chenea202f62017-07-05 11:22:25 -07001592 dest.writeInt(mIndex);
Anthony Chen8f5f3582017-04-11 11:18:37 -07001593 mNestedViews.writeToParcel(dest, flags);
Jeff Sharkey1162fd72009-11-04 17:58:08 -08001594 }
1595
1596 @Override
Sunny Goyal5d8bcdf2016-10-27 16:11:29 -07001597 public boolean hasSameAppInfo(ApplicationInfo parentInfo) {
Sunny Goyaldd60f4d2017-10-18 15:22:42 -07001598 return mNestedViews.hasSameAppInfo(parentInfo);
Sunny Goyal5d8bcdf2016-10-27 16:11:29 -07001599 }
1600
1601 @Override
Dianne Hackborna1940212012-06-28 16:07:22 -07001602 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
Jeff Sharkey1162fd72009-11-04 17:58:08 -08001603 final Context context = root.getContext();
Alan Viverette8e1a7292017-02-27 10:57:58 -05001604 final ViewGroup target = root.findViewById(viewId);
Anthony Chen8f5f3582017-04-11 11:18:37 -07001605
1606 if (target == null) {
1607 return;
Jeff Sharkey1162fd72009-11-04 17:58:08 -08001608 }
Anthony Chen8f5f3582017-04-11 11:18:37 -07001609
1610 // Inflate nested views and add as children
1611 target.addView(mNestedViews.apply(context, target, handler), mIndex);
Jeff Sharkey1162fd72009-11-04 17:58:08 -08001612 }
1613
Winson Chung3ec9a452010-09-23 16:40:28 -07001614 @Override
Sunny Goyaldd292f42015-12-02 14:29:27 -08001615 public Action initActionAsync(ViewTree root, ViewGroup rootParent, OnClickHandler handler) {
1616 // In the async implementation, update the view tree so that subsequent calls to
Anthony Chen8f5f3582017-04-11 11:18:37 -07001617 // findViewById return the current view.
Sunny Goyaldd292f42015-12-02 14:29:27 -08001618 root.createTree();
1619 ViewTree target = root.findViewTreeById(viewId);
1620 if ((target == null) || !(target.mRoot instanceof ViewGroup)) {
1621 return ACTION_NOOP;
1622 }
Sunny Goyal7b0e2c72016-11-03 14:48:05 -07001623 final ViewGroup targetVg = (ViewGroup) target.mRoot;
Sunny Goyaldd292f42015-12-02 14:29:27 -08001624
Anthony Chen8f5f3582017-04-11 11:18:37 -07001625 // Inflate nested views and perform all the async tasks for the child remoteView.
1626 final Context context = root.mRoot.getContext();
1627 final AsyncApplyTask task = mNestedViews.getAsyncApplyTask(
1628 context, targetVg, null, handler);
1629 final ViewTree tree = task.doInBackground();
Sunny Goyaldd292f42015-12-02 14:29:27 -08001630
Anthony Chen8f5f3582017-04-11 11:18:37 -07001631 if (tree == null) {
1632 throw new ActionException(task.mError);
Sunny Goyaldd292f42015-12-02 14:29:27 -08001633 }
Anthony Chen8f5f3582017-04-11 11:18:37 -07001634
1635 // Update the global view tree, so that next call to findViewTreeById
1636 // goes through the subtree as well.
1637 target.addChild(tree, mIndex);
1638
1639 return new RuntimeAction() {
1640 @Override
1641 public void apply(View root, ViewGroup rootParent, OnClickHandler handler)
1642 throws ActionException {
1643 task.onPostExecute(tree);
1644 targetVg.addView(task.mResult, mIndex);
1645 }
1646 };
Sunny Goyaldd292f42015-12-02 14:29:27 -08001647 }
1648
1649 @Override
Adam Cohen5d200642012-04-24 10:43:31 -07001650 public void setBitmapCache(BitmapCache bitmapCache) {
Anthony Chen8f5f3582017-04-11 11:18:37 -07001651 mNestedViews.setBitmapCache(bitmapCache);
Winson Chung3ec9a452010-09-23 16:40:28 -07001652 }
1653
Anthony Chen8f5f3582017-04-11 11:18:37 -07001654 @Override
Adam Cohenfbe44b72012-09-19 20:36:23 -07001655 public int mergeBehavior() {
1656 return MERGE_APPEND;
1657 }
1658
Sunny Goyal5c022632016-02-17 16:30:41 -08001659 @Override
1660 public boolean prefersAsyncApply() {
Anthony Chen8f5f3582017-04-11 11:18:37 -07001661 return mNestedViews.prefersAsyncApply();
Sunny Goyal5c022632016-02-17 16:30:41 -08001662 }
1663
Anthony Chen8f5f3582017-04-11 11:18:37 -07001664 @Override
Sunny Goyal5b153922017-09-21 21:00:36 -07001665 public int getActionTag() {
1666 return VIEW_GROUP_ACTION_ADD_TAG;
Anthony Chen8f5f3582017-04-11 11:18:37 -07001667 }
1668 }
1669
1670 /**
1671 * ViewGroup methods related to removing child views.
1672 */
1673 private class ViewGroupActionRemove extends Action {
1674 /**
1675 * Id that indicates that all child views of the affected ViewGroup should be removed.
1676 *
1677 * <p>Using -2 because the default id is -1. This avoids accidentally matching that.
1678 */
1679 private static final int REMOVE_ALL_VIEWS_ID = -2;
1680
1681 private int mViewIdToKeep;
1682
1683 ViewGroupActionRemove(int viewId) {
1684 this(viewId, REMOVE_ALL_VIEWS_ID);
1685 }
1686
1687 ViewGroupActionRemove(int viewId, int viewIdToKeep) {
1688 this.viewId = viewId;
1689 mViewIdToKeep = viewIdToKeep;
1690 }
1691
1692 ViewGroupActionRemove(Parcel parcel) {
1693 viewId = parcel.readInt();
1694 mViewIdToKeep = parcel.readInt();
1695 }
1696
1697 public void writeToParcel(Parcel dest, int flags) {
Anthony Chen8f5f3582017-04-11 11:18:37 -07001698 dest.writeInt(viewId);
1699 dest.writeInt(mViewIdToKeep);
1700 }
1701
1702 @Override
1703 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
1704 final ViewGroup target = root.findViewById(viewId);
1705
1706 if (target == null) {
1707 return;
1708 }
1709
1710 if (mViewIdToKeep == REMOVE_ALL_VIEWS_ID) {
1711 target.removeAllViews();
1712 return;
1713 }
1714
1715 removeAllViewsExceptIdToKeep(target);
1716 }
1717
1718 @Override
1719 public Action initActionAsync(ViewTree root, ViewGroup rootParent, OnClickHandler handler) {
1720 // In the async implementation, update the view tree so that subsequent calls to
1721 // findViewById return the current view.
1722 root.createTree();
1723 ViewTree target = root.findViewTreeById(viewId);
1724
1725 if ((target == null) || !(target.mRoot instanceof ViewGroup)) {
1726 return ACTION_NOOP;
1727 }
1728
1729 final ViewGroup targetVg = (ViewGroup) target.mRoot;
1730
1731 // Clear all children when nested views omitted
1732 target.mChildren = null;
1733 return new RuntimeAction() {
1734 @Override
1735 public void apply(View root, ViewGroup rootParent, OnClickHandler handler)
1736 throws ActionException {
1737 if (mViewIdToKeep == REMOVE_ALL_VIEWS_ID) {
1738 targetVg.removeAllViews();
1739 return;
1740 }
1741
1742 removeAllViewsExceptIdToKeep(targetVg);
1743 }
1744 };
1745 }
1746
1747 /**
1748 * Iterates through the children in the given ViewGroup and removes all the views that
1749 * do not have an id of {@link #mViewIdToKeep}.
1750 */
1751 private void removeAllViewsExceptIdToKeep(ViewGroup viewGroup) {
1752 // Otherwise, remove all the views that do not match the id to keep.
1753 int index = viewGroup.getChildCount() - 1;
1754 while (index >= 0) {
1755 if (viewGroup.getChildAt(index).getId() != mViewIdToKeep) {
1756 viewGroup.removeViewAt(index);
1757 }
1758 index--;
1759 }
1760 }
1761
1762 @Override
Sunny Goyal5b153922017-09-21 21:00:36 -07001763 public int getActionTag() {
1764 return VIEW_GROUP_ACTION_REMOVE_TAG;
Anthony Chen8f5f3582017-04-11 11:18:37 -07001765 }
1766
1767 @Override
1768 public int mergeBehavior() {
1769 return MERGE_APPEND;
1770 }
Jeff Sharkey1162fd72009-11-04 17:58:08 -08001771 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001772
1773 /**
Daniel Sandler820ba322012-03-23 16:36:00 -05001774 * Helper action to set compound drawables on a TextView. Supports relative
1775 * (s/t/e/b) or cardinal (l/t/r/b) arrangement.
1776 */
1777 private class TextViewDrawableAction extends Action {
1778 public TextViewDrawableAction(int viewId, boolean isRelative, int d1, int d2, int d3, int d4) {
1779 this.viewId = viewId;
1780 this.isRelative = isRelative;
Dan Sandler912282e2015-07-28 22:49:30 -04001781 this.useIcons = false;
Daniel Sandler820ba322012-03-23 16:36:00 -05001782 this.d1 = d1;
1783 this.d2 = d2;
1784 this.d3 = d3;
1785 this.d4 = d4;
1786 }
1787
Dan Sandler912282e2015-07-28 22:49:30 -04001788 public TextViewDrawableAction(int viewId, boolean isRelative,
1789 Icon i1, Icon i2, Icon i3, Icon i4) {
1790 this.viewId = viewId;
1791 this.isRelative = isRelative;
1792 this.useIcons = true;
1793 this.i1 = i1;
1794 this.i2 = i2;
1795 this.i3 = i3;
1796 this.i4 = i4;
1797 }
1798
Daniel Sandler820ba322012-03-23 16:36:00 -05001799 public TextViewDrawableAction(Parcel parcel) {
1800 viewId = parcel.readInt();
1801 isRelative = (parcel.readInt() != 0);
Dan Sandler912282e2015-07-28 22:49:30 -04001802 useIcons = (parcel.readInt() != 0);
1803 if (useIcons) {
Sunny Goyal5b153922017-09-21 21:00:36 -07001804 i1 = parcel.readTypedObject(Icon.CREATOR);
1805 i2 = parcel.readTypedObject(Icon.CREATOR);
1806 i3 = parcel.readTypedObject(Icon.CREATOR);
1807 i4 = parcel.readTypedObject(Icon.CREATOR);
Dan Sandler912282e2015-07-28 22:49:30 -04001808 } else {
1809 d1 = parcel.readInt();
1810 d2 = parcel.readInt();
1811 d3 = parcel.readInt();
1812 d4 = parcel.readInt();
1813 }
Daniel Sandler820ba322012-03-23 16:36:00 -05001814 }
1815
1816 public void writeToParcel(Parcel dest, int flags) {
Daniel Sandler820ba322012-03-23 16:36:00 -05001817 dest.writeInt(viewId);
1818 dest.writeInt(isRelative ? 1 : 0);
Dan Sandler912282e2015-07-28 22:49:30 -04001819 dest.writeInt(useIcons ? 1 : 0);
1820 if (useIcons) {
Sunny Goyal5b153922017-09-21 21:00:36 -07001821 dest.writeTypedObject(i1, 0);
1822 dest.writeTypedObject(i2, 0);
1823 dest.writeTypedObject(i3, 0);
1824 dest.writeTypedObject(i4, 0);
Dan Sandler912282e2015-07-28 22:49:30 -04001825 } else {
1826 dest.writeInt(d1);
1827 dest.writeInt(d2);
1828 dest.writeInt(d3);
1829 dest.writeInt(d4);
1830 }
Daniel Sandler820ba322012-03-23 16:36:00 -05001831 }
1832
1833 @Override
Dianne Hackborna1940212012-06-28 16:07:22 -07001834 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
Alan Viverette8e1a7292017-02-27 10:57:58 -05001835 final TextView target = root.findViewById(viewId);
Daniel Sandler820ba322012-03-23 16:36:00 -05001836 if (target == null) return;
Sunny Goyaldd292f42015-12-02 14:29:27 -08001837 if (drawablesLoaded) {
1838 if (isRelative) {
1839 target.setCompoundDrawablesRelativeWithIntrinsicBounds(id1, id2, id3, id4);
1840 } else {
1841 target.setCompoundDrawablesWithIntrinsicBounds(id1, id2, id3, id4);
1842 }
1843 } else if (useIcons) {
Dan Sandler912282e2015-07-28 22:49:30 -04001844 final Context ctx = target.getContext();
1845 final Drawable id1 = i1 == null ? null : i1.loadDrawable(ctx);
1846 final Drawable id2 = i2 == null ? null : i2.loadDrawable(ctx);
1847 final Drawable id3 = i3 == null ? null : i3.loadDrawable(ctx);
1848 final Drawable id4 = i4 == null ? null : i4.loadDrawable(ctx);
1849 if (isRelative) {
1850 target.setCompoundDrawablesRelativeWithIntrinsicBounds(id1, id2, id3, id4);
1851 } else {
1852 target.setCompoundDrawablesWithIntrinsicBounds(id1, id2, id3, id4);
1853 }
Daniel Sandler820ba322012-03-23 16:36:00 -05001854 } else {
Dan Sandler912282e2015-07-28 22:49:30 -04001855 if (isRelative) {
1856 target.setCompoundDrawablesRelativeWithIntrinsicBounds(d1, d2, d3, d4);
1857 } else {
1858 target.setCompoundDrawablesWithIntrinsicBounds(d1, d2, d3, d4);
1859 }
Daniel Sandler820ba322012-03-23 16:36:00 -05001860 }
1861 }
1862
Sunny Goyaldd292f42015-12-02 14:29:27 -08001863 @Override
1864 public Action initActionAsync(ViewTree root, ViewGroup rootParent, OnClickHandler handler) {
Alan Viverette04fd4702017-04-13 16:37:06 -04001865 final TextView target = root.findViewById(viewId);
Sunny Goyaldd292f42015-12-02 14:29:27 -08001866 if (target == null) return ACTION_NOOP;
1867
1868 TextViewDrawableAction copy = useIcons ?
1869 new TextViewDrawableAction(viewId, isRelative, i1, i2, i3, i4) :
1870 new TextViewDrawableAction(viewId, isRelative, d1, d2, d3, d4);
1871
1872 // Load the drawables on the background thread.
1873 copy.drawablesLoaded = true;
1874 final Context ctx = target.getContext();
1875
1876 if (useIcons) {
1877 copy.id1 = i1 == null ? null : i1.loadDrawable(ctx);
1878 copy.id2 = i2 == null ? null : i2.loadDrawable(ctx);
1879 copy.id3 = i3 == null ? null : i3.loadDrawable(ctx);
1880 copy.id4 = i4 == null ? null : i4.loadDrawable(ctx);
1881 } else {
1882 copy.id1 = d1 == 0 ? null : ctx.getDrawable(d1);
1883 copy.id2 = d2 == 0 ? null : ctx.getDrawable(d2);
1884 copy.id3 = d3 == 0 ? null : ctx.getDrawable(d3);
1885 copy.id4 = d4 == 0 ? null : ctx.getDrawable(d4);
1886 }
1887 return copy;
1888 }
1889
Sunny Goyal5c022632016-02-17 16:30:41 -08001890 @Override
1891 public boolean prefersAsyncApply() {
1892 return useIcons;
1893 }
1894
Sunny Goyal5b153922017-09-21 21:00:36 -07001895 @Override
1896 public int getActionTag() {
1897 return TEXT_VIEW_DRAWABLE_ACTION_TAG;
Adam Cohenfbe44b72012-09-19 20:36:23 -07001898 }
1899
Jeff Sharkey23b31182018-04-18 21:32:12 -06001900 @Override
1901 public void visitUris(@NonNull Consumer<Uri> visitor) {
1902 if (useIcons) {
1903 visitIconUri(i1, visitor);
1904 visitIconUri(i2, visitor);
1905 visitIconUri(i3, visitor);
1906 visitIconUri(i4, visitor);
1907 }
1908 }
1909
Daniel Sandler820ba322012-03-23 16:36:00 -05001910 boolean isRelative = false;
Dan Sandler912282e2015-07-28 22:49:30 -04001911 boolean useIcons = false;
Daniel Sandler820ba322012-03-23 16:36:00 -05001912 int d1, d2, d3, d4;
Dan Sandler912282e2015-07-28 22:49:30 -04001913 Icon i1, i2, i3, i4;
Daniel Sandler820ba322012-03-23 16:36:00 -05001914
Sunny Goyaldd292f42015-12-02 14:29:27 -08001915 boolean drawablesLoaded = false;
1916 Drawable id1, id2, id3, id4;
Daniel Sandler820ba322012-03-23 16:36:00 -05001917 }
1918
1919 /**
Daniel Sandler99d1f742012-05-21 16:14:14 -04001920 * Helper action to set text size on a TextView in any supported units.
Daniel Sandler7264f712012-05-21 14:48:23 -04001921 */
1922 private class TextViewSizeAction extends Action {
1923 public TextViewSizeAction(int viewId, int units, float size) {
1924 this.viewId = viewId;
1925 this.units = units;
1926 this.size = size;
1927 }
1928
1929 public TextViewSizeAction(Parcel parcel) {
1930 viewId = parcel.readInt();
1931 units = parcel.readInt();
1932 size = parcel.readFloat();
1933 }
1934
1935 public void writeToParcel(Parcel dest, int flags) {
Daniel Sandler7264f712012-05-21 14:48:23 -04001936 dest.writeInt(viewId);
1937 dest.writeInt(units);
1938 dest.writeFloat(size);
1939 }
1940
1941 @Override
Dianne Hackborna1940212012-06-28 16:07:22 -07001942 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
Alan Viverette8e1a7292017-02-27 10:57:58 -05001943 final TextView target = root.findViewById(viewId);
Daniel Sandler7264f712012-05-21 14:48:23 -04001944 if (target == null) return;
1945 target.setTextSize(units, size);
1946 }
1947
Sunny Goyal5b153922017-09-21 21:00:36 -07001948 @Override
1949 public int getActionTag() {
1950 return TEXT_VIEW_SIZE_ACTION_TAG;
Adam Cohenfbe44b72012-09-19 20:36:23 -07001951 }
1952
Daniel Sandler7264f712012-05-21 14:48:23 -04001953 int units;
1954 float size;
Daniel Sandler7264f712012-05-21 14:48:23 -04001955 }
1956
1957 /**
Daniel Sandler99d1f742012-05-21 16:14:14 -04001958 * Helper action to set padding on a View.
1959 */
1960 private class ViewPaddingAction extends Action {
1961 public ViewPaddingAction(int viewId, int left, int top, int right, int bottom) {
1962 this.viewId = viewId;
1963 this.left = left;
1964 this.top = top;
1965 this.right = right;
1966 this.bottom = bottom;
1967 }
1968
1969 public ViewPaddingAction(Parcel parcel) {
1970 viewId = parcel.readInt();
1971 left = parcel.readInt();
1972 top = parcel.readInt();
1973 right = parcel.readInt();
1974 bottom = parcel.readInt();
1975 }
1976
1977 public void writeToParcel(Parcel dest, int flags) {
Daniel Sandler99d1f742012-05-21 16:14:14 -04001978 dest.writeInt(viewId);
1979 dest.writeInt(left);
1980 dest.writeInt(top);
1981 dest.writeInt(right);
1982 dest.writeInt(bottom);
1983 }
1984
1985 @Override
Dianne Hackborna1940212012-06-28 16:07:22 -07001986 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
Daniel Sandler99d1f742012-05-21 16:14:14 -04001987 final View target = root.findViewById(viewId);
1988 if (target == null) return;
1989 target.setPadding(left, top, right, bottom);
1990 }
1991
Sunny Goyal5b153922017-09-21 21:00:36 -07001992 @Override
1993 public int getActionTag() {
1994 return VIEW_PADDING_ACTION_TAG;
Adam Cohenfbe44b72012-09-19 20:36:23 -07001995 }
1996
Daniel Sandler99d1f742012-05-21 16:14:14 -04001997 int left, top, right, bottom;
Daniel Sandler99d1f742012-05-21 16:14:14 -04001998 }
1999
2000 /**
Adrian Roos9b123cf2016-02-04 14:55:57 -08002001 * Helper action to set layout params on a View.
Selim Cinek65b2e7c2015-10-26 14:11:31 -07002002 */
Adrian Roos2d5dbba2016-06-08 17:11:53 -07002003 private static class LayoutParamAction extends Action {
Adrian Roos9b123cf2016-02-04 14:55:57 -08002004
2005 /** Set marginEnd */
Adrian Roos2d5dbba2016-06-08 17:11:53 -07002006 public static final int LAYOUT_MARGIN_END_DIMEN = 1;
Adrian Roos9b123cf2016-02-04 14:55:57 -08002007 /** Set width */
2008 public static final int LAYOUT_WIDTH = 2;
Adrian Roos2d5dbba2016-06-08 17:11:53 -07002009 public static final int LAYOUT_MARGIN_BOTTOM_DIMEN = 3;
Selim Cinek384804b2018-04-18 14:31:07 +08002010 public static final int LAYOUT_MARGIN_END = 4;
Adrian Roos9b123cf2016-02-04 14:55:57 -08002011
Sunny Goyal5b153922017-09-21 21:00:36 -07002012 final int mProperty;
2013 final int mValue;
2014
Adrian Roos9b123cf2016-02-04 14:55:57 -08002015 /**
2016 * @param viewId ID of the view alter
2017 * @param property which layout parameter to alter
2018 * @param value new value of the layout parameter
2019 */
2020 public LayoutParamAction(int viewId, int property, int value) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07002021 this.viewId = viewId;
Sunny Goyal5b153922017-09-21 21:00:36 -07002022 this.mProperty = property;
2023 this.mValue = value;
Selim Cinek65b2e7c2015-10-26 14:11:31 -07002024 }
2025
Adrian Roos9b123cf2016-02-04 14:55:57 -08002026 public LayoutParamAction(Parcel parcel) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07002027 viewId = parcel.readInt();
Sunny Goyal5b153922017-09-21 21:00:36 -07002028 mProperty = parcel.readInt();
2029 mValue = parcel.readInt();
Selim Cinek65b2e7c2015-10-26 14:11:31 -07002030 }
2031
2032 public void writeToParcel(Parcel dest, int flags) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07002033 dest.writeInt(viewId);
Sunny Goyal5b153922017-09-21 21:00:36 -07002034 dest.writeInt(mProperty);
2035 dest.writeInt(mValue);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07002036 }
2037
2038 @Override
2039 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
2040 final View target = root.findViewById(viewId);
2041 if (target == null) {
2042 return;
2043 }
2044 ViewGroup.LayoutParams layoutParams = target.getLayoutParams();
Adrian Roos9b123cf2016-02-04 14:55:57 -08002045 if (layoutParams == null) {
2046 return;
2047 }
Selim Cinek384804b2018-04-18 14:31:07 +08002048 int value = mValue;
Sunny Goyal5b153922017-09-21 21:00:36 -07002049 switch (mProperty) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07002050 case LAYOUT_MARGIN_END_DIMEN:
Selim Cinek384804b2018-04-18 14:31:07 +08002051 value = resolveDimenPixelOffset(target, mValue);
2052 // fall-through
2053 case LAYOUT_MARGIN_END:
Adrian Roos9b123cf2016-02-04 14:55:57 -08002054 if (layoutParams instanceof ViewGroup.MarginLayoutParams) {
Selim Cinek384804b2018-04-18 14:31:07 +08002055 ((ViewGroup.MarginLayoutParams) layoutParams).setMarginEnd(value);
Adrian Roos9b123cf2016-02-04 14:55:57 -08002056 target.setLayoutParams(layoutParams);
2057 }
2058 break;
Adrian Roos2d5dbba2016-06-08 17:11:53 -07002059 case LAYOUT_MARGIN_BOTTOM_DIMEN:
Adrian Roosc1a80b02016-04-05 14:54:55 -07002060 if (layoutParams instanceof ViewGroup.MarginLayoutParams) {
Sunny Goyal5b153922017-09-21 21:00:36 -07002061 int resolved = resolveDimenPixelOffset(target, mValue);
Adrian Roos2d5dbba2016-06-08 17:11:53 -07002062 ((ViewGroup.MarginLayoutParams) layoutParams).bottomMargin = resolved;
Adrian Roosc1a80b02016-04-05 14:54:55 -07002063 target.setLayoutParams(layoutParams);
2064 }
2065 break;
Adrian Roos9b123cf2016-02-04 14:55:57 -08002066 case LAYOUT_WIDTH:
Sunny Goyal5b153922017-09-21 21:00:36 -07002067 layoutParams.width = mValue;
Adrian Roos9b123cf2016-02-04 14:55:57 -08002068 target.setLayoutParams(layoutParams);
2069 break;
2070 default:
Sunny Goyal5b153922017-09-21 21:00:36 -07002071 throw new IllegalArgumentException("Unknown property " + mProperty);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07002072 }
2073 }
2074
Adrian Roos2d5dbba2016-06-08 17:11:53 -07002075 private static int resolveDimenPixelOffset(View target, int value) {
2076 if (value == 0) {
2077 return 0;
2078 }
2079 return target.getContext().getResources().getDimensionPixelOffset(value);
2080 }
2081
Sunny Goyal5b153922017-09-21 21:00:36 -07002082 @Override
2083 public int getActionTag() {
2084 return LAYOUT_PARAM_ACTION_TAG;
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01002085 }
2086
2087 @Override
Sunny Goyal5b153922017-09-21 21:00:36 -07002088 public String getUniqueKey() {
2089 return super.getUniqueKey() + mProperty;
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01002090 }
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01002091 }
2092
2093 /**
Adrian Roosfe84e1f2015-11-04 15:55:39 -08002094 * Helper action to add a view tag with RemoteInputs.
2095 */
2096 private class SetRemoteInputsAction extends Action {
2097
2098 public SetRemoteInputsAction(int viewId, RemoteInput[] remoteInputs) {
2099 this.viewId = viewId;
2100 this.remoteInputs = remoteInputs;
2101 }
2102
2103 public SetRemoteInputsAction(Parcel parcel) {
2104 viewId = parcel.readInt();
Adrian Roos5dd685f2016-02-24 12:05:51 -08002105 remoteInputs = parcel.createTypedArray(RemoteInput.CREATOR);
Adrian Roosfe84e1f2015-11-04 15:55:39 -08002106 }
2107
2108 public void writeToParcel(Parcel dest, int flags) {
Adrian Roosfe84e1f2015-11-04 15:55:39 -08002109 dest.writeInt(viewId);
Adrian Roos5dd685f2016-02-24 12:05:51 -08002110 dest.writeTypedArray(remoteInputs, flags);
Adrian Roosfe84e1f2015-11-04 15:55:39 -08002111 }
2112
2113 @Override
2114 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
Selim Cinek5d6ef8d2017-05-18 22:16:00 -07002115 final View target = root.findViewById(viewId);
Adrian Roosfe84e1f2015-11-04 15:55:39 -08002116 if (target == null) return;
2117
2118 target.setTagInternal(R.id.remote_input_tag, remoteInputs);
2119 }
2120
Sunny Goyal5b153922017-09-21 21:00:36 -07002121 @Override
2122 public int getActionTag() {
2123 return SET_REMOTE_INPUTS_ACTION_TAG;
Adrian Roosfe84e1f2015-11-04 15:55:39 -08002124 }
2125
2126 final Parcelable[] remoteInputs;
Adrian Roosfe84e1f2015-11-04 15:55:39 -08002127 }
2128
2129 /**
Selim Cinek87c31532017-08-18 18:53:44 -07002130 * Helper action to override all textViewColors
2131 */
2132 private class OverrideTextColorsAction extends Action {
2133
2134 private final int textColor;
2135
2136 public OverrideTextColorsAction(int textColor) {
2137 this.textColor = textColor;
2138 }
2139
2140 public OverrideTextColorsAction(Parcel parcel) {
2141 textColor = parcel.readInt();
2142 }
2143
2144 public void writeToParcel(Parcel dest, int flags) {
Selim Cinek87c31532017-08-18 18:53:44 -07002145 dest.writeInt(textColor);
2146 }
2147
2148 @Override
2149 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
2150 // Let's traverse the viewtree and override all textColors!
2151 Stack<View> viewsToProcess = new Stack<>();
2152 viewsToProcess.add(root);
2153 while (!viewsToProcess.isEmpty()) {
2154 View v = viewsToProcess.pop();
2155 if (v instanceof TextView) {
2156 TextView textView = (TextView) v;
Lucas Dupina291d192018-06-07 13:59:42 -07002157 textView.setText(ContrastColorUtil.clearColorSpans(textView.getText()));
Selim Cinek87c31532017-08-18 18:53:44 -07002158 textView.setTextColor(textColor);
2159 }
2160 if (v instanceof ViewGroup) {
2161 ViewGroup viewGroup = (ViewGroup) v;
2162 for (int i = 0; i < viewGroup.getChildCount(); i++) {
2163 viewsToProcess.push(viewGroup.getChildAt(i));
2164 }
2165 }
2166 }
2167 }
2168
Sunny Goyal5b153922017-09-21 21:00:36 -07002169 @Override
2170 public int getActionTag() {
2171 return OVERRIDE_TEXT_COLORS_TAG;
Selim Cinek87c31532017-08-18 18:53:44 -07002172 }
2173 }
2174
Tony Mak7d4b3a52018-11-27 17:29:36 +00002175 private class SetIntTagAction extends Action {
2176 private final int mViewId;
2177 private final int mKey;
2178 private final int mTag;
2179
2180 SetIntTagAction(int viewId, int key, int tag) {
2181 mViewId = viewId;
2182 mKey = key;
2183 mTag = tag;
2184 }
2185
2186 SetIntTagAction(Parcel parcel) {
2187 mViewId = parcel.readInt();
2188 mKey = parcel.readInt();
2189 mTag = parcel.readInt();
2190 }
2191
2192 public void writeToParcel(Parcel dest, int flags) {
2193 dest.writeInt(mViewId);
2194 dest.writeInt(mKey);
2195 dest.writeInt(mTag);
2196 }
2197
2198 @Override
2199 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
2200 final View target = root.findViewById(mViewId);
2201 if (target == null) return;
2202
2203 target.setTagInternal(mKey, mTag);
2204 }
2205
2206 @Override
2207 public int getActionTag() {
2208 return SET_INT_TAG_TAG;
2209 }
2210 }
2211
Selim Cinek87c31532017-08-18 18:53:44 -07002212 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002213 * Create a new RemoteViews object that will display the views contained
2214 * in the specified layout file.
Jim Millere667a7a2012-08-09 19:22:32 -07002215 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002216 * @param packageName Name of the package that contains the layout resource
2217 * @param layoutId The id of the layout resource
2218 */
2219 public RemoteViews(String packageName, int layoutId) {
Svet Ganov0da85b62014-08-06 14:11:37 -07002220 this(getApplicationInfo(packageName, UserHandle.myUserId()), layoutId);
2221 }
2222
2223 /**
2224 * Create a new RemoteViews object that will display the views contained
2225 * in the specified layout file.
2226 *
2227 * @param packageName Name of the package that contains the layout resource.
2228 * @param userId The user under which the package is running.
2229 * @param layoutId The id of the layout resource.
2230 *
2231 * @hide
2232 */
Sunny Goyalc12d31c2018-11-12 16:29:18 -08002233 public RemoteViews(String packageName, int userId, @LayoutRes int layoutId) {
Svet Ganov0da85b62014-08-06 14:11:37 -07002234 this(getApplicationInfo(packageName, userId), layoutId);
2235 }
2236
2237 /**
2238 * Create a new RemoteViews object that will display the views contained
2239 * in the specified layout file.
2240 *
2241 * @param application The application whose content is shown by the views.
2242 * @param layoutId The id of the layout resource.
Kenny Guy77320062014-08-27 21:37:15 +01002243 *
2244 * @hide
Svet Ganov0da85b62014-08-06 14:11:37 -07002245 */
Sunny Goyalc12d31c2018-11-12 16:29:18 -08002246 protected RemoteViews(ApplicationInfo application, @LayoutRes int layoutId) {
Svet Ganov0da85b62014-08-06 14:11:37 -07002247 mApplication = application;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002248 mLayoutId = layoutId;
Adam Cohen5d200642012-04-24 10:43:31 -07002249 mBitmapCache = new BitmapCache();
Adrian Roosfb921842017-10-26 14:49:56 +02002250 mClassCookies = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002251 }
2252
Adam Cohen5d200642012-04-24 10:43:31 -07002253 private boolean hasLandscapeAndPortraitLayouts() {
2254 return (mLandscape != null) && (mPortrait != null);
2255 }
2256
Jeff Sharkey6d515712012-09-20 16:06:08 -07002257 /**
Adam Cohen5d200642012-04-24 10:43:31 -07002258 * Create a new RemoteViews object that will inflate as the specified
2259 * landspace or portrait RemoteViews, depending on the current configuration.
2260 *
2261 * @param landscape The RemoteViews to inflate in landscape configuration
2262 * @param portrait The RemoteViews to inflate in portrait configuration
2263 */
2264 public RemoteViews(RemoteViews landscape, RemoteViews portrait) {
2265 if (landscape == null || portrait == null) {
2266 throw new RuntimeException("Both RemoteViews must be non-null");
2267 }
Sunny Goyaldd60f4d2017-10-18 15:22:42 -07002268 if (!landscape.hasSameAppInfo(portrait.mApplication)) {
Svet Ganov0da85b62014-08-06 14:11:37 -07002269 throw new RuntimeException("Both RemoteViews must share the same package and user");
Adam Cohen5d200642012-04-24 10:43:31 -07002270 }
Svet Ganov0da85b62014-08-06 14:11:37 -07002271 mApplication = portrait.mApplication;
Sunny Goyalc12d31c2018-11-12 16:29:18 -08002272 mLayoutId = portrait.mLayoutId;
2273 mLightBackgroundLayoutId = portrait.mLightBackgroundLayoutId;
Adam Cohen5d200642012-04-24 10:43:31 -07002274
2275 mLandscape = landscape;
2276 mPortrait = portrait;
2277
Adam Cohen5d200642012-04-24 10:43:31 -07002278 mBitmapCache = new BitmapCache();
2279 configureRemoteViewsAsChild(landscape);
2280 configureRemoteViewsAsChild(portrait);
Adrian Roosfb921842017-10-26 14:49:56 +02002281
2282 mClassCookies = (portrait.mClassCookies != null)
2283 ? portrait.mClassCookies : landscape.mClassCookies;
Sunny Goyal56333a82017-08-29 13:46:29 -07002284 }
Adam Cohen5d200642012-04-24 10:43:31 -07002285
Sunny Goyal56333a82017-08-29 13:46:29 -07002286 /**
2287 * Creates a copy of another RemoteViews.
2288 */
2289 public RemoteViews(RemoteViews src) {
2290 mBitmapCache = src.mBitmapCache;
2291 mApplication = src.mApplication;
2292 mIsRoot = src.mIsRoot;
2293 mLayoutId = src.mLayoutId;
Sunny Goyalc12d31c2018-11-12 16:29:18 -08002294 mLightBackgroundLayoutId = src.mLightBackgroundLayoutId;
2295 mApplyFlags = src.mApplyFlags;
Adrian Roosfb921842017-10-26 14:49:56 +02002296 mClassCookies = src.mClassCookies;
Sunny Goyal56333a82017-08-29 13:46:29 -07002297
2298 if (src.hasLandscapeAndPortraitLayouts()) {
2299 mLandscape = new RemoteViews(src.mLandscape);
2300 mPortrait = new RemoteViews(src.mPortrait);
Sunny Goyal56333a82017-08-29 13:46:29 -07002301 }
2302
2303 if (src.mActions != null) {
Sunny Goyal56333a82017-08-29 13:46:29 -07002304 Parcel p = Parcel.obtain();
Adrian Roosfb921842017-10-26 14:49:56 +02002305 p.putClassCookies(mClassCookies);
Sunny Goyal0ebf8812017-09-25 10:02:59 -07002306 src.writeActionsToParcel(p);
Sunny Goyal5b7689f2017-09-21 11:08:34 -07002307 p.setDataPosition(0);
2308 // Since src is already in memory, we do not care about stack overflow as it has
2309 // already been read once.
2310 readActionsFromParcel(p, 0);
Sunny Goyal56333a82017-08-29 13:46:29 -07002311 p.recycle();
2312 }
2313
2314 // Now that everything is initialized and duplicated, setting a new BitmapCache will
2315 // re-initialize the cache.
2316 setBitmapCache(new BitmapCache());
Adam Cohen5d200642012-04-24 10:43:31 -07002317 }
2318
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002319 /**
2320 * Reads a RemoteViews object from a parcel.
Jim Millere667a7a2012-08-09 19:22:32 -07002321 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002322 * @param parcel
2323 */
2324 public RemoteViews(Parcel parcel) {
Adrian Roosfb921842017-10-26 14:49:56 +02002325 this(parcel, null, null, 0, null);
Adam Cohen5d200642012-04-24 10:43:31 -07002326 }
Adam Cohenca6fd842010-09-03 18:10:35 -07002327
Adrian Roosfb921842017-10-26 14:49:56 +02002328 private RemoteViews(Parcel parcel, BitmapCache bitmapCache, ApplicationInfo info, int depth,
2329 Map<Class, Object> classCookies) {
Sunny Goyal692f8c92016-11-11 09:19:14 -08002330 if (depth > MAX_NESTED_VIEWS
2331 && (UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID)) {
2332 throw new IllegalArgumentException("Too many nested views.");
2333 }
2334 depth++;
2335
Adam Cohen5d200642012-04-24 10:43:31 -07002336 int mode = parcel.readInt();
2337
2338 // We only store a bitmap cache in the root of the RemoteViews.
2339 if (bitmapCache == null) {
2340 mBitmapCache = new BitmapCache(parcel);
Adrian Roosfb921842017-10-26 14:49:56 +02002341 // Store the class cookies such that they are available when we clone this RemoteView.
2342 mClassCookies = parcel.copyClassCookies();
Adam Cohen5d200642012-04-24 10:43:31 -07002343 } else {
2344 setBitmapCache(bitmapCache);
Adrian Roosfb921842017-10-26 14:49:56 +02002345 mClassCookies = classCookies;
Adam Cohen5d200642012-04-24 10:43:31 -07002346 setNotRoot();
2347 }
2348
2349 if (mode == MODE_NORMAL) {
Sunny Goyal5d8bcdf2016-10-27 16:11:29 -07002350 mApplication = parcel.readInt() == 0 ? info :
2351 ApplicationInfo.CREATOR.createFromParcel(parcel);
Adam Cohen5d200642012-04-24 10:43:31 -07002352 mLayoutId = parcel.readInt();
Sunny Goyalc12d31c2018-11-12 16:29:18 -08002353 mLightBackgroundLayoutId = parcel.readInt();
Adam Cohen5d200642012-04-24 10:43:31 -07002354
Sunny Goyal5b7689f2017-09-21 11:08:34 -07002355 readActionsFromParcel(parcel, depth);
Adam Cohen5d200642012-04-24 10:43:31 -07002356 } else {
2357 // MODE_HAS_LANDSCAPE_AND_PORTRAIT
Adrian Roosfb921842017-10-26 14:49:56 +02002358 mLandscape = new RemoteViews(parcel, mBitmapCache, info, depth, mClassCookies);
2359 mPortrait = new RemoteViews(parcel, mBitmapCache, mLandscape.mApplication, depth,
2360 mClassCookies);
Svet Ganov0da85b62014-08-06 14:11:37 -07002361 mApplication = mPortrait.mApplication;
Sunny Goyalc12d31c2018-11-12 16:29:18 -08002362 mLayoutId = mPortrait.mLayoutId;
2363 mLightBackgroundLayoutId = mPortrait.mLightBackgroundLayoutId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002364 }
Sunny Goyalc12d31c2018-11-12 16:29:18 -08002365 mApplyFlags = parcel.readInt();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002366 }
2367
Sunny Goyal5b7689f2017-09-21 11:08:34 -07002368 private void readActionsFromParcel(Parcel parcel, int depth) {
2369 int count = parcel.readInt();
2370 if (count > 0) {
2371 mActions = new ArrayList<>(count);
2372 for (int i = 0; i < count; i++) {
2373 mActions.add(getActionFromParcel(parcel, depth));
2374 }
2375 }
2376 }
2377
Sunny Goyal56333a82017-08-29 13:46:29 -07002378 private Action getActionFromParcel(Parcel parcel, int depth) {
2379 int tag = parcel.readInt();
2380 switch (tag) {
Sunny Goyal43c97042018-08-23 15:21:26 -07002381 case SET_ON_CLICK_RESPONSE_TAG:
2382 return new SetOnClickResponse(parcel);
Sunny Goyal5b153922017-09-21 21:00:36 -07002383 case SET_DRAWABLE_TINT_TAG:
2384 return new SetDrawableTint(parcel);
Sunny Goyal56333a82017-08-29 13:46:29 -07002385 case REFLECTION_ACTION_TAG:
2386 return new ReflectionAction(parcel);
2387 case VIEW_GROUP_ACTION_ADD_TAG:
Adrian Roosfb921842017-10-26 14:49:56 +02002388 return new ViewGroupActionAdd(parcel, mBitmapCache, mApplication, depth,
2389 mClassCookies);
Sunny Goyal56333a82017-08-29 13:46:29 -07002390 case VIEW_GROUP_ACTION_REMOVE_TAG:
2391 return new ViewGroupActionRemove(parcel);
Sunny Goyal5b153922017-09-21 21:00:36 -07002392 case VIEW_CONTENT_NAVIGATION_TAG:
2393 return new ViewContentNavigation(parcel);
Sunny Goyal56333a82017-08-29 13:46:29 -07002394 case SET_EMPTY_VIEW_ACTION_TAG:
2395 return new SetEmptyView(parcel);
2396 case SET_PENDING_INTENT_TEMPLATE_TAG:
2397 return new SetPendingIntentTemplate(parcel);
Sunny Goyal56333a82017-08-29 13:46:29 -07002398 case SET_REMOTE_VIEW_ADAPTER_INTENT_TAG:
2399 return new SetRemoteViewsAdapterIntent(parcel);
2400 case TEXT_VIEW_DRAWABLE_ACTION_TAG:
2401 return new TextViewDrawableAction(parcel);
2402 case TEXT_VIEW_SIZE_ACTION_TAG:
2403 return new TextViewSizeAction(parcel);
2404 case VIEW_PADDING_ACTION_TAG:
2405 return new ViewPaddingAction(parcel);
2406 case BITMAP_REFLECTION_ACTION_TAG:
2407 return new BitmapReflectionAction(parcel);
2408 case SET_REMOTE_VIEW_ADAPTER_LIST_TAG:
2409 return new SetRemoteViewsAdapterList(parcel);
Sunny Goyal56333a82017-08-29 13:46:29 -07002410 case SET_REMOTE_INPUTS_ACTION_TAG:
2411 return new SetRemoteInputsAction(parcel);
2412 case LAYOUT_PARAM_ACTION_TAG:
2413 return new LayoutParamAction(parcel);
2414 case OVERRIDE_TEXT_COLORS_TAG:
2415 return new OverrideTextColorsAction(parcel);
Gus Prevas9cc96602018-10-11 11:24:23 -04002416 case SET_RIPPLE_DRAWABLE_COLOR_TAG:
2417 return new SetRippleDrawableColor(parcel);
Tony Mak7d4b3a52018-11-27 17:29:36 +00002418 case SET_INT_TAG_TAG:
2419 return new SetIntTagAction(parcel);
Sunny Goyal56333a82017-08-29 13:46:29 -07002420 default:
2421 throw new ActionException("Tag " + tag + " not found");
2422 }
2423 };
2424
Alan Viverette0a14ba52017-06-14 16:54:05 -04002425 /**
2426 * Returns a deep copy of the RemoteViews object. The RemoteView may not be
2427 * attached to another RemoteView -- it must be the root of a hierarchy.
2428 *
Sunny Goyal56333a82017-08-29 13:46:29 -07002429 * @deprecated use {@link #RemoteViews(RemoteViews)} instead.
Alan Viverette0a14ba52017-06-14 16:54:05 -04002430 * @throws IllegalStateException if this is not the root of a RemoteView
2431 * hierarchy
2432 */
2433 @Override
Sunny Goyal56333a82017-08-29 13:46:29 -07002434 @Deprecated
Alan Viverette0a14ba52017-06-14 16:54:05 -04002435 public RemoteViews clone() {
Sunny Goyal56333a82017-08-29 13:46:29 -07002436 Preconditions.checkState(mIsRoot, "RemoteView has been attached to another RemoteView. "
2437 + "May only clone the root of a RemoteView hierarchy.");
Winson Chung3ec9a452010-09-23 16:40:28 -07002438
Sunny Goyal56333a82017-08-29 13:46:29 -07002439 return new RemoteViews(this);
Joe Onorato18e69df2010-05-17 22:26:12 -07002440 }
2441
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002442 public String getPackage() {
Svetoslavb6242442014-09-19 13:21:55 -07002443 return (mApplication != null) ? mApplication.packageName : null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002444 }
2445
Adam Cohen5d200642012-04-24 10:43:31 -07002446 /**
Adrian Roos7da889d2016-03-16 18:38:58 -07002447 * Returns the layout id of the root layout associated with this RemoteViews. In the case
Adam Cohen5d200642012-04-24 10:43:31 -07002448 * that the RemoteViews has both a landscape and portrait root, this will return the layout
2449 * id associated with the portrait layout.
2450 *
2451 * @return the layout id.
2452 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002453 public int getLayoutId() {
Sunny Goyalc12d31c2018-11-12 16:29:18 -08002454 return hasFlags(FLAG_USE_LIGHT_BACKGROUND_LAYOUT) && (mLightBackgroundLayoutId != 0)
2455 ? mLightBackgroundLayoutId : mLayoutId;
Adam Cohenca6fd842010-09-03 18:10:35 -07002456 }
2457
2458 /**
Adam Cohen5d200642012-04-24 10:43:31 -07002459 * Recursively sets BitmapCache in the hierarchy and update the bitmap ids.
2460 */
2461 private void setBitmapCache(BitmapCache bitmapCache) {
2462 mBitmapCache = bitmapCache;
2463 if (!hasLandscapeAndPortraitLayouts()) {
2464 if (mActions != null) {
2465 final int count = mActions.size();
2466 for (int i= 0; i < count; ++i) {
2467 mActions.get(i).setBitmapCache(bitmapCache);
2468 }
2469 }
2470 } else {
2471 mLandscape.setBitmapCache(bitmapCache);
2472 mPortrait.setBitmapCache(bitmapCache);
Winson Chung3ec9a452010-09-23 16:40:28 -07002473 }
2474 }
2475
2476 /**
2477 * Returns an estimate of the bitmap heap memory usage for this RemoteViews.
2478 */
Adam Cohen311c79c2012-05-10 14:44:38 -07002479 /** @hide */
Mathew Inwood3a75f262019-06-27 12:47:38 +01002480 @UnsupportedAppUsage
Adam Cohen311c79c2012-05-10 14:44:38 -07002481 public int estimateMemoryUsage() {
Sunny Goyal56333a82017-08-29 13:46:29 -07002482 return mBitmapCache.getBitmapMemory();
Winson Chung3ec9a452010-09-23 16:40:28 -07002483 }
2484
2485 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002486 * Add an action to be executed on the remote side when apply is called.
Jim Millere667a7a2012-08-09 19:22:32 -07002487 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002488 * @param a The action to add
2489 */
2490 private void addAction(Action a) {
Adam Cohen5d200642012-04-24 10:43:31 -07002491 if (hasLandscapeAndPortraitLayouts()) {
2492 throw new RuntimeException("RemoteViews specifying separate landscape and portrait" +
2493 " layouts cannot be modified. Instead, fully configure the landscape and" +
2494 " portrait layouts individually before constructing the combined layout.");
2495 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002496 if (mActions == null) {
Sunny Goyal56333a82017-08-29 13:46:29 -07002497 mActions = new ArrayList<>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002498 }
2499 mActions.add(a);
2500 }
Jeff Sharkey1162fd72009-11-04 17:58:08 -08002501
2502 /**
2503 * Equivalent to calling {@link ViewGroup#addView(View)} after inflating the
2504 * given {@link RemoteViews}. This allows users to build "nested"
2505 * {@link RemoteViews}. In cases where consumers of {@link RemoteViews} may
2506 * recycle layouts, use {@link #removeAllViews(int)} to clear any existing
2507 * children.
2508 *
2509 * @param viewId The id of the parent {@link ViewGroup} to add child into.
2510 * @param nestedView {@link RemoteViews} that describes the child.
2511 */
2512 public void addView(int viewId, RemoteViews nestedView) {
Anthony Chen8f5f3582017-04-11 11:18:37 -07002513 addAction(nestedView == null
2514 ? new ViewGroupActionRemove(viewId)
2515 : new ViewGroupActionAdd(viewId, nestedView));
2516 }
2517
2518 /**
2519 * Equivalent to calling {@link ViewGroup#addView(View, int)} after inflating the
2520 * given {@link RemoteViews}.
2521 *
2522 * @param viewId The id of the parent {@link ViewGroup} to add the child into.
Sunny Goyal271e3222017-08-29 16:05:47 -07002523 * @param nestedView {@link RemoteViews} of the child to add.
Anthony Chen8f5f3582017-04-11 11:18:37 -07002524 * @param index The position at which to add the child.
2525 *
2526 * @hide
2527 */
Mathew Inwood3a75f262019-06-27 12:47:38 +01002528 @UnsupportedAppUsage
Anthony Chen8f5f3582017-04-11 11:18:37 -07002529 public void addView(int viewId, RemoteViews nestedView, int index) {
2530 addAction(new ViewGroupActionAdd(viewId, nestedView, index));
Jeff Sharkey1162fd72009-11-04 17:58:08 -08002531 }
2532
2533 /**
2534 * Equivalent to calling {@link ViewGroup#removeAllViews()}.
2535 *
2536 * @param viewId The id of the parent {@link ViewGroup} to remove all
2537 * children from.
2538 */
2539 public void removeAllViews(int viewId) {
Anthony Chen8f5f3582017-04-11 11:18:37 -07002540 addAction(new ViewGroupActionRemove(viewId));
2541 }
2542
2543 /**
2544 * Removes all views in the {@link ViewGroup} specified by the {@code viewId} except for any
2545 * child that has the {@code viewIdToKeep} as its id.
2546 *
2547 * @param viewId The id of the parent {@link ViewGroup} to remove children from.
2548 * @param viewIdToKeep The id of a child that should not be removed.
2549 *
2550 * @hide
2551 */
2552 public void removeAllViewsExceptId(int viewId, int viewIdToKeep) {
2553 addAction(new ViewGroupActionRemove(viewId, viewIdToKeep));
Jeff Sharkey1162fd72009-11-04 17:58:08 -08002554 }
2555
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002556 /**
Adam Cohen0b96a572011-02-10 15:56:16 -08002557 * Equivalent to calling {@link AdapterViewAnimator#showNext()}
Adam Cohen2dd21972010-08-15 18:20:04 -07002558 *
Adam Cohen0b96a572011-02-10 15:56:16 -08002559 * @param viewId The id of the view on which to call {@link AdapterViewAnimator#showNext()}
Adam Cohen2dd21972010-08-15 18:20:04 -07002560 */
2561 public void showNext(int viewId) {
Sunny Goyal5b153922017-09-21 21:00:36 -07002562 addAction(new ViewContentNavigation(viewId, true /* next */));
Adam Cohen2dd21972010-08-15 18:20:04 -07002563 }
2564
2565 /**
Adam Cohen0b96a572011-02-10 15:56:16 -08002566 * Equivalent to calling {@link AdapterViewAnimator#showPrevious()}
Adam Cohen2dd21972010-08-15 18:20:04 -07002567 *
Adam Cohen0b96a572011-02-10 15:56:16 -08002568 * @param viewId The id of the view on which to call {@link AdapterViewAnimator#showPrevious()}
Adam Cohen2dd21972010-08-15 18:20:04 -07002569 */
2570 public void showPrevious(int viewId) {
Sunny Goyal5b153922017-09-21 21:00:36 -07002571 addAction(new ViewContentNavigation(viewId, false /* next */));
Adam Cohen2dd21972010-08-15 18:20:04 -07002572 }
2573
2574 /**
Adam Cohen0b96a572011-02-10 15:56:16 -08002575 * Equivalent to calling {@link AdapterViewAnimator#setDisplayedChild(int)}
2576 *
2577 * @param viewId The id of the view on which to call
2578 * {@link AdapterViewAnimator#setDisplayedChild(int)}
2579 */
2580 public void setDisplayedChild(int viewId, int childIndex) {
2581 setInt(viewId, "setDisplayedChild", childIndex);
2582 }
2583
2584 /**
Kirill Grouchnikov40a39782016-07-01 17:24:32 -04002585 * Equivalent to calling {@link View#setVisibility(int)}
Jim Millere667a7a2012-08-09 19:22:32 -07002586 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002587 * @param viewId The id of the view whose visibility should change
2588 * @param visibility The new visibility for the view
2589 */
2590 public void setViewVisibility(int viewId, int visibility) {
2591 setInt(viewId, "setVisibility", visibility);
2592 }
Adam Cohenca6fd842010-09-03 18:10:35 -07002593
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002594 /**
Kirill Grouchnikov40a39782016-07-01 17:24:32 -04002595 * Equivalent to calling {@link TextView#setText(CharSequence)}
Jim Millere667a7a2012-08-09 19:22:32 -07002596 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002597 * @param viewId The id of the view whose text should change
2598 * @param text The new text for the view
2599 */
2600 public void setTextViewText(int viewId, CharSequence text) {
2601 setCharSequence(viewId, "setText", text);
2602 }
Daniel Sandler7264f712012-05-21 14:48:23 -04002603
2604 /**
Daniel Sandler7264f712012-05-21 14:48:23 -04002605 * Equivalent to calling {@link TextView#setTextSize(int, float)}
Jim Millere667a7a2012-08-09 19:22:32 -07002606 *
Daniel Sandler7264f712012-05-21 14:48:23 -04002607 * @param viewId The id of the view whose text size should change
2608 * @param units The units of size (e.g. COMPLEX_UNIT_SP)
2609 * @param size The size of the text
2610 */
2611 public void setTextViewTextSize(int viewId, int units, float size) {
2612 addAction(new TextViewSizeAction(viewId, units, size));
2613 }
2614
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002615 /**
Jim Millere667a7a2012-08-09 19:22:32 -07002616 * Equivalent to calling
Daniel Sandler820ba322012-03-23 16:36:00 -05002617 * {@link TextView#setCompoundDrawablesWithIntrinsicBounds(int, int, int, int)}.
2618 *
2619 * @param viewId The id of the view whose text should change
2620 * @param left The id of a drawable to place to the left of the text, or 0
2621 * @param top The id of a drawable to place above the text, or 0
2622 * @param right The id of a drawable to place to the right of the text, or 0
Jim Millere667a7a2012-08-09 19:22:32 -07002623 * @param bottom The id of a drawable to place below the text, or 0
Daniel Sandler820ba322012-03-23 16:36:00 -05002624 */
2625 public void setTextViewCompoundDrawables(int viewId, int left, int top, int right, int bottom) {
2626 addAction(new TextViewDrawableAction(viewId, false, left, top, right, bottom));
2627 }
2628
2629 /**
Jim Millere667a7a2012-08-09 19:22:32 -07002630 * Equivalent to calling {@link
Daniel Sandler820ba322012-03-23 16:36:00 -05002631 * TextView#setCompoundDrawablesRelativeWithIntrinsicBounds(int, int, int, int)}.
2632 *
2633 * @param viewId The id of the view whose text should change
Jim Millere667a7a2012-08-09 19:22:32 -07002634 * @param start The id of a drawable to place before the text (relative to the
Daniel Sandler820ba322012-03-23 16:36:00 -05002635 * layout direction), or 0
2636 * @param top The id of a drawable to place above the text, or 0
2637 * @param end The id of a drawable to place after the text, or 0
Fabrice Di Meglio66388dc2012-05-03 18:51:57 -07002638 * @param bottom The id of a drawable to place below the text, or 0
Daniel Sandler820ba322012-03-23 16:36:00 -05002639 */
2640 public void setTextViewCompoundDrawablesRelative(int viewId, int start, int top, int end, int bottom) {
2641 addAction(new TextViewDrawableAction(viewId, true, start, top, end, bottom));
2642 }
2643
2644 /**
Dan Sandler912282e2015-07-28 22:49:30 -04002645 * Equivalent to calling {@link
2646 * TextView#setCompoundDrawablesWithIntrinsicBounds(Drawable, Drawable, Drawable, Drawable)}
2647 * using the drawables yielded by {@link Icon#loadDrawable(Context)}.
2648 *
2649 * @param viewId The id of the view whose text should change
2650 * @param left an Icon to place to the left of the text, or 0
2651 * @param top an Icon to place above the text, or 0
2652 * @param right an Icon to place to the right of the text, or 0
2653 * @param bottom an Icon to place below the text, or 0
2654 *
2655 * @hide
2656 */
2657 public void setTextViewCompoundDrawables(int viewId, Icon left, Icon top, Icon right, Icon bottom) {
2658 addAction(new TextViewDrawableAction(viewId, false, left, top, right, bottom));
2659 }
2660
2661 /**
2662 * Equivalent to calling {@link
2663 * TextView#setCompoundDrawablesRelativeWithIntrinsicBounds(Drawable, Drawable, Drawable, Drawable)}
2664 * using the drawables yielded by {@link Icon#loadDrawable(Context)}.
2665 *
2666 * @param viewId The id of the view whose text should change
2667 * @param start an Icon to place before the text (relative to the
2668 * layout direction), or 0
2669 * @param top an Icon to place above the text, or 0
2670 * @param end an Icon to place after the text, or 0
2671 * @param bottom an Icon to place below the text, or 0
2672 *
2673 * @hide
2674 */
2675 public void setTextViewCompoundDrawablesRelative(int viewId, Icon start, Icon top, Icon end, Icon bottom) {
2676 addAction(new TextViewDrawableAction(viewId, true, start, top, end, bottom));
2677 }
2678
2679 /**
Kirill Grouchnikov40a39782016-07-01 17:24:32 -04002680 * Equivalent to calling {@link ImageView#setImageResource(int)}
Jim Millere667a7a2012-08-09 19:22:32 -07002681 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002682 * @param viewId The id of the view whose drawable should change
2683 * @param srcId The new resource id for the drawable
2684 */
Jim Millere667a7a2012-08-09 19:22:32 -07002685 public void setImageViewResource(int viewId, int srcId) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002686 setInt(viewId, "setImageResource", srcId);
2687 }
2688
2689 /**
Kirill Grouchnikov40a39782016-07-01 17:24:32 -04002690 * Equivalent to calling {@link ImageView#setImageURI(Uri)}
Jim Millere667a7a2012-08-09 19:22:32 -07002691 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002692 * @param viewId The id of the view whose drawable should change
2693 * @param uri The Uri for the image
2694 */
2695 public void setImageViewUri(int viewId, Uri uri) {
2696 setUri(viewId, "setImageURI", uri);
2697 }
2698
2699 /**
Kirill Grouchnikov40a39782016-07-01 17:24:32 -04002700 * Equivalent to calling {@link ImageView#setImageBitmap(Bitmap)}
Jim Millere667a7a2012-08-09 19:22:32 -07002701 *
Scott Main93dc6422012-02-24 12:04:06 -08002702 * @param viewId The id of the view whose bitmap should change
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002703 * @param bitmap The new Bitmap for the drawable
2704 */
2705 public void setImageViewBitmap(int viewId, Bitmap bitmap) {
2706 setBitmap(viewId, "setImageBitmap", bitmap);
2707 }
2708
2709 /**
Kirill Grouchnikov40a39782016-07-01 17:24:32 -04002710 * Equivalent to calling {@link ImageView#setImageIcon(Icon)}
Dan Sandlera22a3802015-05-13 00:12:47 -04002711 *
2712 * @param viewId The id of the view whose bitmap should change
2713 * @param icon The new Icon for the ImageView
2714 */
2715 public void setImageViewIcon(int viewId, Icon icon) {
2716 setIcon(viewId, "setImageIcon", icon);
2717 }
2718
2719 /**
Kirill Grouchnikov40a39782016-07-01 17:24:32 -04002720 * Equivalent to calling {@link AdapterView#setEmptyView(View)}
Adam Cohen1480fdd2010-08-25 17:24:53 -07002721 *
2722 * @param viewId The id of the view on which to set the empty view
2723 * @param emptyViewId The view id of the empty view
2724 */
2725 public void setEmptyView(int viewId, int emptyViewId) {
2726 addAction(new SetEmptyView(viewId, emptyViewId));
2727 }
2728
2729 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002730 * Equivalent to calling {@link Chronometer#setBase Chronometer.setBase},
2731 * {@link Chronometer#setFormat Chronometer.setFormat},
2732 * and {@link Chronometer#start Chronometer.start()} or
2733 * {@link Chronometer#stop Chronometer.stop()}.
Jim Millere667a7a2012-08-09 19:22:32 -07002734 *
Scott Main93dc6422012-02-24 12:04:06 -08002735 * @param viewId The id of the {@link Chronometer} to change
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002736 * @param base The time at which the timer would have read 0:00. This
2737 * time should be based off of
2738 * {@link android.os.SystemClock#elapsedRealtime SystemClock.elapsedRealtime()}.
2739 * @param format The Chronometer format string, or null to
2740 * simply display the timer value.
2741 * @param started True if you want the clock to be started, false if not.
Selim Cinek81c23aa2016-02-25 16:23:13 -08002742 *
Selim Cinekc3b752e2016-04-20 16:13:59 -07002743 * @see #setChronometerCountDown(int, boolean)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002744 */
2745 public void setChronometer(int viewId, long base, String format, boolean started) {
2746 setLong(viewId, "setBase", base);
2747 setString(viewId, "setFormat", format);
2748 setBoolean(viewId, "setStarted", started);
2749 }
Jim Millere667a7a2012-08-09 19:22:32 -07002750
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002751 /**
Selim Cinek81c23aa2016-02-25 16:23:13 -08002752 * Equivalent to calling {@link Chronometer#setCountDown(boolean) Chronometer.setCountDown} on
2753 * the chronometer with the given viewId.
2754 *
2755 * @param viewId The id of the {@link Chronometer} to change
2756 * @param isCountDown True if you want the chronometer to count down to base instead of
2757 * counting up.
2758 */
Selim Cinekc3b752e2016-04-20 16:13:59 -07002759 public void setChronometerCountDown(int viewId, boolean isCountDown) {
Selim Cinek81c23aa2016-02-25 16:23:13 -08002760 setBoolean(viewId, "setCountDown", isCountDown);
2761 }
2762
2763 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002764 * Equivalent to calling {@link ProgressBar#setMax ProgressBar.setMax},
2765 * {@link ProgressBar#setProgress ProgressBar.setProgress}, and
2766 * {@link ProgressBar#setIndeterminate ProgressBar.setIndeterminate}
2767 *
2768 * If indeterminate is true, then the values for max and progress are ignored.
Jim Millere667a7a2012-08-09 19:22:32 -07002769 *
Scott Main93dc6422012-02-24 12:04:06 -08002770 * @param viewId The id of the {@link ProgressBar} to change
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002771 * @param max The 100% value for the progress bar
2772 * @param progress The current value of the progress bar.
Jim Millere667a7a2012-08-09 19:22:32 -07002773 * @param indeterminate True if the progress bar is indeterminate,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002774 * false if not.
2775 */
Jim Millere667a7a2012-08-09 19:22:32 -07002776 public void setProgressBar(int viewId, int max, int progress,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002777 boolean indeterminate) {
2778 setBoolean(viewId, "setIndeterminate", indeterminate);
2779 if (!indeterminate) {
2780 setInt(viewId, "setMax", max);
2781 setInt(viewId, "setProgress", progress);
2782 }
2783 }
Jim Millere667a7a2012-08-09 19:22:32 -07002784
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002785 /**
2786 * Equivalent to calling
2787 * {@link android.view.View#setOnClickListener(android.view.View.OnClickListener)}
Sunny Goyald7d0c692017-10-12 12:05:14 -07002788 * to launch the provided {@link PendingIntent}. The source bounds
2789 * ({@link Intent#getSourceBounds()}) of the intent will be set to the bounds of the clicked
2790 * view in screen space.
Sunny Goyal43c97042018-08-23 15:21:26 -07002791 * Note that any activity options associated with the mPendingIntent may get overridden
Sunny Goyald7d0c692017-10-12 12:05:14 -07002792 * before starting the intent.
Jim Millere667a7a2012-08-09 19:22:32 -07002793 *
Adam Cohen35ae9ca2010-09-20 15:20:41 -07002794 * When setting the on-click action of items within collections (eg. {@link ListView},
2795 * {@link StackView} etc.), this method will not work. Instead, use {@link
Kirill Grouchnikovc5b30102016-06-28 15:34:13 -04002796 * RemoteViews#setPendingIntentTemplate(int, PendingIntent)} in conjunction with
2797 * {@link RemoteViews#setOnClickFillInIntent(int, Intent)}.
Adam Cohen35ae9ca2010-09-20 15:20:41 -07002798 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002799 * @param viewId The id of the view that will trigger the {@link PendingIntent} when clicked
2800 * @param pendingIntent The {@link PendingIntent} to send when user clicks
2801 */
2802 public void setOnClickPendingIntent(int viewId, PendingIntent pendingIntent) {
Sunny Goyal43c97042018-08-23 15:21:26 -07002803 setOnClickResponse(viewId, RemoteResponse.fromPendingIntent(pendingIntent));
2804 }
2805
2806 /**
2807 * Equivalent of calling
2808 * {@link android.view.View#setOnClickListener(android.view.View.OnClickListener)}
2809 * to launch the provided {@link RemoteResponse}.
2810 *
2811 * @param viewId The id of the view that will trigger the {@link RemoteResponse} when clicked
2812 * @param response The {@link RemoteResponse} to send when user clicks
2813 */
Sunny Goyal40635d72019-03-01 11:46:44 -08002814 public void setOnClickResponse(int viewId, @NonNull RemoteResponse response) {
Sunny Goyal43c97042018-08-23 15:21:26 -07002815 addAction(new SetOnClickResponse(viewId, response));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002816 }
2817
2818 /**
Adam Cohen35ae9ca2010-09-20 15:20:41 -07002819 * When using collections (eg. {@link ListView}, {@link StackView} etc.) in widgets, it is very
2820 * costly to set PendingIntents on the individual items, and is hence not permitted. Instead
2821 * this method should be used to set a single PendingIntent template on the collection, and
2822 * individual items can differentiate their on-click behavior using
2823 * {@link RemoteViews#setOnClickFillInIntent(int, Intent)}.
Adam Cohenca6fd842010-09-03 18:10:35 -07002824 *
2825 * @param viewId The id of the collection who's children will use this PendingIntent template
2826 * when clicked
2827 * @param pendingIntentTemplate The {@link PendingIntent} to be combined with extras specified
2828 * by a child of viewId and executed when that child is clicked
2829 */
2830 public void setPendingIntentTemplate(int viewId, PendingIntent pendingIntentTemplate) {
2831 addAction(new SetPendingIntentTemplate(viewId, pendingIntentTemplate));
2832 }
2833
2834 /**
Adam Cohen35ae9ca2010-09-20 15:20:41 -07002835 * When using collections (eg. {@link ListView}, {@link StackView} etc.) in widgets, it is very
2836 * costly to set PendingIntents on the individual items, and is hence not permitted. Instead
2837 * a single PendingIntent template can be set on the collection, see {@link
2838 * RemoteViews#setPendingIntentTemplate(int, PendingIntent)}, and the individual on-click
2839 * action of a given item can be distinguished by setting a fillInIntent on that item. The
2840 * fillInIntent is then combined with the PendingIntent template in order to determine the final
2841 * intent which will be executed when the item is clicked. This works as follows: any fields
2842 * which are left blank in the PendingIntent template, but are provided by the fillInIntent
Kirill Grouchnikovc5b30102016-06-28 15:34:13 -04002843 * will be overwritten, and the resulting PendingIntent will be used. The rest
Adam Cohen35ae9ca2010-09-20 15:20:41 -07002844 * of the PendingIntent template will then be filled in with the associated fields that are
2845 * set in fillInIntent. See {@link Intent#fillIn(Intent, int)} for more details.
2846 *
2847 * @param viewId The id of the view on which to set the fillInIntent
2848 * @param fillInIntent The intent which will be combined with the parent's PendingIntent
2849 * in order to determine the on-click behavior of the view specified by viewId
2850 */
2851 public void setOnClickFillInIntent(int viewId, Intent fillInIntent) {
Sunny Goyal43c97042018-08-23 15:21:26 -07002852 setOnClickResponse(viewId, RemoteResponse.fromFillInIntent(fillInIntent));
Adam Cohen35ae9ca2010-09-20 15:20:41 -07002853 }
2854
2855 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002856 * @hide
Sunny Goyal5b153922017-09-21 21:00:36 -07002857 * Equivalent to calling
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002858 * {@link Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)},
Sunny Goyal5b153922017-09-21 21:00:36 -07002859 * on the {@link Drawable} of a given view.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002860 * <p>
Jim Millere667a7a2012-08-09 19:22:32 -07002861 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002862 * @param viewId The id of the view that contains the target
2863 * {@link Drawable}
2864 * @param targetBackground If true, apply these parameters to the
2865 * {@link Drawable} returned by
2866 * {@link android.view.View#getBackground()}. Otherwise, assume
2867 * the target view is an {@link ImageView} and apply them to
2868 * {@link ImageView#getDrawable()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002869 * @param colorFilter Specify a color for a
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02002870 * {@link android.graphics.ColorFilter} for this drawable. This will be ignored if
2871 * {@code mode} is {@code null}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002872 * @param mode Specify a PorterDuff mode for this drawable, or null to leave
2873 * unchanged.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002874 */
Sunny Goyal5b153922017-09-21 21:00:36 -07002875 public void setDrawableTint(int viewId, boolean targetBackground,
2876 int colorFilter, @NonNull PorterDuff.Mode mode) {
2877 addAction(new SetDrawableTint(viewId, targetBackground, colorFilter, mode));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002878 }
2879
2880 /**
Jorim Jaggief72a192014-08-26 21:57:46 +02002881 * @hide
Gus Prevas9cc96602018-10-11 11:24:23 -04002882 * Equivalent to calling
2883 * {@link RippleDrawable#setColor(ColorStateList)} on the {@link Drawable} of a given view,
2884 * assuming it's a {@link RippleDrawable}.
2885 * <p>
2886 *
2887 * @param viewId The id of the view that contains the target
2888 * {@link RippleDrawable}
2889 * @param colorStateList Specify a color for a
2890 * {@link ColorStateList} for this drawable.
2891 */
2892 public void setRippleDrawableColor(int viewId, ColorStateList colorStateList) {
2893 addAction(new SetRippleDrawableColor(viewId, colorStateList));
2894 }
2895
2896 /**
2897 * @hide
Jorim Jaggief72a192014-08-26 21:57:46 +02002898 * Equivalent to calling {@link android.widget.ProgressBar#setProgressTintList}.
2899 *
2900 * @param viewId The id of the view whose tint should change
2901 * @param tint the tint to apply, may be {@code null} to clear tint
2902 */
2903 public void setProgressTintList(int viewId, ColorStateList tint) {
2904 addAction(new ReflectionAction(viewId, "setProgressTintList",
2905 ReflectionAction.COLOR_STATE_LIST, tint));
2906 }
2907
2908 /**
2909 * @hide
2910 * Equivalent to calling {@link android.widget.ProgressBar#setProgressBackgroundTintList}.
2911 *
2912 * @param viewId The id of the view whose tint should change
2913 * @param tint the tint to apply, may be {@code null} to clear tint
2914 */
2915 public void setProgressBackgroundTintList(int viewId, ColorStateList tint) {
2916 addAction(new ReflectionAction(viewId, "setProgressBackgroundTintList",
2917 ReflectionAction.COLOR_STATE_LIST, tint));
2918 }
2919
2920 /**
2921 * @hide
2922 * Equivalent to calling {@link android.widget.ProgressBar#setIndeterminateTintList}.
2923 *
2924 * @param viewId The id of the view whose tint should change
2925 * @param tint the tint to apply, may be {@code null} to clear tint
2926 */
2927 public void setProgressIndeterminateTintList(int viewId, ColorStateList tint) {
2928 addAction(new ReflectionAction(viewId, "setIndeterminateTintList",
2929 ReflectionAction.COLOR_STATE_LIST, tint));
2930 }
2931
2932 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002933 * Equivalent to calling {@link android.widget.TextView#setTextColor(int)}.
Jim Millere667a7a2012-08-09 19:22:32 -07002934 *
Scott Main93dc6422012-02-24 12:04:06 -08002935 * @param viewId The id of the view whose text color should change
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002936 * @param color Sets the text color for all the states (normal, selected,
2937 * focused) to be this color.
2938 */
Tor Norbye80756e32015-03-02 09:39:27 -08002939 public void setTextColor(int viewId, @ColorInt int color) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002940 setInt(viewId, "setTextColor", color);
2941 }
2942
Joe Onorato592d0652009-03-24 22:25:52 -07002943 /**
Selim Cinek981962e2016-07-20 20:41:58 -07002944 * @hide
2945 * Equivalent to calling {@link android.widget.TextView#setTextColor(ColorStateList)}.
2946 *
2947 * @param viewId The id of the view whose text color should change
2948 * @param colors the text colors to set
2949 */
2950 public void setTextColor(int viewId, @ColorInt ColorStateList colors) {
2951 addAction(new ReflectionAction(viewId, "setTextColor", ReflectionAction.COLOR_STATE_LIST,
2952 colors));
2953 }
2954
2955 /**
Adam Cohen3b4ca102012-12-14 12:00:41 -08002956 * Equivalent to calling {@link android.widget.AbsListView#setRemoteViewsAdapter(Intent)}.
Winson Chung499cb9f2010-07-16 11:18:17 -07002957 *
Winson Chung037300b2011-03-29 15:40:16 -07002958 * @param appWidgetId The id of the app widget which contains the specified view. (This
2959 * parameter is ignored in this deprecated method)
Adam Cohen50f3d1b2012-12-11 18:36:07 -08002960 * @param viewId The id of the {@link AdapterView}
Winson Chung037300b2011-03-29 15:40:16 -07002961 * @param intent The intent of the service which will be
2962 * providing data to the RemoteViewsAdapter
2963 * @deprecated This method has been deprecated. See
2964 * {@link android.widget.RemoteViews#setRemoteAdapter(int, Intent)}
2965 */
2966 @Deprecated
2967 public void setRemoteAdapter(int appWidgetId, int viewId, Intent intent) {
2968 setRemoteAdapter(viewId, intent);
2969 }
2970
2971 /**
2972 * Equivalent to calling {@link android.widget.AbsListView#setRemoteViewsAdapter(Intent)}.
2973 * Can only be used for App Widgets.
2974 *
Adam Cohen50f3d1b2012-12-11 18:36:07 -08002975 * @param viewId The id of the {@link AdapterView}
Winson Chung81f39eb2011-01-11 18:05:01 -08002976 * @param intent The intent of the service which will be
2977 * providing data to the RemoteViewsAdapter
2978 */
Winson Chung037300b2011-03-29 15:40:16 -07002979 public void setRemoteAdapter(int viewId, Intent intent) {
2980 addAction(new SetRemoteViewsAdapterIntent(viewId, intent));
Winson Chung499cb9f2010-07-16 11:18:17 -07002981 }
2982
2983 /**
Adam Cohen50f3d1b2012-12-11 18:36:07 -08002984 * Creates a simple Adapter for the viewId specified. The viewId must point to an AdapterView,
2985 * ie. {@link ListView}, {@link GridView}, {@link StackView} or {@link AdapterViewAnimator}.
2986 * This is a simpler but less flexible approach to populating collection widgets. Its use is
2987 * encouraged for most scenarios, as long as the total memory within the list of RemoteViews
2988 * is relatively small (ie. doesn't contain large or numerous Bitmaps, see {@link
2989 * RemoteViews#setImageViewBitmap}). In the case of numerous images, the use of API is still
2990 * possible by setting image URIs instead of Bitmaps, see {@link RemoteViews#setImageViewUri}.
2991 *
2992 * This API is supported in the compatibility library for previous API levels, see
2993 * RemoteViewsCompat.
2994 *
2995 * @param viewId The id of the {@link AdapterView}
2996 * @param list The list of RemoteViews which will populate the view specified by viewId.
Adam Cohenb00d9f02013-01-10 14:12:52 -08002997 * @param viewTypeCount The maximum number of unique layout id's used to construct the list of
2998 * RemoteViews. This count cannot change during the life-cycle of a given widget, so this
2999 * parameter should account for the maximum possible number of types that may appear in the
3000 * See {@link Adapter#getViewTypeCount()}.
Adam Cohen33f3aab2013-04-17 13:48:17 -07003001 *
3002 * @hide
Adam Powellf4d604f2019-02-04 15:28:42 -08003003 * @deprecated this appears to have no users outside of UnsupportedAppUsage?
Adam Cohen50f3d1b2012-12-11 18:36:07 -08003004 */
Mathew Inwood3a75f262019-06-27 12:47:38 +01003005 @UnsupportedAppUsage
3006 @Deprecated
Adam Cohenb00d9f02013-01-10 14:12:52 -08003007 public void setRemoteAdapter(int viewId, ArrayList<RemoteViews> list, int viewTypeCount) {
3008 addAction(new SetRemoteViewsAdapterList(viewId, list, viewTypeCount));
Adam Cohen50f3d1b2012-12-11 18:36:07 -08003009 }
3010
3011 /**
Kirill Grouchnikov40a39782016-07-01 17:24:32 -04003012 * Equivalent to calling {@link ListView#smoothScrollToPosition(int)}.
Winson Chung499cb9f2010-07-16 11:18:17 -07003013 *
Scott Main93dc6422012-02-24 12:04:06 -08003014 * @param viewId The id of the view to change
Winson Chung499cb9f2010-07-16 11:18:17 -07003015 * @param position Scroll to this adapter position
3016 */
3017 public void setScrollPosition(int viewId, int position) {
3018 setInt(viewId, "smoothScrollToPosition", position);
3019 }
3020
3021 /**
Kirill Grouchnikov40a39782016-07-01 17:24:32 -04003022 * Equivalent to calling {@link ListView#smoothScrollByOffset(int)}.
Winson Chung499cb9f2010-07-16 11:18:17 -07003023 *
Scott Main93dc6422012-02-24 12:04:06 -08003024 * @param viewId The id of the view to change
Winson Chung95362592010-07-19 16:05:50 -07003025 * @param offset Scroll by this adapter position offset
Winson Chung499cb9f2010-07-16 11:18:17 -07003026 */
3027 public void setRelativeScrollPosition(int viewId, int offset) {
3028 setInt(viewId, "smoothScrollByOffset", offset);
3029 }
3030
3031 /**
Daniel Sandlerd5353b42012-06-21 09:28:07 -04003032 * Equivalent to calling {@link android.view.View#setPadding(int, int, int, int)}.
Daniel Sandler99d1f742012-05-21 16:14:14 -04003033 *
3034 * @param viewId The id of the view to change
3035 * @param left the left padding in pixels
3036 * @param top the top padding in pixels
3037 * @param right the right padding in pixels
3038 * @param bottom the bottom padding in pixels
3039 */
3040 public void setViewPadding(int viewId, int left, int top, int right, int bottom) {
3041 addAction(new ViewPaddingAction(viewId, left, top, right, bottom));
3042 }
3043
3044 /**
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003045 * @hide
3046 * Equivalent to calling {@link android.view.ViewGroup.MarginLayoutParams#setMarginEnd(int)}.
3047 * Only works if the {@link View#getLayoutParams()} supports margins.
3048 * Hidden for now since we don't want to support this for all different layout margins yet.
3049 *
3050 * @param viewId The id of the view to change
Adrian Roos2d5dbba2016-06-08 17:11:53 -07003051 * @param endMarginDimen a dimen resource to read the margin from or 0 to clear the margin.
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003052 */
Adrian Roos2d5dbba2016-06-08 17:11:53 -07003053 public void setViewLayoutMarginEndDimen(int viewId, @DimenRes int endMarginDimen) {
3054 addAction(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_MARGIN_END_DIMEN,
3055 endMarginDimen));
Adrian Roos9b123cf2016-02-04 14:55:57 -08003056 }
3057
3058 /**
Selim Cinek384804b2018-04-18 14:31:07 +08003059 * Equivalent to calling {@link android.view.ViewGroup.MarginLayoutParams#setMarginEnd(int)}.
3060 * Only works if the {@link View#getLayoutParams()} supports margins.
3061 * Hidden for now since we don't want to support this for all different layout margins yet.
3062 *
3063 * @param viewId The id of the view to change
3064 * @param endMargin a value in pixels for the end margin.
3065 * @hide
3066 */
3067 public void setViewLayoutMarginEnd(int viewId, @DimenRes int endMargin) {
3068 addAction(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_MARGIN_END,
3069 endMargin));
3070 }
3071
3072 /**
Adrian Roosc1a80b02016-04-05 14:54:55 -07003073 * Equivalent to setting {@link android.view.ViewGroup.MarginLayoutParams#bottomMargin}.
3074 *
Adrian Roos2d5dbba2016-06-08 17:11:53 -07003075 * @param bottomMarginDimen a dimen resource to read the margin from or 0 to clear the margin.
Adrian Roosc1a80b02016-04-05 14:54:55 -07003076 * @hide
3077 */
Adrian Roos2d5dbba2016-06-08 17:11:53 -07003078 public void setViewLayoutMarginBottomDimen(int viewId, @DimenRes int bottomMarginDimen) {
3079 addAction(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_MARGIN_BOTTOM_DIMEN,
3080 bottomMarginDimen));
Adrian Roosc1a80b02016-04-05 14:54:55 -07003081 }
3082
3083 /**
Adrian Roos9b123cf2016-02-04 14:55:57 -08003084 * Equivalent to setting {@link android.view.ViewGroup.LayoutParams#width}.
Adrian Roos2d5dbba2016-06-08 17:11:53 -07003085 *
3086 * @param layoutWidth one of 0, MATCH_PARENT or WRAP_CONTENT. Other sizes are not allowed
3087 * because they behave poorly when the density changes.
Adrian Roos9b123cf2016-02-04 14:55:57 -08003088 * @hide
3089 */
3090 public void setViewLayoutWidth(int viewId, int layoutWidth) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07003091 if (layoutWidth != 0 && layoutWidth != ViewGroup.LayoutParams.MATCH_PARENT
3092 && layoutWidth != ViewGroup.LayoutParams.WRAP_CONTENT) {
3093 throw new IllegalArgumentException("Only supports 0, WRAP_CONTENT and MATCH_PARENT");
3094 }
Adrian Roos9b123cf2016-02-04 14:55:57 -08003095 mActions.add(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_WIDTH, layoutWidth));
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003096 }
3097
3098 /**
Joe Onorato592d0652009-03-24 22:25:52 -07003099 * Call a method taking one boolean on a view in the layout for this RemoteViews.
3100 *
Scott Main93dc6422012-02-24 12:04:06 -08003101 * @param viewId The id of the view on which to call the method.
Joe Onorato592d0652009-03-24 22:25:52 -07003102 * @param methodName The name of the method to call.
3103 * @param value The value to pass to the method.
3104 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003105 public void setBoolean(int viewId, String methodName, boolean value) {
3106 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.BOOLEAN, value));
3107 }
3108
Joe Onorato592d0652009-03-24 22:25:52 -07003109 /**
3110 * Call a method taking one byte on a view in the layout for this RemoteViews.
3111 *
Scott Main93dc6422012-02-24 12:04:06 -08003112 * @param viewId The id of the view on which to call the method.
Joe Onorato592d0652009-03-24 22:25:52 -07003113 * @param methodName The name of the method to call.
3114 * @param value The value to pass to the method.
3115 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003116 public void setByte(int viewId, String methodName, byte value) {
3117 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.BYTE, value));
3118 }
3119
Joe Onorato592d0652009-03-24 22:25:52 -07003120 /**
3121 * Call a method taking one short on a view in the layout for this RemoteViews.
3122 *
Scott Main93dc6422012-02-24 12:04:06 -08003123 * @param viewId The id of the view on which to call the method.
Joe Onorato592d0652009-03-24 22:25:52 -07003124 * @param methodName The name of the method to call.
3125 * @param value The value to pass to the method.
3126 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003127 public void setShort(int viewId, String methodName, short value) {
3128 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.SHORT, value));
3129 }
3130
Joe Onorato592d0652009-03-24 22:25:52 -07003131 /**
3132 * Call a method taking one int on a view in the layout for this RemoteViews.
3133 *
Scott Main93dc6422012-02-24 12:04:06 -08003134 * @param viewId The id of the view on which to call the method.
Joe Onorato592d0652009-03-24 22:25:52 -07003135 * @param methodName The name of the method to call.
3136 * @param value The value to pass to the method.
3137 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003138 public void setInt(int viewId, String methodName, int value) {
3139 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.INT, value));
3140 }
3141
Joe Onorato592d0652009-03-24 22:25:52 -07003142 /**
Selim Cinek396caca2018-04-10 17:46:46 -07003143 * Call a method taking one ColorStateList on a view in the layout for this RemoteViews.
3144 *
3145 * @param viewId The id of the view on which to call the method.
3146 * @param methodName The name of the method to call.
3147 * @param value The value to pass to the method.
3148 *
3149 * @hide
3150 */
3151 public void setColorStateList(int viewId, String methodName, ColorStateList value) {
3152 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.COLOR_STATE_LIST,
3153 value));
3154 }
3155
3156
3157 /**
Joe Onorato592d0652009-03-24 22:25:52 -07003158 * Call a method taking one long on a view in the layout for this RemoteViews.
3159 *
Scott Main93dc6422012-02-24 12:04:06 -08003160 * @param viewId The id of the view on which to call the method.
Joe Onorato592d0652009-03-24 22:25:52 -07003161 * @param methodName The name of the method to call.
3162 * @param value The value to pass to the method.
3163 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003164 public void setLong(int viewId, String methodName, long value) {
3165 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.LONG, value));
3166 }
3167
Joe Onorato592d0652009-03-24 22:25:52 -07003168 /**
3169 * Call a method taking one float on a view in the layout for this RemoteViews.
3170 *
Scott Main93dc6422012-02-24 12:04:06 -08003171 * @param viewId The id of the view on which to call the method.
Joe Onorato592d0652009-03-24 22:25:52 -07003172 * @param methodName The name of the method to call.
3173 * @param value The value to pass to the method.
3174 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003175 public void setFloat(int viewId, String methodName, float value) {
3176 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.FLOAT, value));
3177 }
3178
Joe Onorato592d0652009-03-24 22:25:52 -07003179 /**
3180 * Call a method taking one double on a view in the layout for this RemoteViews.
3181 *
Scott Main93dc6422012-02-24 12:04:06 -08003182 * @param viewId The id of the view on which to call the method.
Joe Onorato592d0652009-03-24 22:25:52 -07003183 * @param methodName The name of the method to call.
3184 * @param value The value to pass to the method.
3185 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003186 public void setDouble(int viewId, String methodName, double value) {
3187 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.DOUBLE, value));
3188 }
3189
Joe Onorato592d0652009-03-24 22:25:52 -07003190 /**
3191 * Call a method taking one char on a view in the layout for this RemoteViews.
3192 *
Scott Main93dc6422012-02-24 12:04:06 -08003193 * @param viewId The id of the view on which to call the method.
Joe Onorato592d0652009-03-24 22:25:52 -07003194 * @param methodName The name of the method to call.
3195 * @param value The value to pass to the method.
3196 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003197 public void setChar(int viewId, String methodName, char value) {
3198 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.CHAR, value));
3199 }
3200
Joe Onorato592d0652009-03-24 22:25:52 -07003201 /**
3202 * Call a method taking one String on a view in the layout for this RemoteViews.
3203 *
Scott Main93dc6422012-02-24 12:04:06 -08003204 * @param viewId The id of the view on which to call the method.
Joe Onorato592d0652009-03-24 22:25:52 -07003205 * @param methodName The name of the method to call.
3206 * @param value The value to pass to the method.
3207 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003208 public void setString(int viewId, String methodName, String value) {
3209 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.STRING, value));
3210 }
3211
Joe Onorato592d0652009-03-24 22:25:52 -07003212 /**
3213 * Call a method taking one CharSequence on a view in the layout for this RemoteViews.
3214 *
Scott Main93dc6422012-02-24 12:04:06 -08003215 * @param viewId The id of the view on which to call the method.
Joe Onorato592d0652009-03-24 22:25:52 -07003216 * @param methodName The name of the method to call.
3217 * @param value The value to pass to the method.
3218 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003219 public void setCharSequence(int viewId, String methodName, CharSequence value) {
3220 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.CHAR_SEQUENCE, value));
3221 }
3222
Joe Onorato592d0652009-03-24 22:25:52 -07003223 /**
3224 * Call a method taking one Uri on a view in the layout for this RemoteViews.
3225 *
Scott Main93dc6422012-02-24 12:04:06 -08003226 * @param viewId The id of the view on which to call the method.
Joe Onorato592d0652009-03-24 22:25:52 -07003227 * @param methodName The name of the method to call.
3228 * @param value The value to pass to the method.
3229 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003230 public void setUri(int viewId, String methodName, Uri value) {
Jeff Sharkeya14acd22013-04-02 18:27:45 -07003231 if (value != null) {
3232 // Resolve any filesystem path before sending remotely
3233 value = value.getCanonicalUri();
3234 if (StrictMode.vmFileUriExposureEnabled()) {
3235 value.checkFileUriExposed("RemoteViews.setUri()");
3236 }
3237 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003238 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.URI, value));
3239 }
3240
Joe Onorato592d0652009-03-24 22:25:52 -07003241 /**
3242 * Call a method taking one Bitmap on a view in the layout for this RemoteViews.
3243 * @more
3244 * <p class="note">The bitmap will be flattened into the parcel if this object is
3245 * sent across processes, so it may end up using a lot of memory, and may be fairly slow.</p>
3246 *
Scott Main93dc6422012-02-24 12:04:06 -08003247 * @param viewId The id of the view on which to call the method.
Joe Onorato592d0652009-03-24 22:25:52 -07003248 * @param methodName The name of the method to call.
3249 * @param value The value to pass to the method.
3250 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003251 public void setBitmap(int viewId, String methodName, Bitmap value) {
Adam Cohen5d200642012-04-24 10:43:31 -07003252 addAction(new BitmapReflectionAction(viewId, methodName, value));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003253 }
3254
3255 /**
Bjorn Bringertd755b062010-01-06 17:15:37 +00003256 * Call a method taking one Bundle on a view in the layout for this RemoteViews.
3257 *
Scott Main93dc6422012-02-24 12:04:06 -08003258 * @param viewId The id of the view on which to call the method.
Bjorn Bringertd755b062010-01-06 17:15:37 +00003259 * @param methodName The name of the method to call.
3260 * @param value The value to pass to the method.
3261 */
3262 public void setBundle(int viewId, String methodName, Bundle value) {
3263 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.BUNDLE, value));
3264 }
3265
3266 /**
Scott Main93dc6422012-02-24 12:04:06 -08003267 * Call a method taking one Intent on a view in the layout for this RemoteViews.
Winson Chung499cb9f2010-07-16 11:18:17 -07003268 *
Scott Main93dc6422012-02-24 12:04:06 -08003269 * @param viewId The id of the view on which to call the method.
3270 * @param methodName The name of the method to call.
3271 * @param value The {@link android.content.Intent} to pass the method.
Winson Chung499cb9f2010-07-16 11:18:17 -07003272 */
3273 public void setIntent(int viewId, String methodName, Intent value) {
3274 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.INTENT, value));
3275 }
3276
3277 /**
Dan Sandlera22a3802015-05-13 00:12:47 -04003278 * Call a method taking one Icon on a view in the layout for this RemoteViews.
3279 *
3280 * @param viewId The id of the view on which to call the method.
3281 * @param methodName The name of the method to call.
3282 * @param value The {@link android.graphics.drawable.Icon} to pass the method.
3283 */
3284 public void setIcon(int viewId, String methodName, Icon value) {
3285 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.ICON, value));
3286 }
3287
3288 /**
Svetoslav Ganov33aef982012-09-13 12:49:03 -07003289 * Equivalent to calling View.setContentDescription(CharSequence).
Svetoslav Ganove261e282011-10-18 17:47:04 -07003290 *
Svetoslav Ganov33aef982012-09-13 12:49:03 -07003291 * @param viewId The id of the view whose content description should change.
3292 * @param contentDescription The new content description for the view.
Svetoslav Ganove261e282011-10-18 17:47:04 -07003293 */
3294 public void setContentDescription(int viewId, CharSequence contentDescription) {
3295 setCharSequence(viewId, "setContentDescription", contentDescription);
3296 }
3297
Svetoslav Ganov33aef982012-09-13 12:49:03 -07003298 /**
Svetoslav6c702902014-10-09 18:40:56 -07003299 * Equivalent to calling {@link android.view.View#setAccessibilityTraversalBefore(int)}.
3300 *
3301 * @param viewId The id of the view whose before view in accessibility traversal to set.
3302 * @param nextId The id of the next in the accessibility traversal.
3303 **/
3304 public void setAccessibilityTraversalBefore(int viewId, int nextId) {
3305 setInt(viewId, "setAccessibilityTraversalBefore", nextId);
3306 }
3307
3308 /**
3309 * Equivalent to calling {@link android.view.View#setAccessibilityTraversalAfter(int)}.
3310 *
3311 * @param viewId The id of the view whose after view in accessibility traversal to set.
3312 * @param nextId The id of the next in the accessibility traversal.
3313 **/
3314 public void setAccessibilityTraversalAfter(int viewId, int nextId) {
3315 setInt(viewId, "setAccessibilityTraversalAfter", nextId);
3316 }
3317
3318 /**
Kirill Grouchnikov40a39782016-07-01 17:24:32 -04003319 * Equivalent to calling {@link View#setLabelFor(int)}.
Svetoslav Ganov33aef982012-09-13 12:49:03 -07003320 *
3321 * @param viewId The id of the view whose property to set.
3322 * @param labeledId The id of a view for which this view serves as a label.
3323 */
3324 public void setLabelFor(int viewId, int labeledId) {
3325 setInt(viewId, "setLabelFor", labeledId);
3326 }
3327
Sunny Goyalc12d31c2018-11-12 16:29:18 -08003328 /**
3329 * Provides an alternate layout ID, which can be used to inflate this view. This layout will be
3330 * used by the host when the widgets displayed on a light-background where foreground elements
3331 * and text can safely draw using a dark color without any additional background protection.
3332 */
3333 public void setLightBackgroundLayoutId(@LayoutRes int layoutId) {
3334 mLightBackgroundLayoutId = layoutId;
3335 }
3336
3337 /**
3338 * If this view supports dark text versions, creates a copy representing that version,
3339 * otherwise returns itself.
3340 * @hide
3341 */
3342 public RemoteViews getDarkTextViews() {
3343 if (hasFlags(FLAG_USE_LIGHT_BACKGROUND_LAYOUT)) {
3344 return this;
3345 }
3346
3347 try {
3348 addFlags(FLAG_USE_LIGHT_BACKGROUND_LAYOUT);
3349 return new RemoteViews(this);
3350 } finally {
3351 mApplyFlags &= ~FLAG_USE_LIGHT_BACKGROUND_LAYOUT;
3352 }
3353 }
3354
Adam Cohen5d200642012-04-24 10:43:31 -07003355 private RemoteViews getRemoteViewsToApply(Context context) {
3356 if (hasLandscapeAndPortraitLayouts()) {
3357 int orientation = context.getResources().getConfiguration().orientation;
3358 if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
3359 return mLandscape;
3360 } else {
3361 return mPortrait;
3362 }
3363 }
3364 return this;
3365 }
3366
Svetoslav Ganove261e282011-10-18 17:47:04 -07003367 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003368 * Inflates the view hierarchy represented by this object and applies
3369 * all of the actions.
Jim Millere667a7a2012-08-09 19:22:32 -07003370 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003371 * <p><strong>Caller beware: this may throw</strong>
Jim Millere667a7a2012-08-09 19:22:32 -07003372 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003373 * @param context Default context to use
3374 * @param parent Parent that the resulting view hierarchy will be attached to. This method
3375 * does <strong>not</strong> attach the hierarchy. The caller should do so when appropriate.
3376 * @return The inflated view hierarchy
3377 */
3378 public View apply(Context context, ViewGroup parent) {
Jim Millere667a7a2012-08-09 19:22:32 -07003379 return apply(context, parent, null);
Dianne Hackborn1927ae82012-06-22 15:21:36 -07003380 }
3381
Dianne Hackborna1940212012-06-28 16:07:22 -07003382 /** @hide */
3383 public View apply(Context context, ViewGroup parent, OnClickHandler handler) {
Adam Cohen5d200642012-04-24 10:43:31 -07003384 RemoteViews rvToApply = getRemoteViewsToApply(context);
3385
Sunny Goyaldd292f42015-12-02 14:29:27 -08003386 View result = inflateView(context, rvToApply, parent);
Sunny Goyaldd292f42015-12-02 14:29:27 -08003387 rvToApply.performApply(result, parent, handler);
Sunny Goyal40f589c2018-11-12 11:34:32 -08003388 return result;
3389 }
Sunny Goyaldd292f42015-12-02 14:29:27 -08003390
Sunny Goyal40f589c2018-11-12 11:34:32 -08003391 /** @hide */
3392 public View applyWithTheme(Context context, ViewGroup parent, OnClickHandler handler,
3393 @StyleRes int applyThemeResId) {
3394 RemoteViews rvToApply = getRemoteViewsToApply(context);
3395
3396 View result = inflateView(context, rvToApply, parent, applyThemeResId);
3397 rvToApply.performApply(result, parent, handler);
Sunny Goyaldd292f42015-12-02 14:29:27 -08003398 return result;
3399 }
3400
3401 private View inflateView(Context context, RemoteViews rv, ViewGroup parent) {
Sunny Goyal40f589c2018-11-12 11:34:32 -08003402 return inflateView(context, rv, parent, 0);
3403 }
3404
3405 private View inflateView(Context context, RemoteViews rv, ViewGroup parent,
3406 @StyleRes int applyThemeResId) {
Kenny Guy77320062014-08-27 21:37:15 +01003407 // RemoteViews may be built by an application installed in another
3408 // user. So build a context that loads resources from that user but
3409 // still returns the current users userId so settings like data / time formats
3410 // are loaded without requiring cross user persmissions.
3411 final Context contextForResources = getContextForResources(context);
Adrian Roos83fad0a2017-04-24 16:58:26 -07003412 Context inflationContext = new RemoteViewsContextWrapper(context, contextForResources);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003413
Dake Gu36b86c22018-04-16 12:49:30 -07003414 // If mApplyThemeResId is not given, Theme.DeviceDefault will be used.
Sunny Goyal40f589c2018-11-12 11:34:32 -08003415 if (applyThemeResId != 0) {
3416 inflationContext = new ContextThemeWrapper(inflationContext, applyThemeResId);
Dake Gu36b86c22018-04-16 12:49:30 -07003417 }
Romain Guya5475592009-07-01 17:20:08 -07003418 LayoutInflater inflater = (LayoutInflater)
Kenny Guy77320062014-08-27 21:37:15 +01003419 context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003420
Kenny Guy77320062014-08-27 21:37:15 +01003421 // Clone inflater so we load resources from correct context and
3422 // we don't add a filter to the static version returned by getSystemService.
3423 inflater = inflater.cloneInContext(inflationContext);
Zhen Zhang6e9e5fa2019-11-12 10:51:00 -08003424 inflater.setFilter(shouldUseStaticFilter() ? INFLATER_FILTER : this);
Sunny Goyal49e66952016-05-17 11:57:53 -07003425 View v = inflater.inflate(rv.getLayoutId(), parent, false);
3426 v.setTagInternal(R.id.widget_frame, rv.getLayoutId());
3427 return v;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003428 }
Adam Cohen5d200642012-04-24 10:43:31 -07003429
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003430 /**
Zhen Zhang6e9e5fa2019-11-12 10:51:00 -08003431 * A static filter is much lighter than RemoteViews itself. It's optimized here only for
3432 * RemoteVies class. Subclasses should always override this and return true if not overriding
3433 * {@link this#onLoadClass(Class)}.
3434 *
3435 * @hide
3436 */
3437 protected boolean shouldUseStaticFilter() {
3438 return this.getClass().equals(RemoteViews.class);
3439 }
3440
3441 /**
Sunny Goyaldd292f42015-12-02 14:29:27 -08003442 * Implement this interface to receive a callback when
3443 * {@link #applyAsync} or {@link #reapplyAsync} is finished.
3444 * @hide
3445 */
3446 public interface OnViewAppliedListener {
Ahan Wude396fa2018-05-08 20:42:24 +08003447 /**
3448 * Callback when the RemoteView has finished inflating,
3449 * but no actions have been applied yet.
3450 */
3451 default void onViewInflated(View v) {};
3452
Sunny Goyaldd292f42015-12-02 14:29:27 -08003453 void onViewApplied(View v);
3454
3455 void onError(Exception e);
3456 }
3457
3458 /**
3459 * Applies the views asynchronously, moving as much of the task on the background
3460 * thread as possible.
3461 *
Elliot Waite54de7742017-01-11 15:30:35 -08003462 * @see #apply(Context, ViewGroup)
Sunny Goyaldd292f42015-12-02 14:29:27 -08003463 * @param context Default context to use
3464 * @param parent Parent that the resulting view hierarchy will be attached to. This method
3465 * does <strong>not</strong> attach the hierarchy. The caller should do so when appropriate.
3466 * @param listener the callback to run when all actions have been applied. May be null.
3467 * @param executor The executor to use. If null {@link AsyncTask#THREAD_POOL_EXECUTOR} is used.
3468 * @return CancellationSignal
3469 * @hide
3470 */
3471 public CancellationSignal applyAsync(
3472 Context context, ViewGroup parent, Executor executor, OnViewAppliedListener listener) {
3473 return applyAsync(context, parent, executor, listener, null);
3474 }
3475
3476 private CancellationSignal startTaskOnExecutor(AsyncApplyTask task, Executor executor) {
3477 CancellationSignal cancelSignal = new CancellationSignal();
3478 cancelSignal.setOnCancelListener(task);
3479
3480 task.executeOnExecutor(executor == null ? AsyncTask.THREAD_POOL_EXECUTOR : executor);
3481 return cancelSignal;
3482 }
3483
3484 /** @hide */
3485 public CancellationSignal applyAsync(Context context, ViewGroup parent,
3486 Executor executor, OnViewAppliedListener listener, OnClickHandler handler) {
3487 return startTaskOnExecutor(getAsyncApplyTask(context, parent, listener, handler), executor);
3488 }
3489
3490 private AsyncApplyTask getAsyncApplyTask(Context context, ViewGroup parent,
3491 OnViewAppliedListener listener, OnClickHandler handler) {
3492 return new AsyncApplyTask(getRemoteViewsToApply(context), parent, context, listener,
3493 handler, null);
3494 }
3495
3496 private class AsyncApplyTask extends AsyncTask<Void, Void, ViewTree>
3497 implements CancellationSignal.OnCancelListener {
3498 final RemoteViews mRV;
3499 final ViewGroup mParent;
3500 final Context mContext;
3501 final OnViewAppliedListener mListener;
3502 final OnClickHandler mHandler;
3503
3504 private View mResult;
3505 private ViewTree mTree;
3506 private Action[] mActions;
3507 private Exception mError;
3508
3509 private AsyncApplyTask(
3510 RemoteViews rv, ViewGroup parent, Context context, OnViewAppliedListener listener,
3511 OnClickHandler handler, View result) {
3512 mRV = rv;
3513 mParent = parent;
3514 mContext = context;
3515 mListener = listener;
3516 mHandler = handler;
3517
3518 mResult = result;
Sunny Goyaldd292f42015-12-02 14:29:27 -08003519 }
3520
3521 @Override
3522 protected ViewTree doInBackground(Void... params) {
3523 try {
3524 if (mResult == null) {
3525 mResult = inflateView(mContext, mRV, mParent);
3526 }
3527
3528 mTree = new ViewTree(mResult);
3529 if (mRV.mActions != null) {
3530 int count = mRV.mActions.size();
3531 mActions = new Action[count];
3532 for (int i = 0; i < count && !isCancelled(); i++) {
Sunny Goyal7b0e2c72016-11-03 14:48:05 -07003533 // TODO: check if isCancelled in nested views.
Sunny Goyaldd292f42015-12-02 14:29:27 -08003534 mActions[i] = mRV.mActions.get(i).initActionAsync(mTree, mParent, mHandler);
3535 }
3536 } else {
3537 mActions = null;
3538 }
3539 return mTree;
3540 } catch (Exception e) {
3541 mError = e;
3542 return null;
3543 }
3544 }
3545
3546 @Override
3547 protected void onPostExecute(ViewTree viewTree) {
3548 if (mError == null) {
Ahan Wude396fa2018-05-08 20:42:24 +08003549 if (mListener != null) {
3550 mListener.onViewInflated(viewTree.mRoot);
3551 }
3552
Sunny Goyaldd292f42015-12-02 14:29:27 -08003553 try {
3554 if (mActions != null) {
3555 OnClickHandler handler = mHandler == null
3556 ? DEFAULT_ON_CLICK_HANDLER : mHandler;
3557 for (Action a : mActions) {
3558 a.apply(viewTree.mRoot, mParent, handler);
3559 }
3560 }
3561 } catch (Exception e) {
3562 mError = e;
3563 }
3564 }
3565
3566 if (mListener != null) {
3567 if (mError != null) {
3568 mListener.onError(mError);
3569 } else {
3570 mListener.onViewApplied(viewTree.mRoot);
3571 }
3572 } else if (mError != null) {
3573 if (mError instanceof ActionException) {
3574 throw (ActionException) mError;
3575 } else {
3576 throw new ActionException(mError);
3577 }
3578 }
3579 }
3580
3581 @Override
3582 public void onCancel() {
3583 cancel(true);
3584 }
3585 }
3586
3587 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003588 * Applies all of the actions to the provided view.
3589 *
3590 * <p><strong>Caller beware: this may throw</strong>
Jim Millere667a7a2012-08-09 19:22:32 -07003591 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003592 * @param v The view to apply the actions to. This should be the result of
3593 * the {@link #apply(Context,ViewGroup)} call.
3594 */
3595 public void reapply(Context context, View v) {
Jim Millere667a7a2012-08-09 19:22:32 -07003596 reapply(context, v, null);
Dianne Hackborna1940212012-06-28 16:07:22 -07003597 }
3598
3599 /** @hide */
3600 public void reapply(Context context, View v, OnClickHandler handler) {
Adam Cohen5d200642012-04-24 10:43:31 -07003601 RemoteViews rvToApply = getRemoteViewsToApply(context);
3602
3603 // In the case that a view has this RemoteViews applied in one orientation, is persisted
3604 // across orientation change, and has the RemoteViews re-applied in the new orientation,
3605 // we throw an exception, since the layouts may be completely unrelated.
3606 if (hasLandscapeAndPortraitLayouts()) {
Sunny Goyal49e66952016-05-17 11:57:53 -07003607 if ((Integer) v.getTag(R.id.widget_frame) != rvToApply.getLayoutId()) {
Adam Cohen5d200642012-04-24 10:43:31 -07003608 throw new RuntimeException("Attempting to re-apply RemoteViews to a view that" +
3609 " that does not share the same root layout id.");
3610 }
3611 }
3612
Dianne Hackborna1940212012-06-28 16:07:22 -07003613 rvToApply.performApply(v, (ViewGroup) v.getParent(), handler);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003614 }
3615
Sunny Goyaldd292f42015-12-02 14:29:27 -08003616 /**
3617 * Applies all the actions to the provided view, moving as much of the task on the background
3618 * thread as possible.
3619 *
Elliot Waite54de7742017-01-11 15:30:35 -08003620 * @see #reapply(Context, View)
Sunny Goyaldd292f42015-12-02 14:29:27 -08003621 * @param context Default context to use
3622 * @param v The view to apply the actions to. This should be the result of
3623 * the {@link #apply(Context,ViewGroup)} call.
3624 * @param listener the callback to run when all actions have been applied. May be null.
3625 * @param executor The executor to use. If null {@link AsyncTask#THREAD_POOL_EXECUTOR} is used
3626 * @return CancellationSignal
3627 * @hide
3628 */
3629 public CancellationSignal reapplyAsync(
3630 Context context, View v, Executor executor, OnViewAppliedListener listener) {
3631 return reapplyAsync(context, v, executor, listener, null);
3632 }
3633
3634 /** @hide */
3635 public CancellationSignal reapplyAsync(Context context, View v, Executor executor,
3636 OnViewAppliedListener listener, OnClickHandler handler) {
3637 RemoteViews rvToApply = getRemoteViewsToApply(context);
3638
3639 // In the case that a view has this RemoteViews applied in one orientation, is persisted
3640 // across orientation change, and has the RemoteViews re-applied in the new orientation,
3641 // we throw an exception, since the layouts may be completely unrelated.
3642 if (hasLandscapeAndPortraitLayouts()) {
Sunny Goyal49e66952016-05-17 11:57:53 -07003643 if ((Integer) v.getTag(R.id.widget_frame) != rvToApply.getLayoutId()) {
Sunny Goyaldd292f42015-12-02 14:29:27 -08003644 throw new RuntimeException("Attempting to re-apply RemoteViews to a view that" +
3645 " that does not share the same root layout id.");
3646 }
3647 }
3648
3649 return startTaskOnExecutor(new AsyncApplyTask(rvToApply, (ViewGroup) v.getParent(),
3650 context, listener, handler, v), executor);
3651 }
3652
Dianne Hackborna1940212012-06-28 16:07:22 -07003653 private void performApply(View v, ViewGroup parent, OnClickHandler handler) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003654 if (mActions != null) {
Jim Millere667a7a2012-08-09 19:22:32 -07003655 handler = handler == null ? DEFAULT_ON_CLICK_HANDLER : handler;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003656 final int count = mActions.size();
3657 for (int i = 0; i < count; i++) {
3658 Action a = mActions.get(i);
Dianne Hackborna1940212012-06-28 16:07:22 -07003659 a.apply(v, parent, handler);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003660 }
3661 }
3662 }
3663
Sunny Goyal5c022632016-02-17 16:30:41 -08003664 /**
3665 * Returns true if the RemoteViews contains potentially costly operations and should be
3666 * applied asynchronously.
3667 *
3668 * @hide
3669 */
3670 public boolean prefersAsyncApply() {
3671 if (mActions != null) {
3672 final int count = mActions.size();
3673 for (int i = 0; i < count; i++) {
3674 if (mActions.get(i).prefersAsyncApply()) {
3675 return true;
3676 }
3677 }
3678 }
3679 return false;
3680 }
3681
Kenny Guy77320062014-08-27 21:37:15 +01003682 private Context getContextForResources(Context context) {
Svetoslav976e8bd2014-07-16 15:12:03 -07003683 if (mApplication != null) {
3684 if (context.getUserId() == UserHandle.getUserId(mApplication.uid)
3685 && context.getPackageName().equals(mApplication.packageName)) {
3686 return context;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003687 }
Svetoslav976e8bd2014-07-16 15:12:03 -07003688 try {
3689 return context.createApplicationContext(mApplication,
3690 Context.CONTEXT_RESTRICTED);
3691 } catch (NameNotFoundException e) {
Svet Ganov0da85b62014-08-06 14:11:37 -07003692 Log.e(LOG_TAG, "Package name " + mApplication.packageName + " not found");
Svetoslav976e8bd2014-07-16 15:12:03 -07003693 }
3694 }
3695
3696 return context;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003697 }
3698
Christoph Studer4600f9b2014-07-22 22:44:43 +02003699 /**
3700 * Returns the number of actions in this RemoteViews. Can be used as a sequence number.
3701 *
3702 * @hide
3703 */
3704 public int getSequenceNumber() {
3705 return (mActions == null) ? 0 : mActions.size();
3706 }
3707
Zhen Zhang6e9e5fa2019-11-12 10:51:00 -08003708 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003709 * Used to restrict the views which can be inflated
Jim Millere667a7a2012-08-09 19:22:32 -07003710 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003711 * @see android.view.LayoutInflater.Filter#onLoadClass(java.lang.Class)
Zhen Zhang6e9e5fa2019-11-12 10:51:00 -08003712 * @deprecated Used by system to enforce safe inflation of {@link RemoteViews}. Apps should not
3713 * override this method. Changing of this method will NOT affect the process where RemoteViews
3714 * is rendered.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003715 */
Zhen Zhang6e9e5fa2019-11-12 10:51:00 -08003716 @Deprecated
Gilles Debunnee6ac8b92010-06-17 10:55:04 -07003717 public boolean onLoadClass(Class clazz) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003718 return clazz.isAnnotationPresent(RemoteView.class);
3719 }
Adam Cohen5d200642012-04-24 10:43:31 -07003720
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003721 public int describeContents() {
3722 return 0;
3723 }
3724
3725 public void writeToParcel(Parcel dest, int flags) {
Adam Cohen5d200642012-04-24 10:43:31 -07003726 if (!hasLandscapeAndPortraitLayouts()) {
3727 dest.writeInt(MODE_NORMAL);
3728 // We only write the bitmap cache if we are the root RemoteViews, as this cache
3729 // is shared by all children.
3730 if (mIsRoot) {
3731 mBitmapCache.writeBitmapsToParcel(dest, flags);
3732 }
Sunny Goyal5d8bcdf2016-10-27 16:11:29 -07003733 if (!mIsRoot && (flags & PARCELABLE_ELIDE_DUPLICATES) != 0) {
3734 dest.writeInt(0);
3735 } else {
3736 dest.writeInt(1);
3737 mApplication.writeToParcel(dest, flags);
3738 }
Adam Cohen5d200642012-04-24 10:43:31 -07003739 dest.writeInt(mLayoutId);
Sunny Goyalc12d31c2018-11-12 16:29:18 -08003740 dest.writeInt(mLightBackgroundLayoutId);
Sunny Goyal5b7689f2017-09-21 11:08:34 -07003741 writeActionsToParcel(dest);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003742 } else {
Adam Cohen5d200642012-04-24 10:43:31 -07003743 dest.writeInt(MODE_HAS_LANDSCAPE_AND_PORTRAIT);
3744 // We only write the bitmap cache if we are the root RemoteViews, as this cache
3745 // is shared by all children.
3746 if (mIsRoot) {
3747 mBitmapCache.writeBitmapsToParcel(dest, flags);
3748 }
3749 mLandscape.writeToParcel(dest, flags);
Sunny Goyal5d8bcdf2016-10-27 16:11:29 -07003750 // Both RemoteViews already share the same package and user
3751 mPortrait.writeToParcel(dest, flags | PARCELABLE_ELIDE_DUPLICATES);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003752 }
Sunny Goyalc12d31c2018-11-12 16:29:18 -08003753 dest.writeInt(mApplyFlags);
Svet Ganov0da85b62014-08-06 14:11:37 -07003754 }
Svetoslav976e8bd2014-07-16 15:12:03 -07003755
Sunny Goyal5b7689f2017-09-21 11:08:34 -07003756 private void writeActionsToParcel(Parcel parcel) {
3757 int count;
3758 if (mActions != null) {
3759 count = mActions.size();
3760 } else {
3761 count = 0;
3762 }
3763 parcel.writeInt(count);
3764 for (int i = 0; i < count; i++) {
3765 Action a = mActions.get(i);
Sunny Goyal5b153922017-09-21 21:00:36 -07003766 parcel.writeInt(a.getActionTag());
Sunny Goyal5b7689f2017-09-21 11:08:34 -07003767 a.writeToParcel(parcel, a.hasSameAppInfo(mApplication)
3768 ? PARCELABLE_ELIDE_DUPLICATES : 0);
3769 }
3770 }
3771
Svet Ganov0da85b62014-08-06 14:11:37 -07003772 private static ApplicationInfo getApplicationInfo(String packageName, int userId) {
Svetoslavb6242442014-09-19 13:21:55 -07003773 if (packageName == null) {
3774 return null;
3775 }
3776
Svet Ganov0da85b62014-08-06 14:11:37 -07003777 // Get the application for the passed in package and user.
3778 Application application = ActivityThread.currentApplication();
3779 if (application == null) {
3780 throw new IllegalStateException("Cannot create remote views out of an aplication.");
3781 }
3782
3783 ApplicationInfo applicationInfo = application.getApplicationInfo();
3784 if (UserHandle.getUserId(applicationInfo.uid) != userId
3785 || !applicationInfo.packageName.equals(packageName)) {
3786 try {
Svetoslav14494a82014-08-18 10:43:27 -07003787 Context context = application.getBaseContext().createPackageContextAsUser(
Svet Ganov0da85b62014-08-06 14:11:37 -07003788 packageName, 0, new UserHandle(userId));
3789 applicationInfo = context.getApplicationInfo();
3790 } catch (NameNotFoundException nnfe) {
3791 throw new IllegalArgumentException("No such package " + packageName);
3792 }
3793 }
3794
3795 return applicationInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003796 }
3797
3798 /**
Sunny Goyaldd60f4d2017-10-18 15:22:42 -07003799 * Returns true if the {@link #mApplication} is same as the provided info.
3800 *
3801 * @hide
3802 */
3803 public boolean hasSameAppInfo(ApplicationInfo info) {
3804 return mApplication.packageName.equals(info.packageName) && mApplication.uid == info.uid;
3805 }
3806
3807 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003808 * Parcelable.Creator that instantiates RemoteViews objects
3809 */
Jeff Sharkey9e8f83d2019-02-28 12:06:45 -07003810 public static final @android.annotation.NonNull Parcelable.Creator<RemoteViews> CREATOR = new Parcelable.Creator<RemoteViews>() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003811 public RemoteViews createFromParcel(Parcel parcel) {
3812 return new RemoteViews(parcel);
3813 }
3814
3815 public RemoteViews[] newArray(int size) {
3816 return new RemoteViews[size];
3817 }
3818 };
Sunny Goyaldd292f42015-12-02 14:29:27 -08003819
3820 /**
3821 * A representation of the view hierarchy. Only views which have a valid ID are added
3822 * and can be searched.
3823 */
3824 private static class ViewTree {
Anthony Chen8f5f3582017-04-11 11:18:37 -07003825 private static final int INSERT_AT_END_INDEX = -1;
Sunny Goyal7b0e2c72016-11-03 14:48:05 -07003826 private View mRoot;
Sunny Goyaldd292f42015-12-02 14:29:27 -08003827 private ArrayList<ViewTree> mChildren;
3828
3829 private ViewTree(View root) {
3830 mRoot = root;
3831 }
3832
3833 public void createTree() {
3834 if (mChildren != null) {
3835 return;
3836 }
3837
3838 mChildren = new ArrayList<>();
Sunny Goyal7b0e2c72016-11-03 14:48:05 -07003839 if (mRoot instanceof ViewGroup) {
Sunny Goyaldd292f42015-12-02 14:29:27 -08003840 ViewGroup vg = (ViewGroup) mRoot;
3841 int count = vg.getChildCount();
3842 for (int i = 0; i < count; i++) {
3843 addViewChild(vg.getChildAt(i));
3844 }
3845 }
3846 }
3847
3848 public ViewTree findViewTreeById(int id) {
3849 if (mRoot.getId() == id) {
3850 return this;
3851 }
3852 if (mChildren == null) {
3853 return null;
3854 }
3855 for (ViewTree tree : mChildren) {
3856 ViewTree result = tree.findViewTreeById(id);
3857 if (result != null) {
3858 return result;
3859 }
3860 }
3861 return null;
3862 }
3863
Sunny Goyal7b0e2c72016-11-03 14:48:05 -07003864 public void replaceView(View v) {
3865 mRoot = v;
3866 mChildren = null;
3867 createTree();
3868 }
3869
Alan Viverette04fd4702017-04-13 16:37:06 -04003870 public <T extends View> T findViewById(int id) {
Sunny Goyaldd292f42015-12-02 14:29:27 -08003871 if (mChildren == null) {
3872 return mRoot.findViewById(id);
3873 }
3874 ViewTree tree = findViewTreeById(id);
Alan Viverette04fd4702017-04-13 16:37:06 -04003875 return tree == null ? null : (T) tree.mRoot;
Sunny Goyaldd292f42015-12-02 14:29:27 -08003876 }
3877
3878 public void addChild(ViewTree child) {
Anthony Chen8f5f3582017-04-11 11:18:37 -07003879 addChild(child, INSERT_AT_END_INDEX);
3880 }
3881
3882 /**
3883 * Adds the given {@link ViewTree} as a child at the given index.
3884 *
3885 * @param index The position at which to add the child or -1 to add last.
3886 */
3887 public void addChild(ViewTree child, int index) {
Sunny Goyaldd292f42015-12-02 14:29:27 -08003888 if (mChildren == null) {
3889 mChildren = new ArrayList<>();
3890 }
3891 child.createTree();
Anthony Chen8f5f3582017-04-11 11:18:37 -07003892
3893 if (index == INSERT_AT_END_INDEX) {
3894 mChildren.add(child);
3895 return;
3896 }
3897
3898 mChildren.add(index, child);
Sunny Goyaldd292f42015-12-02 14:29:27 -08003899 }
3900
3901 private void addViewChild(View v) {
Sunny Goyal7b0e2c72016-11-03 14:48:05 -07003902 // ViewTree only contains Views which can be found using findViewById.
3903 // If isRootNamespace is true, this view is skipped.
3904 // @see ViewGroup#findViewTraversal(int)
3905 if (v.isRootNamespace()) {
3906 return;
3907 }
Sunny Goyaldd292f42015-12-02 14:29:27 -08003908 final ViewTree target;
3909
3910 // If the view has a valid id, i.e., if can be found using findViewById, add it to the
3911 // tree, otherwise skip this view and add its children instead.
3912 if (v.getId() != 0) {
3913 ViewTree tree = new ViewTree(v);
3914 mChildren.add(tree);
3915 target = tree;
3916 } else {
3917 target = this;
3918 }
3919
Sunny Goyal7b0e2c72016-11-03 14:48:05 -07003920 if (v instanceof ViewGroup) {
Sunny Goyaldd292f42015-12-02 14:29:27 -08003921 if (target.mChildren == null) {
3922 target.mChildren = new ArrayList<>();
3923 ViewGroup vg = (ViewGroup) v;
3924 int count = vg.getChildCount();
3925 for (int i = 0; i < count; i++) {
3926 target.addViewChild(vg.getChildAt(i));
3927 }
3928 }
3929 }
3930 }
3931 }
Sunny Goyal43c97042018-08-23 15:21:26 -07003932
3933 /**
3934 * Class representing a response to an action performed on any element of a RemoteViews.
3935 */
3936 public static class RemoteResponse {
3937
3938 private PendingIntent mPendingIntent;
3939 private Intent mFillIntent;
3940
3941 private IntArray mViewIds;
3942 private ArrayList<String> mElementNames;
3943
3944 /**
3945 * Creates a response which sends a pending intent as part of the response. The source
3946 * bounds ({@link Intent#getSourceBounds()}) of the intent will be set to the bounds of the
3947 * target view in screen space.
3948 * Note that any activity options associated with the mPendingIntent may get overridden
3949 * before starting the intent.
3950 *
3951 * @param pendingIntent The {@link PendingIntent} to send as part of the response
3952 */
Sunny Goyal40635d72019-03-01 11:46:44 -08003953 @NonNull
3954 public static RemoteResponse fromPendingIntent(@NonNull PendingIntent pendingIntent) {
Sunny Goyal43c97042018-08-23 15:21:26 -07003955 RemoteResponse response = new RemoteResponse();
3956 response.mPendingIntent = pendingIntent;
3957 return response;
3958 }
3959
3960 /**
3961 * When using collections (eg. {@link ListView}, {@link StackView} etc.) in widgets, it is
3962 * very costly to set PendingIntents on the individual items, and is hence not permitted.
3963 * Instead a single PendingIntent template can be set on the collection, see {@link
3964 * RemoteViews#setPendingIntentTemplate(int, PendingIntent)}, and the individual on-click
3965 * action of a given item can be distinguished by setting a fillInIntent on that item. The
3966 * fillInIntent is then combined with the PendingIntent template in order to determine the
3967 * final intent which will be executed when the item is clicked. This works as follows: any
3968 * fields which are left blank in the PendingIntent template, but are provided by the
3969 * fillInIntent will be overwritten, and the resulting PendingIntent will be used. The rest
3970 * of the PendingIntent template will then be filled in with the associated fields that are
3971 * set in fillInIntent. See {@link Intent#fillIn(Intent, int)} for more details.
3972 * Creates a response which sends a pending intent as part of the response. The source
3973 * bounds ({@link Intent#getSourceBounds()}) of the intent will be set to the bounds of the
3974 * target view in screen space.
3975 * Note that any activity options associated with the mPendingIntent may get overridden
3976 * before starting the intent.
3977 *
3978 * @param fillIntent The intent which will be combined with the parent's PendingIntent in
3979 * order to determine the behavior of the response
3980 *
3981 * @see RemoteViews#setPendingIntentTemplate(int, PendingIntent)
3982 * @see RemoteViews#setOnClickFillInIntent(int, Intent)
3983 * @return
3984 */
Sunny Goyal40635d72019-03-01 11:46:44 -08003985 @NonNull
3986 public static RemoteResponse fromFillInIntent(@NonNull Intent fillIntent) {
Sunny Goyal43c97042018-08-23 15:21:26 -07003987 RemoteResponse response = new RemoteResponse();
3988 response.mFillIntent = fillIntent;
3989 return response;
3990 }
3991
3992 /**
3993 * Adds a shared element to be transferred as part of the transition between Activities
3994 * using cross-Activity scene animations. The position of the first element will be used as
3995 * the epicenter for the exit Transition. The position of the associated shared element in
3996 * the launched Activity will be the epicenter of its entering Transition.
3997 *
3998 * @param viewId The id of the view to be shared as part of the transition
3999 * @param sharedElementName The shared element name for this view
4000 *
4001 * @see ActivityOptions#makeSceneTransitionAnimation(Activity, Pair[])
4002 */
Sunny Goyal40635d72019-03-01 11:46:44 -08004003 @NonNull
4004 public RemoteResponse addSharedElement(int viewId, @NonNull String sharedElementName) {
Sunny Goyal43c97042018-08-23 15:21:26 -07004005 if (mViewIds == null) {
4006 mViewIds = new IntArray();
4007 mElementNames = new ArrayList<>();
4008 }
4009 mViewIds.add(viewId);
4010 mElementNames.add(sharedElementName);
4011 return this;
4012 }
4013
4014 private void writeToParcel(Parcel dest, int flags) {
4015 PendingIntent.writePendingIntentOrNullToParcel(mPendingIntent, dest);
4016 if (mPendingIntent == null) {
4017 // Only write the intent if pending intent is null
4018 dest.writeTypedObject(mFillIntent, flags);
4019 }
4020 dest.writeIntArray(mViewIds == null ? null : mViewIds.toArray());
4021 dest.writeStringList(mElementNames);
4022 }
4023
4024 private void readFromParcel(Parcel parcel) {
4025 mPendingIntent = PendingIntent.readPendingIntentOrNullFromParcel(parcel);
4026 if (mPendingIntent == null) {
4027 mFillIntent = parcel.readTypedObject(Intent.CREATOR);
4028 }
4029 int[] viewIds = parcel.createIntArray();
4030 mViewIds = viewIds == null ? null : IntArray.wrap(viewIds);
4031 mElementNames = parcel.createStringArrayList();
4032 }
4033
4034 private void handleViewClick(View v, OnClickHandler handler) {
4035 final PendingIntent pi;
4036 if (mPendingIntent != null) {
4037 pi = mPendingIntent;
4038 } else if (mFillIntent != null) {
4039 // Insure that this view is a child of an AdapterView
4040 View parent = (View) v.getParent();
4041 // Break the for loop on the first encounter of:
4042 // 1) an AdapterView,
4043 // 2) an AppWidgetHostView that is not a RemoteViewsFrameLayout, or
4044 // 3) a null parent.
4045 // 2) and 3) are unexpected and catch the case where a child is not
4046 // correctly parented in an AdapterView.
4047 while (parent != null && !(parent instanceof AdapterView<?>)
4048 && !((parent instanceof AppWidgetHostView)
4049 && !(parent instanceof RemoteViewsAdapter.RemoteViewsFrameLayout))) {
4050 parent = (View) parent.getParent();
4051 }
4052
4053 if (!(parent instanceof AdapterView<?>)) {
4054 // Somehow they've managed to get this far without having
4055 // and AdapterView as a parent.
4056 Log.e(LOG_TAG, "Collection item doesn't have AdapterView parent");
4057 return;
4058 }
4059 // Insure that a template pending intent has been set on an ancestor
4060 if (!(parent.getTag() instanceof PendingIntent)) {
4061 Log.e(LOG_TAG, "Attempting setOnClickFillInIntent without"
4062 + " calling setPendingIntentTemplate on parent.");
4063 return;
4064 }
4065
4066 pi = (PendingIntent) parent.getTag();
4067 } else {
4068 Log.e(LOG_TAG, "Response has neither pendingIntent nor fillInIntent");
4069 return;
4070 }
4071
4072 handler.onClickHandler(v, pi, this);
4073 }
4074
4075 /** @hide */
4076 public Pair<Intent, ActivityOptions> getLaunchOptions(View view) {
4077 Intent intent = mPendingIntent != null ? new Intent() : new Intent(mFillIntent);
4078 intent.setSourceBounds(getSourceBounds(view));
4079
4080 ActivityOptions opts = null;
4081
4082 Context context = view.getContext();
4083 if (context.getResources().getBoolean(
4084 com.android.internal.R.bool.config_overrideRemoteViewsActivityTransition)) {
4085 TypedArray windowStyle = context.getTheme().obtainStyledAttributes(
4086 com.android.internal.R.styleable.Window);
4087 int windowAnimations = windowStyle.getResourceId(
4088 com.android.internal.R.styleable.Window_windowAnimationStyle, 0);
4089 TypedArray windowAnimationStyle = context.obtainStyledAttributes(
4090 windowAnimations, com.android.internal.R.styleable.WindowAnimation);
4091 int enterAnimationId = windowAnimationStyle.getResourceId(com.android.internal.R
4092 .styleable.WindowAnimation_activityOpenRemoteViewsEnterAnimation, 0);
4093 windowStyle.recycle();
4094 windowAnimationStyle.recycle();
4095
4096 if (enterAnimationId != 0) {
4097 opts = ActivityOptions.makeCustomAnimation(context,
4098 enterAnimationId, 0);
4099 opts.setPendingIntentLaunchFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
4100 }
4101 }
4102
4103 if (opts == null && mViewIds != null && mElementNames != null) {
4104 View parent = (View) view.getParent();
4105 while (parent != null && !(parent instanceof AppWidgetHostView)) {
4106 parent = (View) parent.getParent();
4107 }
4108 if (parent instanceof AppWidgetHostView) {
4109 opts = ((AppWidgetHostView) parent).createSharedElementActivityOptions(
4110 mViewIds.toArray(),
4111 mElementNames.toArray(new String[mElementNames.size()]), intent);
4112 }
4113 }
4114
4115 if (opts == null) {
4116 opts = ActivityOptions.makeBasic();
4117 opts.setPendingIntentLaunchFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
4118 }
4119 return Pair.create(intent, opts);
4120 }
4121 }
4122
4123 /** @hide */
4124 public static boolean startPendingIntent(View view, PendingIntent pendingIntent,
4125 Pair<Intent, ActivityOptions> options) {
4126 try {
4127 // TODO: Unregister this handler if PendingIntent.FLAG_ONE_SHOT?
4128 Context context = view.getContext();
4129 // The NEW_TASK flags are applied through the activity options and not as a part of
4130 // the call to startIntentSender() to ensure that they are consistently applied to
4131 // both mutable and immutable PendingIntents.
4132 context.startIntentSender(
4133 pendingIntent.getIntentSender(), options.first,
4134 0, 0, 0, options.second.toBundle());
4135 } catch (IntentSender.SendIntentException e) {
4136 Log.e(LOG_TAG, "Cannot send pending intent: ", e);
4137 return false;
4138 } catch (Exception e) {
4139 Log.e(LOG_TAG, "Cannot send pending intent due to unknown exception: ", e);
4140 return false;
4141 }
4142 return true;
4143 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004144}