blob: 86cec5e0f0a24fcb68ea9da98a46ca152d04e1cc [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 /**
Svetoslav976e8bd2014-07-16 15:12:03 -0700205 * Application that hosts the remote views.
206 *
207 * @hide
Jeff Sharkey6d515712012-09-20 16:06:08 -0700208 */
Mathew Inwood3a75f262019-06-27 12:47:38 +0100209 @UnsupportedAppUsage
Sunny Goyaldd60f4d2017-10-18 15:22:42 -0700210 public ApplicationInfo mApplication;
Jeff Sharkey6d515712012-09-20 16:06:08 -0700211
212 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800213 * The resource ID of the layout file. (Added to the parcel)
214 */
Mathew Inwood3a75f262019-06-27 12:47:38 +0100215 @UnsupportedAppUsage
Gilles Debunne30301932010-06-16 18:32:00 -0700216 private final int mLayoutId;
Romain Guya5475592009-07-01 17:20:08 -0700217
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800218 /**
Sunny Goyalc12d31c2018-11-12 16:29:18 -0800219 * The resource ID of the layout file in dark text mode. (Added to the parcel)
220 */
221 private int mLightBackgroundLayoutId = 0;
222
223 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800224 * An array of actions to perform on the view tree once it has been
225 * inflated
226 */
Mathew Inwood3a75f262019-06-27 12:47:38 +0100227 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800228 private ArrayList<Action> mActions;
Jim Millere667a7a2012-08-09 19:22:32 -0700229
Winson Chung3ec9a452010-09-23 16:40:28 -0700230 /**
Adam Cohen5d200642012-04-24 10:43:31 -0700231 * Maps bitmaps to unique indicies to avoid Bitmap duplication.
232 */
Mathew Inwood3a75f262019-06-27 12:47:38 +0100233 @UnsupportedAppUsage
Adam Cohen5d200642012-04-24 10:43:31 -0700234 private BitmapCache mBitmapCache;
235
236 /**
237 * Indicates whether or not this RemoteViews object is contained as a child of any other
238 * RemoteViews.
239 */
240 private boolean mIsRoot = true;
241
242 /**
243 * Constants to whether or not this RemoteViews is composed of a landscape and portrait
244 * RemoteViews.
245 */
246 private static final int MODE_NORMAL = 0;
247 private static final int MODE_HAS_LANDSCAPE_AND_PORTRAIT = 1;
248
249 /**
250 * Used in conjunction with the special constructor
251 * {@link #RemoteViews(RemoteViews, RemoteViews)} to keep track of the landscape and portrait
252 * RemoteViews.
253 */
254 private RemoteViews mLandscape = null;
Mathew Inwood3a75f262019-06-27 12:47:38 +0100255 @UnsupportedAppUsage
Adam Cohen5d200642012-04-24 10:43:31 -0700256 private RemoteViews mPortrait = null;
257
Sunny Goyalc12d31c2018-11-12 16:29:18 -0800258 @ApplyFlags
259 private int mApplyFlags = 0;
Dianne Hackborn1927ae82012-06-22 15:21:36 -0700260
Adrian Roosfb921842017-10-26 14:49:56 +0200261 /** Class cookies of the Parcel this instance was read from. */
262 private final Map<Class, Object> mClassCookies;
263
Sunny Goyal43c97042018-08-23 15:21:26 -0700264 private static final OnClickHandler DEFAULT_ON_CLICK_HANDLER = (view, pendingIntent, response)
265 -> startPendingIntent(view, pendingIntent, response.getLaunchOptions(view));
Adam Cohenca6fd842010-09-03 18:10:35 -0700266
Sunny Goyal271e3222017-08-29 16:05:47 -0700267 private static final ArrayMap<MethodKey, MethodArgs> sMethods = new ArrayMap<>();
Sunny Goyaldd292f42015-12-02 14:29:27 -0800268
Sunny Goyal271e3222017-08-29 16:05:47 -0700269 /**
270 * This key is used to perform lookups in sMethods without causing allocations.
271 */
272 private static final MethodKey sLookupKey = new MethodKey();
Romain Guye4d4e202013-07-22 13:02:02 -0700273
Adam Cohenca6fd842010-09-03 18:10:35 -0700274 /**
Adrian Roosfe84e1f2015-11-04 15:55:39 -0800275 * @hide
276 */
277 public void setRemoteInputs(int viewId, RemoteInput[] remoteInputs) {
278 mActions.add(new SetRemoteInputsAction(viewId, remoteInputs));
279 }
280
281 /**
Selim Cinekd0426622017-07-11 13:19:59 +0200282 * Reduces all images and ensures that they are all below the given sizes.
283 *
284 * @param maxWidth the maximum width allowed
285 * @param maxHeight the maximum height allowed
286 *
287 * @hide
288 */
289 public void reduceImageSizes(int maxWidth, int maxHeight) {
290 ArrayList<Bitmap> cache = mBitmapCache.mBitmaps;
291 for (int i = 0; i < cache.size(); i++) {
292 Bitmap bitmap = cache.get(i);
293 cache.set(i, Icon.scaleDownIfNecessary(bitmap, maxWidth, maxHeight));
294 }
295 }
296
297 /**
Selim Cinek87c31532017-08-18 18:53:44 -0700298 * Override all text colors in this layout and replace them by the given text color.
299 *
300 * @param textColor The color to use.
301 *
302 * @hide
303 */
304 public void overrideTextColors(int textColor) {
305 addAction(new OverrideTextColorsAction(textColor));
306 }
307
308 /**
Tony Mak7d4b3a52018-11-27 17:29:36 +0000309 * Sets an integer tag to the view.
310 *
311 * @hide
312 */
313 public void setIntTag(int viewId, int key, int tag) {
314 addAction(new SetIntTagAction(viewId, key, tag));
315 }
316
317 /**
Selim Cinekfc8073c2017-08-16 17:50:20 -0700318 * Set that it is disallowed to reapply another remoteview with the same layout as this view.
319 * This should be done if an action is destroying the view tree of the base layout.
320 *
321 * @hide
322 */
Sunny Goyalc12d31c2018-11-12 16:29:18 -0800323 public void addFlags(@ApplyFlags int flags) {
324 mApplyFlags = mApplyFlags | flags;
Selim Cinekfc8073c2017-08-16 17:50:20 -0700325 }
326
327 /**
Selim Cinekfc8073c2017-08-16 17:50:20 -0700328 * @hide
329 */
Sunny Goyalc12d31c2018-11-12 16:29:18 -0800330 public boolean hasFlags(@ApplyFlags int flag) {
331 return (mApplyFlags & flag) == flag;
Selim Cinekfc8073c2017-08-16 17:50:20 -0700332 }
333
334 /**
Sunny Goyal271e3222017-08-29 16:05:47 -0700335 * Stores information related to reflection method lookup.
Romain Guy484f4d62013-07-22 16:39:16 -0700336 */
Sunny Goyal271e3222017-08-29 16:05:47 -0700337 static class MethodKey {
338 public Class targetClass;
339 public Class paramClass;
340 public String methodName;
Romain Guy484f4d62013-07-22 16:39:16 -0700341
342 @Override
343 public boolean equals(Object o) {
Sunny Goyal271e3222017-08-29 16:05:47 -0700344 if (!(o instanceof MethodKey)) {
Romain Guy484f4d62013-07-22 16:39:16 -0700345 return false;
346 }
Sunny Goyal271e3222017-08-29 16:05:47 -0700347 MethodKey p = (MethodKey) o;
Narayan Kamath607223f2018-02-19 14:09:02 +0000348 return Objects.equals(p.targetClass, targetClass)
349 && Objects.equals(p.paramClass, paramClass)
350 && Objects.equals(p.methodName, methodName);
Romain Guy484f4d62013-07-22 16:39:16 -0700351 }
352
353 @Override
354 public int hashCode() {
Sunny Goyal271e3222017-08-29 16:05:47 -0700355 return Objects.hashCode(targetClass) ^ Objects.hashCode(paramClass)
356 ^ Objects.hashCode(methodName);
357 }
358
359 public void set(Class targetClass, Class paramClass, String methodName) {
360 this.targetClass = targetClass;
361 this.paramClass = paramClass;
362 this.methodName = methodName;
Romain Guy484f4d62013-07-22 16:39:16 -0700363 }
364 }
365
Sunny Goyal271e3222017-08-29 16:05:47 -0700366
Romain Guy484f4d62013-07-22 16:39:16 -0700367 /**
Sunny Goyal271e3222017-08-29 16:05:47 -0700368 * Stores information related to reflection method lookup result.
Romain Guy484f4d62013-07-22 16:39:16 -0700369 */
Sunny Goyal271e3222017-08-29 16:05:47 -0700370 static class MethodArgs {
371 public MethodHandle syncMethod;
372 public MethodHandle asyncMethod;
373 public String asyncMethodName;
374 }
375
Romain Guy484f4d62013-07-22 16:39:16 -0700376 /**
Kirill Grouchnikov9091df02016-06-28 11:24:04 -0400377 * This annotation indicates that a subclass of View is allowed to be used
Romain Guya5475592009-07-01 17:20:08 -0700378 * with the {@link RemoteViews} mechanism.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800379 */
380 @Target({ ElementType.TYPE })
381 @Retention(RetentionPolicy.RUNTIME)
382 public @interface RemoteView {
383 }
384
385 /**
386 * Exception to send when something goes wrong executing an action
387 *
388 */
389 public static class ActionException extends RuntimeException {
390 public ActionException(Exception ex) {
391 super(ex);
392 }
393 public ActionException(String message) {
394 super(message);
395 }
Sunny Goyal271e3222017-08-29 16:05:47 -0700396 /**
397 * @hide
398 */
399 public ActionException(Throwable t) {
400 super(t);
401 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800402 }
Adam Cohena32edd42010-10-26 10:35:01 -0700403
Dianne Hackborn1927ae82012-06-22 15:21:36 -0700404 /** @hide */
Sunny Goyal43c97042018-08-23 15:21:26 -0700405 public interface OnClickHandler {
Gus Prevas1ed322b2015-09-17 17:34:46 -0400406
Sunny Goyal02794532018-08-22 15:18:37 -0700407 /** @hide */
Sunny Goyal43c97042018-08-23 15:21:26 -0700408 boolean onClickHandler(View view, PendingIntent pendingIntent, RemoteResponse response);
Dianne Hackborn1927ae82012-06-22 15:21:36 -0700409 }
410
411 /**
412 * Base class for all actions that can be performed on an
413 * inflated view.
414 *
415 * SUBCLASSES MUST BE IMMUTABLE SO CLONE WORKS!!!!!
416 */
417 private abstract static class Action implements Parcelable {
Dianne Hackborna1940212012-06-28 16:07:22 -0700418 public abstract void apply(View root, ViewGroup rootParent,
419 OnClickHandler handler) throws ActionException;
Dianne Hackborn1927ae82012-06-22 15:21:36 -0700420
Adam Cohenfbe44b72012-09-19 20:36:23 -0700421 public static final int MERGE_REPLACE = 0;
422 public static final int MERGE_APPEND = 1;
423 public static final int MERGE_IGNORE = 2;
424
Dianne Hackborn1927ae82012-06-22 15:21:36 -0700425 public int describeContents() {
426 return 0;
427 }
428
Adam Cohen5d200642012-04-24 10:43:31 -0700429 public void setBitmapCache(BitmapCache bitmapCache) {
430 // Do nothing
431 }
Adam Cohenfbe44b72012-09-19 20:36:23 -0700432
Mathew Inwood3a75f262019-06-27 12:47:38 +0100433 @UnsupportedAppUsage
Adam Cohenfbe44b72012-09-19 20:36:23 -0700434 public int mergeBehavior() {
435 return MERGE_REPLACE;
436 }
437
Sunny Goyal5b153922017-09-21 21:00:36 -0700438 public abstract int getActionTag();
Adam Cohenfbe44b72012-09-19 20:36:23 -0700439
440 public String getUniqueKey() {
Sunny Goyal5b153922017-09-21 21:00:36 -0700441 return (getActionTag() + "_" + viewId);
Adam Cohenfbe44b72012-09-19 20:36:23 -0700442 }
443
Sunny Goyaldd292f42015-12-02 14:29:27 -0800444 /**
445 * This is called on the background thread. It should perform any non-ui computations
446 * and return the final action which will run on the UI thread.
447 * Override this if some of the tasks can be performed async.
448 */
449 public Action initActionAsync(ViewTree root, ViewGroup rootParent, OnClickHandler handler) {
450 return this;
451 }
452
Sunny Goyal5c022632016-02-17 16:30:41 -0800453 public boolean prefersAsyncApply() {
454 return false;
455 }
456
Sunny Goyal5d8bcdf2016-10-27 16:11:29 -0700457 /**
458 * Overridden by subclasses which have (or inherit) an ApplicationInfo instance
459 * as member variable
460 */
461 public boolean hasSameAppInfo(ApplicationInfo parentInfo) {
462 return true;
463 }
464
Jeff Sharkey23b31182018-04-18 21:32:12 -0600465 public void visitUris(@NonNull Consumer<Uri> visitor) {
466 // Nothing to visit by default
467 }
468
Mathew Inwood3a75f262019-06-27 12:47:38 +0100469 @UnsupportedAppUsage
Adam Cohenfbe44b72012-09-19 20:36:23 -0700470 int viewId;
471 }
472
Adam Cohenbd1e0072012-09-21 16:51:50 -0700473 /**
Sunny Goyaldd292f42015-12-02 14:29:27 -0800474 * Action class used during async inflation of RemoteViews. Subclasses are not parcelable.
475 */
476 private static abstract class RuntimeAction extends Action {
477 @Override
Sunny Goyal5b153922017-09-21 21:00:36 -0700478 public final int getActionTag() {
479 return 0;
Sunny Goyaldd292f42015-12-02 14:29:27 -0800480 }
481
482 @Override
483 public final void writeToParcel(Parcel dest, int flags) {
484 throw new UnsupportedOperationException();
485 }
486 }
487
488 // Constant used during async execution. It is not parcelable.
489 private static final Action ACTION_NOOP = new RuntimeAction() {
490 @Override
491 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) { }
492 };
493
494 /**
Adam Cohenbd1e0072012-09-21 16:51:50 -0700495 * Merges the passed RemoteViews actions with this RemoteViews actions according to
496 * action-specific merge rules.
Svetoslav976e8bd2014-07-16 15:12:03 -0700497 *
Adam Cohenbd1e0072012-09-21 16:51:50 -0700498 * @param newRv
Svetoslav976e8bd2014-07-16 15:12:03 -0700499 *
Adam Cohenbd1e0072012-09-21 16:51:50 -0700500 * @hide
501 */
Mathew Inwood3a75f262019-06-27 12:47:38 +0100502 @UnsupportedAppUsage
Adam Cohenfbe44b72012-09-19 20:36:23 -0700503 public void mergeRemoteViews(RemoteViews newRv) {
Adam Cohen3ff2d862012-09-26 14:07:57 -0700504 if (newRv == null) return;
Adam Cohenfbe44b72012-09-19 20:36:23 -0700505 // We first copy the new RemoteViews, as the process of merging modifies the way the actions
506 // reference the bitmap cache. We don't want to modify the object as it may need to
507 // be merged and applied multiple times.
Sunny Goyal56333a82017-08-29 13:46:29 -0700508 RemoteViews copy = new RemoteViews(newRv);
Adam Cohenfbe44b72012-09-19 20:36:23 -0700509
510 HashMap<String, Action> map = new HashMap<String, Action>();
511 if (mActions == null) {
512 mActions = new ArrayList<Action>();
513 }
514
515 int count = mActions.size();
516 for (int i = 0; i < count; i++) {
517 Action a = mActions.get(i);
518 map.put(a.getUniqueKey(), a);
519 }
520
521 ArrayList<Action> newActions = copy.mActions;
522 if (newActions == null) return;
523 count = newActions.size();
524 for (int i = 0; i < count; i++) {
525 Action a = newActions.get(i);
526 String key = newActions.get(i).getUniqueKey();
Adam Cohen3ff2d862012-09-26 14:07:57 -0700527 int mergeBehavior = newActions.get(i).mergeBehavior();
Adam Cohenfbe44b72012-09-19 20:36:23 -0700528 if (map.containsKey(key) && mergeBehavior == Action.MERGE_REPLACE) {
529 mActions.remove(map.get(key));
530 map.remove(key);
531 }
532
533 // If the merge behavior is ignore, we don't bother keeping the extra action
534 if (mergeBehavior == Action.MERGE_REPLACE || mergeBehavior == Action.MERGE_APPEND) {
535 mActions.add(a);
536 }
537 }
538
539 // Because pruning can remove the need for bitmaps, we reconstruct the bitmap cache
540 mBitmapCache = new BitmapCache();
541 setBitmapCache(mBitmapCache);
Romain Guya5475592009-07-01 17:20:08 -0700542 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800543
Jeff Sharkey23b31182018-04-18 21:32:12 -0600544 /**
545 * Note all {@link Uri} that are referenced internally, with the expectation
546 * that Uri permission grants will need to be issued to ensure the recipient
547 * of this object is able to render its contents.
548 *
549 * @hide
550 */
551 public void visitUris(@NonNull Consumer<Uri> visitor) {
552 if (mActions != null) {
553 for (int i = 0; i < mActions.size(); i++) {
554 mActions.get(i).visitUris(visitor);
555 }
556 }
557 }
558
559 private static void visitIconUri(Icon icon, @NonNull Consumer<Uri> visitor) {
560 if (icon != null && icon.getType() == Icon.TYPE_URI) {
561 visitor.accept(icon.getUri());
562 }
563 }
564
Adrian Roos83fad0a2017-04-24 16:58:26 -0700565 private static class RemoteViewsContextWrapper extends ContextWrapper {
566 private final Context mContextForResources;
567
568 RemoteViewsContextWrapper(Context context, Context contextForResources) {
569 super(context);
570 mContextForResources = contextForResources;
571 }
572
573 @Override
574 public Resources getResources() {
575 return mContextForResources.getResources();
576 }
577
578 @Override
579 public Resources.Theme getTheme() {
580 return mContextForResources.getTheme();
581 }
582
583 @Override
584 public String getPackageName() {
585 return mContextForResources.getPackageName();
586 }
587 }
588
Adam Cohen1480fdd2010-08-25 17:24:53 -0700589 private class SetEmptyView extends Action {
Adam Cohen1480fdd2010-08-25 17:24:53 -0700590 int emptyViewId;
591
Adam Cohen1480fdd2010-08-25 17:24:53 -0700592 SetEmptyView(int viewId, int emptyViewId) {
593 this.viewId = viewId;
594 this.emptyViewId = emptyViewId;
595 }
596
597 SetEmptyView(Parcel in) {
598 this.viewId = in.readInt();
599 this.emptyViewId = in.readInt();
600 }
601
602 public void writeToParcel(Parcel out, int flags) {
Adam Cohen1480fdd2010-08-25 17:24:53 -0700603 out.writeInt(this.viewId);
604 out.writeInt(this.emptyViewId);
605 }
606
607 @Override
Dianne Hackborna1940212012-06-28 16:07:22 -0700608 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
Adam Cohen1480fdd2010-08-25 17:24:53 -0700609 final View view = root.findViewById(viewId);
610 if (!(view instanceof AdapterView<?>)) return;
611
612 AdapterView<?> adapterView = (AdapterView<?>) view;
613
614 final View emptyView = root.findViewById(emptyViewId);
615 if (emptyView == null) return;
616
617 adapterView.setEmptyView(emptyView);
618 }
Adam Cohenfbe44b72012-09-19 20:36:23 -0700619
Sunny Goyal5b153922017-09-21 21:00:36 -0700620 @Override
621 public int getActionTag() {
622 return SET_EMPTY_VIEW_ACTION_TAG;
Adam Cohenfbe44b72012-09-19 20:36:23 -0700623 }
Adam Cohen1480fdd2010-08-25 17:24:53 -0700624 }
625
Adam Cohenca6fd842010-09-03 18:10:35 -0700626 private class SetPendingIntentTemplate extends Action {
627 public SetPendingIntentTemplate(int id, PendingIntent pendingIntentTemplate) {
628 this.viewId = id;
629 this.pendingIntentTemplate = pendingIntentTemplate;
630 }
631
632 public SetPendingIntentTemplate(Parcel parcel) {
633 viewId = parcel.readInt();
634 pendingIntentTemplate = PendingIntent.readPendingIntentOrNullFromParcel(parcel);
635 }
636
637 public void writeToParcel(Parcel dest, int flags) {
Adam Cohenca6fd842010-09-03 18:10:35 -0700638 dest.writeInt(viewId);
Sunny Goyal5b153922017-09-21 21:00:36 -0700639 PendingIntent.writePendingIntentOrNullToParcel(pendingIntentTemplate, dest);
Adam Cohenca6fd842010-09-03 18:10:35 -0700640 }
641
642 @Override
Dianne Hackborna1940212012-06-28 16:07:22 -0700643 public void apply(View root, ViewGroup rootParent, final OnClickHandler handler) {
Adam Cohenca6fd842010-09-03 18:10:35 -0700644 final View target = root.findViewById(viewId);
Joe Onorato2b69ce42010-10-31 11:35:41 -0700645 if (target == null) return;
Adam Cohenca6fd842010-09-03 18:10:35 -0700646
647 // If the view isn't an AdapterView, setting a PendingIntent template doesn't make sense
648 if (target instanceof AdapterView<?>) {
Adam Cohena32edd42010-10-26 10:35:01 -0700649 AdapterView<?> av = (AdapterView<?>) target;
Adam Cohenca6fd842010-09-03 18:10:35 -0700650 // The PendingIntent template is stored in the view's tag.
Adam Cohena32edd42010-10-26 10:35:01 -0700651 OnItemClickListener listener = new OnItemClickListener() {
652 public void onItemClick(AdapterView<?> parent, View view,
653 int position, long id) {
654 // The view should be a frame layout
655 if (view instanceof ViewGroup) {
656 ViewGroup vg = (ViewGroup) view;
657
658 // AdapterViews contain their children in a frame
659 // so we need to go one layer deeper here.
660 if (parent instanceof AdapterViewAnimator) {
661 vg = (ViewGroup) vg.getChildAt(0);
662 }
663 if (vg == null) return;
664
Sunny Goyal43c97042018-08-23 15:21:26 -0700665 RemoteResponse response = null;
Adam Cohena32edd42010-10-26 10:35:01 -0700666 int childCount = vg.getChildCount();
667 for (int i = 0; i < childCount; i++) {
668 Object tag = vg.getChildAt(i).getTag(com.android.internal.R.id.fillInIntent);
Sunny Goyal43c97042018-08-23 15:21:26 -0700669 if (tag instanceof RemoteResponse) {
670 response = (RemoteResponse) tag;
Adam Cohena32edd42010-10-26 10:35:01 -0700671 break;
672 }
673 }
Sunny Goyal43c97042018-08-23 15:21:26 -0700674 if (response == null) return;
675 response.handleViewClick(view, handler);
Adam Cohena32edd42010-10-26 10:35:01 -0700676 }
677 }
678 };
679 av.setOnItemClickListener(listener);
680 av.setTag(pendingIntentTemplate);
Adam Cohenca6fd842010-09-03 18:10:35 -0700681 } else {
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800682 Log.e(LOG_TAG, "Cannot setPendingIntentTemplate on a view which is not" +
Adam Cohen35ae9ca2010-09-20 15:20:41 -0700683 "an AdapterView (id: " + viewId + ")");
Adam Cohenca6fd842010-09-03 18:10:35 -0700684 return;
685 }
686 }
687
Sunny Goyal5b153922017-09-21 21:00:36 -0700688 @Override
689 public int getActionTag() {
690 return SET_PENDING_INTENT_TEMPLATE_TAG;
Adam Cohenfbe44b72012-09-19 20:36:23 -0700691 }
692
Mathew Inwood3a75f262019-06-27 12:47:38 +0100693 @UnsupportedAppUsage
Adam Cohenca6fd842010-09-03 18:10:35 -0700694 PendingIntent pendingIntentTemplate;
Adam Cohenca6fd842010-09-03 18:10:35 -0700695 }
696
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800697 private class SetRemoteViewsAdapterList extends Action {
Adam Cohenb00d9f02013-01-10 14:12:52 -0800698 public SetRemoteViewsAdapterList(int id, ArrayList<RemoteViews> list, int viewTypeCount) {
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800699 this.viewId = id;
700 this.list = list;
Adam Cohenb00d9f02013-01-10 14:12:52 -0800701 this.viewTypeCount = viewTypeCount;
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800702 }
703
704 public SetRemoteViewsAdapterList(Parcel parcel) {
705 viewId = parcel.readInt();
Adam Cohenb00d9f02013-01-10 14:12:52 -0800706 viewTypeCount = parcel.readInt();
Sunny Goyal5b153922017-09-21 21:00:36 -0700707 list = parcel.createTypedArrayList(RemoteViews.CREATOR);
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800708 }
709
710 public void writeToParcel(Parcel dest, int flags) {
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800711 dest.writeInt(viewId);
Adam Cohenb00d9f02013-01-10 14:12:52 -0800712 dest.writeInt(viewTypeCount);
Sunny Goyal5b153922017-09-21 21:00:36 -0700713 dest.writeTypedList(list, flags);
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800714 }
715
716 @Override
717 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
718 final View target = root.findViewById(viewId);
719 if (target == null) return;
720
721 // Ensure that we are applying to an AppWidget root
722 if (!(rootParent instanceof AppWidgetHostView)) {
723 Log.e(LOG_TAG, "SetRemoteViewsAdapterIntent action can only be used for " +
724 "AppWidgets (root id: " + viewId + ")");
725 return;
726 }
727 // Ensure that we are calling setRemoteAdapter on an AdapterView that supports it
728 if (!(target instanceof AbsListView) && !(target instanceof AdapterViewAnimator)) {
729 Log.e(LOG_TAG, "Cannot setRemoteViewsAdapter on a view which is not " +
730 "an AbsListView or AdapterViewAnimator (id: " + viewId + ")");
731 return;
732 }
733
734 if (target instanceof AbsListView) {
735 AbsListView v = (AbsListView) target;
736 Adapter a = v.getAdapter();
Adam Cohenb00d9f02013-01-10 14:12:52 -0800737 if (a instanceof RemoteViewsListAdapter && viewTypeCount <= a.getViewTypeCount()) {
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800738 ((RemoteViewsListAdapter) a).setViewsList(list);
739 } else {
Adam Cohenb00d9f02013-01-10 14:12:52 -0800740 v.setAdapter(new RemoteViewsListAdapter(v.getContext(), list, viewTypeCount));
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800741 }
742 } else if (target instanceof AdapterViewAnimator) {
743 AdapterViewAnimator v = (AdapterViewAnimator) 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 }
751 }
752
Sunny Goyal5b153922017-09-21 21:00:36 -0700753 @Override
754 public int getActionTag() {
755 return SET_REMOTE_VIEW_ADAPTER_LIST_TAG;
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800756 }
757
Adam Cohenb00d9f02013-01-10 14:12:52 -0800758 int viewTypeCount;
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800759 ArrayList<RemoteViews> list;
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800760 }
761
Winson Chung037300b2011-03-29 15:40:16 -0700762 private class SetRemoteViewsAdapterIntent extends Action {
763 public SetRemoteViewsAdapterIntent(int id, Intent intent) {
764 this.viewId = id;
765 this.intent = intent;
766 }
767
768 public SetRemoteViewsAdapterIntent(Parcel parcel) {
769 viewId = parcel.readInt();
Sunny Goyal5b153922017-09-21 21:00:36 -0700770 intent = parcel.readTypedObject(Intent.CREATOR);
Winson Chung037300b2011-03-29 15:40:16 -0700771 }
772
773 public void writeToParcel(Parcel dest, int flags) {
Winson Chung037300b2011-03-29 15:40:16 -0700774 dest.writeInt(viewId);
Sunny Goyal5b153922017-09-21 21:00:36 -0700775 dest.writeTypedObject(intent, flags);
Winson Chung037300b2011-03-29 15:40:16 -0700776 }
777
778 @Override
Dianne Hackborna1940212012-06-28 16:07:22 -0700779 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
Winson Chung037300b2011-03-29 15:40:16 -0700780 final View target = root.findViewById(viewId);
781 if (target == null) return;
782
783 // Ensure that we are applying to an AppWidget root
784 if (!(rootParent instanceof AppWidgetHostView)) {
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800785 Log.e(LOG_TAG, "SetRemoteViewsAdapterIntent action can only be used for " +
Winson Chung037300b2011-03-29 15:40:16 -0700786 "AppWidgets (root id: " + viewId + ")");
787 return;
788 }
789 // Ensure that we are calling setRemoteAdapter on an AdapterView that supports it
790 if (!(target instanceof AbsListView) && !(target instanceof AdapterViewAnimator)) {
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800791 Log.e(LOG_TAG, "Cannot setRemoteViewsAdapter on a view which is not " +
Winson Chung037300b2011-03-29 15:40:16 -0700792 "an AbsListView or AdapterViewAnimator (id: " + viewId + ")");
793 return;
794 }
795
796 // Embed the AppWidget Id for use in RemoteViewsAdapter when connecting to the intent
797 // RemoteViewsService
798 AppWidgetHostView host = (AppWidgetHostView) rootParent;
Sunny Goyalc12d31c2018-11-12 16:29:18 -0800799 intent.putExtra(EXTRA_REMOTEADAPTER_APPWIDGET_ID, host.getAppWidgetId())
800 .putExtra(EXTRA_REMOTEADAPTER_ON_LIGHT_BACKGROUND,
801 hasFlags(FLAG_USE_LIGHT_BACKGROUND_LAYOUT));
802
Winson Chung037300b2011-03-29 15:40:16 -0700803 if (target instanceof AbsListView) {
804 AbsListView v = (AbsListView) target;
Sunny Goyal5c022632016-02-17 16:30:41 -0800805 v.setRemoteViewsAdapter(intent, isAsync);
Adam Cohena6a4cbc2012-09-26 17:36:40 -0700806 v.setRemoteViewsOnClickHandler(handler);
Winson Chung037300b2011-03-29 15:40:16 -0700807 } else if (target instanceof AdapterViewAnimator) {
808 AdapterViewAnimator v = (AdapterViewAnimator) target;
Sunny Goyal5c022632016-02-17 16:30:41 -0800809 v.setRemoteViewsAdapter(intent, isAsync);
Adam Cohena6a4cbc2012-09-26 17:36:40 -0700810 v.setRemoteViewsOnClickHandler(handler);
Winson Chung037300b2011-03-29 15:40:16 -0700811 }
812 }
813
Sunny Goyal5c022632016-02-17 16:30:41 -0800814 @Override
815 public Action initActionAsync(ViewTree root, ViewGroup rootParent,
816 OnClickHandler handler) {
817 SetRemoteViewsAdapterIntent copy = new SetRemoteViewsAdapterIntent(viewId, intent);
818 copy.isAsync = true;
819 return copy;
820 }
821
Sunny Goyal5b153922017-09-21 21:00:36 -0700822 @Override
823 public int getActionTag() {
824 return SET_REMOTE_VIEW_ADAPTER_INTENT_TAG;
Adam Cohenfbe44b72012-09-19 20:36:23 -0700825 }
826
Winson Chung037300b2011-03-29 15:40:16 -0700827 Intent intent;
Sunny Goyal5c022632016-02-17 16:30:41 -0800828 boolean isAsync = false;
Winson Chung037300b2011-03-29 15:40:16 -0700829 }
830
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800831 /**
832 * Equivalent to calling
833 * {@link android.view.View#setOnClickListener(android.view.View.OnClickListener)}
834 * to launch the provided {@link PendingIntent}.
835 */
Sunny Goyal43c97042018-08-23 15:21:26 -0700836 private class SetOnClickResponse extends Action {
837
838 SetOnClickResponse(int id, RemoteResponse response) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800839 this.viewId = id;
Sunny Goyal43c97042018-08-23 15:21:26 -0700840 this.mResponse = response;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800841 }
Adam Cohenca6fd842010-09-03 18:10:35 -0700842
Sunny Goyal43c97042018-08-23 15:21:26 -0700843 SetOnClickResponse(Parcel parcel) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800844 viewId = parcel.readInt();
Sunny Goyal43c97042018-08-23 15:21:26 -0700845 mResponse = new RemoteResponse();
846 mResponse.readFromParcel(parcel);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800847 }
Adam Cohenca6fd842010-09-03 18:10:35 -0700848
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800849 public void writeToParcel(Parcel dest, int flags) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800850 dest.writeInt(viewId);
Sunny Goyal43c97042018-08-23 15:21:26 -0700851 mResponse.writeToParcel(dest, flags);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800852 }
Adam Cohenca6fd842010-09-03 18:10:35 -0700853
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800854 @Override
Dianne Hackborna1940212012-06-28 16:07:22 -0700855 public void apply(View root, ViewGroup rootParent, final OnClickHandler handler) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800856 final View target = root.findViewById(viewId);
Joe Onorato2b69ce42010-10-31 11:35:41 -0700857 if (target == null) return;
Adam Cohenca6fd842010-09-03 18:10:35 -0700858
Sunny Goyal43c97042018-08-23 15:21:26 -0700859 if (mResponse.mPendingIntent != null) {
860 // If the view is an AdapterView, setting a PendingIntent on click doesn't make
861 // much sense, do they mean to set a PendingIntent template for the
862 // AdapterView's children?
Sunny Goyalc12d31c2018-11-12 16:29:18 -0800863 if (hasFlags(FLAG_WIDGET_IS_COLLECTION_CHILD)) {
Sunny Goyal43c97042018-08-23 15:21:26 -0700864 Log.w(LOG_TAG, "Cannot SetOnClickResponse for collection item "
865 + "(id: " + viewId + ")");
866 ApplicationInfo appInfo = root.getContext().getApplicationInfo();
Adam Cohenffc46a52012-04-30 19:54:39 -0700867
Sunny Goyal43c97042018-08-23 15:21:26 -0700868 // We let this slide for HC and ICS so as to not break compatibility. It should
869 // have been disabled from the outset, but was left open by accident.
870 if (appInfo != null
871 && appInfo.targetSdkVersion >= Build.VERSION_CODES.JELLY_BEAN) {
872 return;
873 }
874 }
875 target.setTagInternal(R.id.pending_intent_tag, mResponse.mPendingIntent);
876 } else if (mResponse.mFillIntent != null) {
Sunny Goyalc12d31c2018-11-12 16:29:18 -0800877 if (!hasFlags(FLAG_WIDGET_IS_COLLECTION_CHILD)) {
Sunny Goyal43c97042018-08-23 15:21:26 -0700878 Log.e(LOG_TAG, "The method setOnClickFillInIntent is available "
879 + "only from RemoteViewsFactory (ie. on collection items).");
Adam Cohenffc46a52012-04-30 19:54:39 -0700880 return;
881 }
Sunny Goyal43c97042018-08-23 15:21:26 -0700882 if (target == root) {
883 // Target is a root node of an AdapterView child. Set the response in the tag.
884 // Actual click handling is done by OnItemClickListener in
885 // SetPendingIntentTemplate, which uses this tag information.
886 target.setTagInternal(com.android.internal.R.id.fillInIntent, mResponse);
887 return;
888 }
889 } else {
890 // No intent to apply
891 target.setOnClickListener(null);
892 return;
Adam Cohenca6fd842010-09-03 18:10:35 -0700893 }
Sunny Goyal43c97042018-08-23 15:21:26 -0700894 target.setOnClickListener(v -> mResponse.handleViewClick(v, handler));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800895 }
Adam Cohenc6151f22012-02-02 21:02:31 -0800896
Sunny Goyal5b153922017-09-21 21:00:36 -0700897 @Override
898 public int getActionTag() {
Sunny Goyal43c97042018-08-23 15:21:26 -0700899 return SET_ON_CLICK_RESPONSE_TAG;
Adam Cohenfbe44b72012-09-19 20:36:23 -0700900 }
901
Sunny Goyal43c97042018-08-23 15:21:26 -0700902 final RemoteResponse mResponse;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800903 }
904
Sunny Goyal43c97042018-08-23 15:21:26 -0700905 /** @hide **/
906 public static Rect getSourceBounds(View v) {
Romain Guye4d4e202013-07-22 13:02:02 -0700907 final float appScale = v.getContext().getResources()
908 .getCompatibilityInfo().applicationScale;
909 final int[] pos = new int[2];
910 v.getLocationOnScreen(pos);
911
912 final Rect rect = new Rect();
913 rect.left = (int) (pos[0] * appScale + 0.5f);
914 rect.top = (int) (pos[1] * appScale + 0.5f);
915 rect.right = (int) ((pos[0] + v.getWidth()) * appScale + 0.5f);
916 rect.bottom = (int) ((pos[1] + v.getHeight()) * appScale + 0.5f);
917 return rect;
918 }
919
Sunny Goyal271e3222017-08-29 16:05:47 -0700920 private MethodHandle getMethod(View view, String methodName, Class<?> paramType,
921 boolean async) {
922 MethodArgs result;
Romain Guye4d4e202013-07-22 13:02:02 -0700923 Class<? extends View> klass = view.getClass();
924
Sunny Goyal271e3222017-08-29 16:05:47 -0700925 synchronized (sMethods) {
926 // The key is defined by the view class, param class and method name.
927 sLookupKey.set(klass, paramType, methodName);
928 result = sMethods.get(sLookupKey);
Romain Guye4d4e202013-07-22 13:02:02 -0700929
Sunny Goyal271e3222017-08-29 16:05:47 -0700930 if (result == null) {
931 Method method;
Romain Guye4d4e202013-07-22 13:02:02 -0700932 try {
Romain Guy9870e5c2013-07-23 13:09:51 -0700933 if (paramType == null) {
934 method = klass.getMethod(methodName);
935 } else {
936 method = klass.getMethod(methodName, paramType);
937 }
Sunny Goyal271e3222017-08-29 16:05:47 -0700938 if (!method.isAnnotationPresent(RemotableViewMethod.class)) {
939 throw new ActionException("view: " + klass.getName()
940 + " can't use method with RemoteViews: "
941 + methodName + getParameters(paramType));
942 }
943
944 result = new MethodArgs();
945 result.syncMethod = MethodHandles.publicLookup().unreflect(method);
946 result.asyncMethodName =
947 method.getAnnotation(RemotableViewMethod.class).asyncImpl();
948 } catch (NoSuchMethodException | IllegalAccessException ex) {
Romain Guye4d4e202013-07-22 13:02:02 -0700949 throw new ActionException("view: " + klass.getName() + " doesn't have method: "
950 + methodName + getParameters(paramType));
951 }
952
Sunny Goyal271e3222017-08-29 16:05:47 -0700953 MethodKey key = new MethodKey();
954 key.set(klass, paramType, methodName);
955 sMethods.put(key, result);
Sunny Goyaldd292f42015-12-02 14:29:27 -0800956 }
957
Sunny Goyal271e3222017-08-29 16:05:47 -0700958 if (!async) {
959 return result.syncMethod;
960 }
961 // Check this so see if async method is implemented or not.
962 if (result.asyncMethodName.isEmpty()) {
963 return null;
964 }
965 // Async method is lazily loaded. If it is not yet loaded, load now.
966 if (result.asyncMethod == null) {
967 MethodType asyncType = result.syncMethod.type()
968 .dropParameterTypes(0, 1).changeReturnType(Runnable.class);
Sunny Goyaldd292f42015-12-02 14:29:27 -0800969 try {
Sunny Goyal271e3222017-08-29 16:05:47 -0700970 result.asyncMethod = MethodHandles.publicLookup().findVirtual(
971 klass, result.asyncMethodName, asyncType);
972 } catch (NoSuchMethodException | IllegalAccessException ex) {
973 throw new ActionException("Async implementation declared as "
974 + result.asyncMethodName + " but not defined for " + methodName
975 + ": public Runnable " + result.asyncMethodName + " ("
976 + TextUtils.join(",", asyncType.parameterArray()) + ")");
Sunny Goyaldd292f42015-12-02 14:29:27 -0800977 }
978 }
Sunny Goyal271e3222017-08-29 16:05:47 -0700979 return result.asyncMethod;
Sunny Goyaldd292f42015-12-02 14:29:27 -0800980 }
981 }
982
Romain Guye4d4e202013-07-22 13:02:02 -0700983 private static String getParameters(Class<?> paramType) {
984 if (paramType == null) return "()";
985 return "(" + paramType + ")";
986 }
987
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800988 /**
Sunny Goyal5b153922017-09-21 21:00:36 -0700989 * Equivalent to calling
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800990 * {@link Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)},
Sunny Goyal5b153922017-09-21 21:00:36 -0700991 * on the {@link Drawable} of a given view.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800992 * <p>
Sunny Goyal5b153922017-09-21 21:00:36 -0700993 * The operation will be performed on the {@link Drawable} returned by the
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800994 * target {@link View#getBackground()} by default. If targetBackground is false,
995 * we assume the target is an {@link ImageView} and try applying the operations
996 * to {@link ImageView#getDrawable()}.
997 * <p>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800998 */
Sunny Goyal5b153922017-09-21 21:00:36 -0700999 private class SetDrawableTint extends Action {
1000 SetDrawableTint(int id, boolean targetBackground,
1001 int colorFilter, @NonNull PorterDuff.Mode mode) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001002 this.viewId = id;
1003 this.targetBackground = targetBackground;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001004 this.colorFilter = colorFilter;
1005 this.filterMode = mode;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001006 }
Jim Millere667a7a2012-08-09 19:22:32 -07001007
Sunny Goyal5b153922017-09-21 21:00:36 -07001008 SetDrawableTint(Parcel parcel) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001009 viewId = parcel.readInt();
1010 targetBackground = parcel.readInt() != 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001011 colorFilter = parcel.readInt();
Sunny Goyal5b153922017-09-21 21:00:36 -07001012 filterMode = PorterDuff.intToMode(parcel.readInt());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001013 }
Jim Millere667a7a2012-08-09 19:22:32 -07001014
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001015 public void writeToParcel(Parcel dest, int flags) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001016 dest.writeInt(viewId);
1017 dest.writeInt(targetBackground ? 1 : 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001018 dest.writeInt(colorFilter);
Sunny Goyal5b153922017-09-21 21:00:36 -07001019 dest.writeInt(PorterDuff.modeToInt(filterMode));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001020 }
Jim Millere667a7a2012-08-09 19:22:32 -07001021
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001022 @Override
Dianne Hackborna1940212012-06-28 16:07:22 -07001023 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001024 final View target = root.findViewById(viewId);
Joe Onorato2b69ce42010-10-31 11:35:41 -07001025 if (target == null) return;
Jim Millere667a7a2012-08-09 19:22:32 -07001026
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001027 // Pick the correct drawable to modify for this view
1028 Drawable targetDrawable = null;
1029 if (targetBackground) {
1030 targetDrawable = target.getBackground();
1031 } else if (target instanceof ImageView) {
1032 ImageView imageView = (ImageView) target;
1033 targetDrawable = imageView.getDrawable();
1034 }
Jim Millere667a7a2012-08-09 19:22:32 -07001035
Romain Guya5475592009-07-01 17:20:08 -07001036 if (targetDrawable != null) {
Sunny Goyal5b153922017-09-21 21:00:36 -07001037 targetDrawable.mutate().setColorFilter(colorFilter, filterMode);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001038 }
1039 }
Winson Chung3ec9a452010-09-23 16:40:28 -07001040
Sunny Goyal5b153922017-09-21 21:00:36 -07001041 @Override
1042 public int getActionTag() {
1043 return SET_DRAWABLE_TINT_TAG;
Adam Cohenfbe44b72012-09-19 20:36:23 -07001044 }
1045
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001046 boolean targetBackground;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001047 int colorFilter;
1048 PorterDuff.Mode filterMode;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001049 }
Jim Millere667a7a2012-08-09 19:22:32 -07001050
Gus Prevas9cc96602018-10-11 11:24:23 -04001051 /**
1052 * Equivalent to calling
1053 * {@link RippleDrawable#setColor(ColorStateList)},
1054 * on the {@link Drawable} of a given view.
1055 * <p>
1056 * The operation will be performed on the {@link Drawable} returned by the
1057 * target {@link View#getBackground()}.
1058 * <p>
1059 */
1060 private class SetRippleDrawableColor extends Action {
1061
1062 ColorStateList mColorStateList;
1063
1064 SetRippleDrawableColor(int id, ColorStateList colorStateList) {
1065 this.viewId = id;
1066 this.mColorStateList = colorStateList;
1067 }
1068
1069 SetRippleDrawableColor(Parcel parcel) {
1070 viewId = parcel.readInt();
1071 mColorStateList = parcel.readParcelable(null);
1072 }
1073
1074 public void writeToParcel(Parcel dest, int flags) {
1075 dest.writeInt(viewId);
1076 dest.writeParcelable(mColorStateList, 0);
1077 }
1078
1079 @Override
1080 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
1081 final View target = root.findViewById(viewId);
1082 if (target == null) return;
1083
1084 // Pick the correct drawable to modify for this view
1085 Drawable targetDrawable = target.getBackground();
1086
1087 if (targetDrawable instanceof RippleDrawable) {
1088 ((RippleDrawable) targetDrawable.mutate()).setColor(mColorStateList);
1089 }
1090 }
1091
1092 @Override
1093 public int getActionTag() {
1094 return SET_RIPPLE_DRAWABLE_COLOR_TAG;
1095 }
1096 }
1097
Sunny Goyal5b153922017-09-21 21:00:36 -07001098 private final class ViewContentNavigation extends Action {
1099 final boolean mNext;
Adam Cohen2dd21972010-08-15 18:20:04 -07001100
Sunny Goyal5b153922017-09-21 21:00:36 -07001101 ViewContentNavigation(int viewId, boolean next) {
Adam Cohen2dd21972010-08-15 18:20:04 -07001102 this.viewId = viewId;
Sunny Goyal5b153922017-09-21 21:00:36 -07001103 this.mNext = next;
Adam Cohen2dd21972010-08-15 18:20:04 -07001104 }
1105
Sunny Goyal5b153922017-09-21 21:00:36 -07001106 ViewContentNavigation(Parcel in) {
Adam Cohen2dd21972010-08-15 18:20:04 -07001107 this.viewId = in.readInt();
Sunny Goyal5b153922017-09-21 21:00:36 -07001108 this.mNext = in.readBoolean();
Adam Cohen2dd21972010-08-15 18:20:04 -07001109 }
1110
1111 public void writeToParcel(Parcel out, int flags) {
Adam Cohen2dd21972010-08-15 18:20:04 -07001112 out.writeInt(this.viewId);
Sunny Goyal5b153922017-09-21 21:00:36 -07001113 out.writeBoolean(this.mNext);
Adam Cohen2dd21972010-08-15 18:20:04 -07001114 }
1115
1116 @Override
Dianne Hackborna1940212012-06-28 16:07:22 -07001117 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
Adam Cohen2dd21972010-08-15 18:20:04 -07001118 final View view = root.findViewById(viewId);
Joe Onorato2b69ce42010-10-31 11:35:41 -07001119 if (view == null) return;
Adam Cohen2dd21972010-08-15 18:20:04 -07001120
Adam Cohen2dd21972010-08-15 18:20:04 -07001121 try {
Sunny Goyal5b153922017-09-21 21:00:36 -07001122 getMethod(view,
1123 mNext ? "showNext" : "showPrevious", null, false /* async */).invoke(view);
Sunny Goyal271e3222017-08-29 16:05:47 -07001124 } catch (Throwable ex) {
Adam Cohen2dd21972010-08-15 18:20:04 -07001125 throw new ActionException(ex);
1126 }
1127 }
Adam Cohenfbe44b72012-09-19 20:36:23 -07001128
1129 public int mergeBehavior() {
Sunny Goyal5b153922017-09-21 21:00:36 -07001130 return MERGE_IGNORE;
Adam Cohenfbe44b72012-09-19 20:36:23 -07001131 }
1132
Sunny Goyal5b153922017-09-21 21:00:36 -07001133 @Override
1134 public int getActionTag() {
1135 return VIEW_CONTENT_NAVIGATION_TAG;
Adam Cohenfbe44b72012-09-19 20:36:23 -07001136 }
Adam Cohen2dd21972010-08-15 18:20:04 -07001137 }
1138
Adam Cohen5d200642012-04-24 10:43:31 -07001139 private static class BitmapCache {
Sunny Goyal56333a82017-08-29 13:46:29 -07001140
Mathew Inwood3a75f262019-06-27 12:47:38 +01001141 @UnsupportedAppUsage
Adam Cohen5d200642012-04-24 10:43:31 -07001142 ArrayList<Bitmap> mBitmaps;
Sunny Goyal56333a82017-08-29 13:46:29 -07001143 int mBitmapMemory = -1;
Adam Cohen5d200642012-04-24 10:43:31 -07001144
1145 public BitmapCache() {
Sunny Goyal56333a82017-08-29 13:46:29 -07001146 mBitmaps = new ArrayList<>();
Adam Cohen5d200642012-04-24 10:43:31 -07001147 }
1148
1149 public BitmapCache(Parcel source) {
Sunny Goyal5b153922017-09-21 21:00:36 -07001150 mBitmaps = source.createTypedArrayList(Bitmap.CREATOR);
Adam Cohen5d200642012-04-24 10:43:31 -07001151 }
1152
1153 public int getBitmapId(Bitmap b) {
1154 if (b == null) {
1155 return -1;
1156 } else {
1157 if (mBitmaps.contains(b)) {
1158 return mBitmaps.indexOf(b);
1159 } else {
1160 mBitmaps.add(b);
Sunny Goyal56333a82017-08-29 13:46:29 -07001161 mBitmapMemory = -1;
Adam Cohen5d200642012-04-24 10:43:31 -07001162 return (mBitmaps.size() - 1);
1163 }
1164 }
1165 }
1166
1167 public Bitmap getBitmapForId(int id) {
1168 if (id == -1 || id >= mBitmaps.size()) {
1169 return null;
1170 } else {
1171 return mBitmaps.get(id);
1172 }
1173 }
1174
1175 public void writeBitmapsToParcel(Parcel dest, int flags) {
Sunny Goyal5b153922017-09-21 21:00:36 -07001176 dest.writeTypedList(mBitmaps, flags);
Adam Cohen5d200642012-04-24 10:43:31 -07001177 }
1178
Sunny Goyal56333a82017-08-29 13:46:29 -07001179 public int getBitmapMemory() {
1180 if (mBitmapMemory < 0) {
1181 mBitmapMemory = 0;
1182 int count = mBitmaps.size();
1183 for (int i = 0; i < count; i++) {
1184 mBitmapMemory += mBitmaps.get(i).getAllocationByteCount();
Adam Cohen5d200642012-04-24 10:43:31 -07001185 }
1186 }
Sunny Goyal56333a82017-08-29 13:46:29 -07001187 return mBitmapMemory;
Adrian Roos7da889d2016-03-16 18:38:58 -07001188 }
Adam Cohen5d200642012-04-24 10:43:31 -07001189 }
1190
1191 private class BitmapReflectionAction extends Action {
1192 int bitmapId;
Mathew Inwood3a75f262019-06-27 12:47:38 +01001193 @UnsupportedAppUsage
Adam Cohen5d200642012-04-24 10:43:31 -07001194 Bitmap bitmap;
Mathew Inwood3a75f262019-06-27 12:47:38 +01001195 @UnsupportedAppUsage
Adam Cohen5d200642012-04-24 10:43:31 -07001196 String methodName;
1197
1198 BitmapReflectionAction(int viewId, String methodName, Bitmap bitmap) {
1199 this.bitmap = bitmap;
1200 this.viewId = viewId;
1201 this.methodName = methodName;
1202 bitmapId = mBitmapCache.getBitmapId(bitmap);
1203 }
1204
1205 BitmapReflectionAction(Parcel in) {
1206 viewId = in.readInt();
1207 methodName = in.readString();
1208 bitmapId = in.readInt();
1209 bitmap = mBitmapCache.getBitmapForId(bitmapId);
1210 }
1211
1212 @Override
1213 public void writeToParcel(Parcel dest, int flags) {
Adam Cohen5d200642012-04-24 10:43:31 -07001214 dest.writeInt(viewId);
1215 dest.writeString(methodName);
1216 dest.writeInt(bitmapId);
1217 }
1218
1219 @Override
Dianne Hackborna1940212012-06-28 16:07:22 -07001220 public void apply(View root, ViewGroup rootParent,
1221 OnClickHandler handler) throws ActionException {
Adam Cohen5d200642012-04-24 10:43:31 -07001222 ReflectionAction ra = new ReflectionAction(viewId, methodName, ReflectionAction.BITMAP,
1223 bitmap);
Dianne Hackborna1940212012-06-28 16:07:22 -07001224 ra.apply(root, rootParent, handler);
Adam Cohen5d200642012-04-24 10:43:31 -07001225 }
1226
1227 @Override
1228 public void setBitmapCache(BitmapCache bitmapCache) {
1229 bitmapId = bitmapCache.getBitmapId(bitmap);
1230 }
1231
Sunny Goyal5b153922017-09-21 21:00:36 -07001232 @Override
1233 public int getActionTag() {
1234 return BITMAP_REFLECTION_ACTION_TAG;
Adam Cohenfbe44b72012-09-19 20:36:23 -07001235 }
Adam Cohen5d200642012-04-24 10:43:31 -07001236 }
1237
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001238 /**
1239 * Base class for the reflection actions.
1240 */
Romain Guy484f4d62013-07-22 16:39:16 -07001241 private final class ReflectionAction extends Action {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001242 static final int BOOLEAN = 1;
1243 static final int BYTE = 2;
1244 static final int SHORT = 3;
1245 static final int INT = 4;
1246 static final int LONG = 5;
1247 static final int FLOAT = 6;
1248 static final int DOUBLE = 7;
1249 static final int CHAR = 8;
1250 static final int STRING = 9;
1251 static final int CHAR_SEQUENCE = 10;
1252 static final int URI = 11;
Adam Cohenfbe44b72012-09-19 20:36:23 -07001253 // BITMAP actions are never stored in the list of actions. They are only used locally
1254 // to implement BitmapReflectionAction, which eliminates duplicates using BitmapCache.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001255 static final int BITMAP = 12;
Bjorn Bringertd755b062010-01-06 17:15:37 +00001256 static final int BUNDLE = 13;
Winson Chung499cb9f2010-07-16 11:18:17 -07001257 static final int INTENT = 14;
Jorim Jaggief72a192014-08-26 21:57:46 +02001258 static final int COLOR_STATE_LIST = 15;
Dan Sandlera22a3802015-05-13 00:12:47 -04001259 static final int ICON = 16;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001260
Mathew Inwood3a75f262019-06-27 12:47:38 +01001261 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001262 String methodName;
1263 int type;
Mathew Inwood3a75f262019-06-27 12:47:38 +01001264 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001265 Object value;
1266
1267 ReflectionAction(int viewId, String methodName, int type, Object value) {
1268 this.viewId = viewId;
1269 this.methodName = methodName;
1270 this.type = type;
1271 this.value = value;
1272 }
1273
1274 ReflectionAction(Parcel in) {
1275 this.viewId = in.readInt();
1276 this.methodName = in.readString();
1277 this.type = in.readInt();
Romain Guya5475592009-07-01 17:20:08 -07001278 //noinspection ConstantIfStatement
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001279 if (false) {
Adam Cohen50f3d1b2012-12-11 18:36:07 -08001280 Log.d(LOG_TAG, "read viewId=0x" + Integer.toHexString(this.viewId)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001281 + " methodName=" + this.methodName + " type=" + this.type);
1282 }
Adam Cohenc6151f22012-02-02 21:02:31 -08001283
1284 // For some values that may have been null, we first check a flag to see if they were
1285 // written to the parcel.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001286 switch (this.type) {
1287 case BOOLEAN:
Sunny Goyal5b153922017-09-21 21:00:36 -07001288 this.value = in.readBoolean();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001289 break;
1290 case BYTE:
1291 this.value = in.readByte();
1292 break;
1293 case SHORT:
1294 this.value = (short)in.readInt();
1295 break;
1296 case INT:
1297 this.value = in.readInt();
1298 break;
1299 case LONG:
1300 this.value = in.readLong();
1301 break;
1302 case FLOAT:
1303 this.value = in.readFloat();
1304 break;
1305 case DOUBLE:
1306 this.value = in.readDouble();
1307 break;
1308 case CHAR:
1309 this.value = (char)in.readInt();
1310 break;
1311 case STRING:
1312 this.value = in.readString();
1313 break;
1314 case CHAR_SEQUENCE:
1315 this.value = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
1316 break;
1317 case URI:
Sunny Goyal5b153922017-09-21 21:00:36 -07001318 this.value = in.readTypedObject(Uri.CREATOR);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001319 break;
1320 case BITMAP:
Sunny Goyal5b153922017-09-21 21:00:36 -07001321 this.value = in.readTypedObject(Bitmap.CREATOR);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001322 break;
Bjorn Bringertd755b062010-01-06 17:15:37 +00001323 case BUNDLE:
1324 this.value = in.readBundle();
1325 break;
Winson Chung499cb9f2010-07-16 11:18:17 -07001326 case INTENT:
Sunny Goyal5b153922017-09-21 21:00:36 -07001327 this.value = in.readTypedObject(Intent.CREATOR);
Winson Chung499cb9f2010-07-16 11:18:17 -07001328 break;
Jorim Jaggief72a192014-08-26 21:57:46 +02001329 case COLOR_STATE_LIST:
Sunny Goyal5b153922017-09-21 21:00:36 -07001330 this.value = in.readTypedObject(ColorStateList.CREATOR);
Jorim Jaggief72a192014-08-26 21:57:46 +02001331 break;
Dan Sandlera22a3802015-05-13 00:12:47 -04001332 case ICON:
Sunny Goyal5b153922017-09-21 21:00:36 -07001333 this.value = in.readTypedObject(Icon.CREATOR);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001334 default:
1335 break;
1336 }
1337 }
1338
1339 public void writeToParcel(Parcel out, int flags) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001340 out.writeInt(this.viewId);
1341 out.writeString(this.methodName);
1342 out.writeInt(this.type);
Romain Guya5475592009-07-01 17:20:08 -07001343 //noinspection ConstantIfStatement
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001344 if (false) {
Adam Cohen50f3d1b2012-12-11 18:36:07 -08001345 Log.d(LOG_TAG, "write viewId=0x" + Integer.toHexString(this.viewId)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001346 + " methodName=" + this.methodName + " type=" + this.type);
1347 }
Adam Cohenc6151f22012-02-02 21:02:31 -08001348
1349 // For some values which are null, we record an integer flag to indicate whether
1350 // we have written a valid value to the parcel.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001351 switch (this.type) {
1352 case BOOLEAN:
Sunny Goyal5b153922017-09-21 21:00:36 -07001353 out.writeBoolean((Boolean) this.value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001354 break;
1355 case BYTE:
Romain Guya5475592009-07-01 17:20:08 -07001356 out.writeByte((Byte) this.value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001357 break;
1358 case SHORT:
Romain Guya5475592009-07-01 17:20:08 -07001359 out.writeInt((Short) this.value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001360 break;
1361 case INT:
Romain Guya5475592009-07-01 17:20:08 -07001362 out.writeInt((Integer) this.value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001363 break;
1364 case LONG:
Romain Guya5475592009-07-01 17:20:08 -07001365 out.writeLong((Long) this.value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001366 break;
1367 case FLOAT:
Romain Guya5475592009-07-01 17:20:08 -07001368 out.writeFloat((Float) this.value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001369 break;
1370 case DOUBLE:
Romain Guya5475592009-07-01 17:20:08 -07001371 out.writeDouble((Double) this.value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001372 break;
1373 case CHAR:
1374 out.writeInt((int)((Character)this.value).charValue());
1375 break;
1376 case STRING:
1377 out.writeString((String)this.value);
1378 break;
1379 case CHAR_SEQUENCE:
Jim Millere667a7a2012-08-09 19:22:32 -07001380 TextUtils.writeToParcel((CharSequence)this.value, out, flags);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001381 break;
Bjorn Bringertd755b062010-01-06 17:15:37 +00001382 case BUNDLE:
1383 out.writeBundle((Bundle) this.value);
1384 break;
Sunny Goyal56333a82017-08-29 13:46:29 -07001385 case URI:
1386 case BITMAP:
Winson Chung499cb9f2010-07-16 11:18:17 -07001387 case INTENT:
Jorim Jaggief72a192014-08-26 21:57:46 +02001388 case COLOR_STATE_LIST:
Dan Sandlera22a3802015-05-13 00:12:47 -04001389 case ICON:
Sunny Goyal5b153922017-09-21 21:00:36 -07001390 out.writeTypedObject((Parcelable) this.value, flags);
Dan Sandlera22a3802015-05-13 00:12:47 -04001391 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001392 default:
1393 break;
1394 }
1395 }
1396
Romain Guye4d4e202013-07-22 13:02:02 -07001397 private Class<?> getParameterType() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001398 switch (this.type) {
1399 case BOOLEAN:
1400 return boolean.class;
1401 case BYTE:
1402 return byte.class;
1403 case SHORT:
1404 return short.class;
1405 case INT:
1406 return int.class;
1407 case LONG:
1408 return long.class;
1409 case FLOAT:
1410 return float.class;
1411 case DOUBLE:
1412 return double.class;
1413 case CHAR:
1414 return char.class;
1415 case STRING:
1416 return String.class;
1417 case CHAR_SEQUENCE:
1418 return CharSequence.class;
1419 case URI:
1420 return Uri.class;
1421 case BITMAP:
1422 return Bitmap.class;
Bjorn Bringertd755b062010-01-06 17:15:37 +00001423 case BUNDLE:
1424 return Bundle.class;
Winson Chung499cb9f2010-07-16 11:18:17 -07001425 case INTENT:
1426 return Intent.class;
Jorim Jaggief72a192014-08-26 21:57:46 +02001427 case COLOR_STATE_LIST:
1428 return ColorStateList.class;
Dan Sandlera22a3802015-05-13 00:12:47 -04001429 case ICON:
1430 return Icon.class;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001431 default:
1432 return null;
1433 }
1434 }
1435
1436 @Override
Dianne Hackborna1940212012-06-28 16:07:22 -07001437 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001438 final View view = root.findViewById(viewId);
Joe Onorato2b69ce42010-10-31 11:35:41 -07001439 if (view == null) return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001440
Romain Guye4d4e202013-07-22 13:02:02 -07001441 Class<?> param = getParameterType();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001442 if (param == null) {
1443 throw new ActionException("bad type: " + this.type);
1444 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001445 try {
Sunny Goyal271e3222017-08-29 16:05:47 -07001446 getMethod(view, this.methodName, param, false /* async */).invoke(view, this.value);
1447 } catch (Throwable ex) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001448 throw new ActionException(ex);
1449 }
1450 }
Winson Chung3ec9a452010-09-23 16:40:28 -07001451
Sunny Goyaldd292f42015-12-02 14:29:27 -08001452 @Override
1453 public Action initActionAsync(ViewTree root, ViewGroup rootParent, OnClickHandler handler) {
1454 final View view = root.findViewById(viewId);
1455 if (view == null) return ACTION_NOOP;
1456
1457 Class<?> param = getParameterType();
1458 if (param == null) {
1459 throw new ActionException("bad type: " + this.type);
1460 }
1461
1462 try {
Sunny Goyal271e3222017-08-29 16:05:47 -07001463 MethodHandle method = getMethod(view, this.methodName, param, true /* async */);
Sunny Goyaldd292f42015-12-02 14:29:27 -08001464
Sunny Goyal271e3222017-08-29 16:05:47 -07001465 if (method != null) {
1466 Runnable endAction = (Runnable) method.invoke(view, this.value);
Sunny Goyaldd292f42015-12-02 14:29:27 -08001467 if (endAction == null) {
1468 return ACTION_NOOP;
1469 } else {
Sunny Goyal7b0e2c72016-11-03 14:48:05 -07001470 // Special case view stub
1471 if (endAction instanceof ViewStub.ViewReplaceRunnable) {
1472 root.createTree();
1473 // Replace child tree
1474 root.findViewTreeById(viewId).replaceView(
1475 ((ViewStub.ViewReplaceRunnable) endAction).view);
1476 }
Sunny Goyaldd292f42015-12-02 14:29:27 -08001477 return new RunnableAction(endAction);
1478 }
1479 }
Sunny Goyal271e3222017-08-29 16:05:47 -07001480 } catch (Throwable ex) {
Sunny Goyaldd292f42015-12-02 14:29:27 -08001481 throw new ActionException(ex);
1482 }
1483
1484 return this;
1485 }
1486
Adam Cohenfbe44b72012-09-19 20:36:23 -07001487 public int mergeBehavior() {
1488 // smoothScrollBy is cumulative, everything else overwites.
1489 if (methodName.equals("smoothScrollBy")) {
1490 return MERGE_APPEND;
1491 } else {
1492 return MERGE_REPLACE;
Winson Chung3ec9a452010-09-23 16:40:28 -07001493 }
1494 }
Adam Cohenfbe44b72012-09-19 20:36:23 -07001495
Sunny Goyal5b153922017-09-21 21:00:36 -07001496 @Override
1497 public int getActionTag() {
1498 return REFLECTION_ACTION_TAG;
1499 }
1500
1501 @Override
1502 public String getUniqueKey() {
Adam Cohenfbe44b72012-09-19 20:36:23 -07001503 // Each type of reflection action corresponds to a setter, so each should be seen as
1504 // unique from the standpoint of merging.
Sunny Goyal5b153922017-09-21 21:00:36 -07001505 return super.getUniqueKey() + this.methodName + this.type;
Adam Cohenfbe44b72012-09-19 20:36:23 -07001506 }
Sunny Goyal5c022632016-02-17 16:30:41 -08001507
1508 @Override
1509 public boolean prefersAsyncApply() {
1510 return this.type == URI || this.type == ICON;
1511 }
Jeff Sharkey23b31182018-04-18 21:32:12 -06001512
1513 @Override
1514 public void visitUris(@NonNull Consumer<Uri> visitor) {
1515 switch (this.type) {
1516 case URI:
1517 final Uri uri = (Uri) this.value;
1518 visitor.accept(uri);
1519 break;
1520 case ICON:
1521 final Icon icon = (Icon) this.value;
1522 visitIconUri(icon, visitor);
1523 break;
1524 }
1525 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001526 }
1527
Sunny Goyaldd292f42015-12-02 14:29:27 -08001528 /**
1529 * This is only used for async execution of actions and it not parcelable.
1530 */
1531 private static final class RunnableAction extends RuntimeAction {
1532 private final Runnable mRunnable;
1533
1534 RunnableAction(Runnable r) {
1535 mRunnable = r;
1536 }
1537
1538 @Override
1539 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
1540 mRunnable.run();
1541 }
1542 }
1543
Adam Cohen5d200642012-04-24 10:43:31 -07001544 private void configureRemoteViewsAsChild(RemoteViews rv) {
Adam Cohen5d200642012-04-24 10:43:31 -07001545 rv.setBitmapCache(mBitmapCache);
1546 rv.setNotRoot();
1547 }
1548
1549 void setNotRoot() {
1550 mIsRoot = false;
1551 }
1552
Jeff Sharkey1162fd72009-11-04 17:58:08 -08001553 /**
Anthony Chen8f5f3582017-04-11 11:18:37 -07001554 * ViewGroup methods that are related to adding Views.
Jeff Sharkey1162fd72009-11-04 17:58:08 -08001555 */
Anthony Chen8f5f3582017-04-11 11:18:37 -07001556 private class ViewGroupActionAdd extends Action {
Mathew Inwood3a75f262019-06-27 12:47:38 +01001557 @UnsupportedAppUsage
Anthony Chen8f5f3582017-04-11 11:18:37 -07001558 private RemoteViews mNestedViews;
1559 private int mIndex;
1560
1561 ViewGroupActionAdd(int viewId, RemoteViews nestedViews) {
1562 this(viewId, nestedViews, -1 /* index */);
1563 }
1564
1565 ViewGroupActionAdd(int viewId, RemoteViews nestedViews, int index) {
Jeff Sharkey1162fd72009-11-04 17:58:08 -08001566 this.viewId = viewId;
Anthony Chen8f5f3582017-04-11 11:18:37 -07001567 mNestedViews = nestedViews;
1568 mIndex = index;
Adam Cohenc431c152012-04-26 18:42:17 -07001569 if (nestedViews != null) {
1570 configureRemoteViewsAsChild(nestedViews);
1571 }
Jeff Sharkey1162fd72009-11-04 17:58:08 -08001572 }
1573
Anthony Chen8f5f3582017-04-11 11:18:37 -07001574 ViewGroupActionAdd(Parcel parcel, BitmapCache bitmapCache, ApplicationInfo info,
Adrian Roosfb921842017-10-26 14:49:56 +02001575 int depth, Map<Class, Object> classCookies) {
Jeff Sharkey1162fd72009-11-04 17:58:08 -08001576 viewId = parcel.readInt();
Anthony Chenea202f62017-07-05 11:22:25 -07001577 mIndex = parcel.readInt();
Adrian Roosfb921842017-10-26 14:49:56 +02001578 mNestedViews = new RemoteViews(parcel, bitmapCache, info, depth, classCookies);
Sunny Goyalc12d31c2018-11-12 16:29:18 -08001579 mNestedViews.addFlags(mApplyFlags);
Jeff Sharkey1162fd72009-11-04 17:58:08 -08001580 }
1581
1582 public void writeToParcel(Parcel dest, int flags) {
Jeff Sharkey1162fd72009-11-04 17:58:08 -08001583 dest.writeInt(viewId);
Anthony Chenea202f62017-07-05 11:22:25 -07001584 dest.writeInt(mIndex);
Anthony Chen8f5f3582017-04-11 11:18:37 -07001585 mNestedViews.writeToParcel(dest, flags);
Jeff Sharkey1162fd72009-11-04 17:58:08 -08001586 }
1587
1588 @Override
Sunny Goyal5d8bcdf2016-10-27 16:11:29 -07001589 public boolean hasSameAppInfo(ApplicationInfo parentInfo) {
Sunny Goyaldd60f4d2017-10-18 15:22:42 -07001590 return mNestedViews.hasSameAppInfo(parentInfo);
Sunny Goyal5d8bcdf2016-10-27 16:11:29 -07001591 }
1592
1593 @Override
Dianne Hackborna1940212012-06-28 16:07:22 -07001594 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
Jeff Sharkey1162fd72009-11-04 17:58:08 -08001595 final Context context = root.getContext();
Alan Viverette8e1a7292017-02-27 10:57:58 -05001596 final ViewGroup target = root.findViewById(viewId);
Anthony Chen8f5f3582017-04-11 11:18:37 -07001597
1598 if (target == null) {
1599 return;
Jeff Sharkey1162fd72009-11-04 17:58:08 -08001600 }
Anthony Chen8f5f3582017-04-11 11:18:37 -07001601
1602 // Inflate nested views and add as children
1603 target.addView(mNestedViews.apply(context, target, handler), mIndex);
Jeff Sharkey1162fd72009-11-04 17:58:08 -08001604 }
1605
Winson Chung3ec9a452010-09-23 16:40:28 -07001606 @Override
Sunny Goyaldd292f42015-12-02 14:29:27 -08001607 public Action initActionAsync(ViewTree root, ViewGroup rootParent, OnClickHandler handler) {
1608 // In the async implementation, update the view tree so that subsequent calls to
Anthony Chen8f5f3582017-04-11 11:18:37 -07001609 // findViewById return the current view.
Sunny Goyaldd292f42015-12-02 14:29:27 -08001610 root.createTree();
1611 ViewTree target = root.findViewTreeById(viewId);
1612 if ((target == null) || !(target.mRoot instanceof ViewGroup)) {
1613 return ACTION_NOOP;
1614 }
Sunny Goyal7b0e2c72016-11-03 14:48:05 -07001615 final ViewGroup targetVg = (ViewGroup) target.mRoot;
Sunny Goyaldd292f42015-12-02 14:29:27 -08001616
Anthony Chen8f5f3582017-04-11 11:18:37 -07001617 // Inflate nested views and perform all the async tasks for the child remoteView.
1618 final Context context = root.mRoot.getContext();
1619 final AsyncApplyTask task = mNestedViews.getAsyncApplyTask(
1620 context, targetVg, null, handler);
1621 final ViewTree tree = task.doInBackground();
Sunny Goyaldd292f42015-12-02 14:29:27 -08001622
Anthony Chen8f5f3582017-04-11 11:18:37 -07001623 if (tree == null) {
1624 throw new ActionException(task.mError);
Sunny Goyaldd292f42015-12-02 14:29:27 -08001625 }
Anthony Chen8f5f3582017-04-11 11:18:37 -07001626
1627 // Update the global view tree, so that next call to findViewTreeById
1628 // goes through the subtree as well.
1629 target.addChild(tree, mIndex);
1630
1631 return new RuntimeAction() {
1632 @Override
1633 public void apply(View root, ViewGroup rootParent, OnClickHandler handler)
1634 throws ActionException {
1635 task.onPostExecute(tree);
1636 targetVg.addView(task.mResult, mIndex);
1637 }
1638 };
Sunny Goyaldd292f42015-12-02 14:29:27 -08001639 }
1640
1641 @Override
Adam Cohen5d200642012-04-24 10:43:31 -07001642 public void setBitmapCache(BitmapCache bitmapCache) {
Anthony Chen8f5f3582017-04-11 11:18:37 -07001643 mNestedViews.setBitmapCache(bitmapCache);
Winson Chung3ec9a452010-09-23 16:40:28 -07001644 }
1645
Anthony Chen8f5f3582017-04-11 11:18:37 -07001646 @Override
Adam Cohenfbe44b72012-09-19 20:36:23 -07001647 public int mergeBehavior() {
1648 return MERGE_APPEND;
1649 }
1650
Sunny Goyal5c022632016-02-17 16:30:41 -08001651 @Override
1652 public boolean prefersAsyncApply() {
Anthony Chen8f5f3582017-04-11 11:18:37 -07001653 return mNestedViews.prefersAsyncApply();
Sunny Goyal5c022632016-02-17 16:30:41 -08001654 }
1655
Anthony Chen8f5f3582017-04-11 11:18:37 -07001656 @Override
Sunny Goyal5b153922017-09-21 21:00:36 -07001657 public int getActionTag() {
1658 return VIEW_GROUP_ACTION_ADD_TAG;
Anthony Chen8f5f3582017-04-11 11:18:37 -07001659 }
1660 }
1661
1662 /**
1663 * ViewGroup methods related to removing child views.
1664 */
1665 private class ViewGroupActionRemove extends Action {
1666 /**
1667 * Id that indicates that all child views of the affected ViewGroup should be removed.
1668 *
1669 * <p>Using -2 because the default id is -1. This avoids accidentally matching that.
1670 */
1671 private static final int REMOVE_ALL_VIEWS_ID = -2;
1672
1673 private int mViewIdToKeep;
1674
1675 ViewGroupActionRemove(int viewId) {
1676 this(viewId, REMOVE_ALL_VIEWS_ID);
1677 }
1678
1679 ViewGroupActionRemove(int viewId, int viewIdToKeep) {
1680 this.viewId = viewId;
1681 mViewIdToKeep = viewIdToKeep;
1682 }
1683
1684 ViewGroupActionRemove(Parcel parcel) {
1685 viewId = parcel.readInt();
1686 mViewIdToKeep = parcel.readInt();
1687 }
1688
1689 public void writeToParcel(Parcel dest, int flags) {
Anthony Chen8f5f3582017-04-11 11:18:37 -07001690 dest.writeInt(viewId);
1691 dest.writeInt(mViewIdToKeep);
1692 }
1693
1694 @Override
1695 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
1696 final ViewGroup target = root.findViewById(viewId);
1697
1698 if (target == null) {
1699 return;
1700 }
1701
1702 if (mViewIdToKeep == REMOVE_ALL_VIEWS_ID) {
1703 target.removeAllViews();
1704 return;
1705 }
1706
1707 removeAllViewsExceptIdToKeep(target);
1708 }
1709
1710 @Override
1711 public Action initActionAsync(ViewTree root, ViewGroup rootParent, OnClickHandler handler) {
1712 // In the async implementation, update the view tree so that subsequent calls to
1713 // findViewById return the current view.
1714 root.createTree();
1715 ViewTree target = root.findViewTreeById(viewId);
1716
1717 if ((target == null) || !(target.mRoot instanceof ViewGroup)) {
1718 return ACTION_NOOP;
1719 }
1720
1721 final ViewGroup targetVg = (ViewGroup) target.mRoot;
1722
1723 // Clear all children when nested views omitted
1724 target.mChildren = null;
1725 return new RuntimeAction() {
1726 @Override
1727 public void apply(View root, ViewGroup rootParent, OnClickHandler handler)
1728 throws ActionException {
1729 if (mViewIdToKeep == REMOVE_ALL_VIEWS_ID) {
1730 targetVg.removeAllViews();
1731 return;
1732 }
1733
1734 removeAllViewsExceptIdToKeep(targetVg);
1735 }
1736 };
1737 }
1738
1739 /**
1740 * Iterates through the children in the given ViewGroup and removes all the views that
1741 * do not have an id of {@link #mViewIdToKeep}.
1742 */
1743 private void removeAllViewsExceptIdToKeep(ViewGroup viewGroup) {
1744 // Otherwise, remove all the views that do not match the id to keep.
1745 int index = viewGroup.getChildCount() - 1;
1746 while (index >= 0) {
1747 if (viewGroup.getChildAt(index).getId() != mViewIdToKeep) {
1748 viewGroup.removeViewAt(index);
1749 }
1750 index--;
1751 }
1752 }
1753
1754 @Override
Sunny Goyal5b153922017-09-21 21:00:36 -07001755 public int getActionTag() {
1756 return VIEW_GROUP_ACTION_REMOVE_TAG;
Anthony Chen8f5f3582017-04-11 11:18:37 -07001757 }
1758
1759 @Override
1760 public int mergeBehavior() {
1761 return MERGE_APPEND;
1762 }
Jeff Sharkey1162fd72009-11-04 17:58:08 -08001763 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001764
1765 /**
Daniel Sandler820ba322012-03-23 16:36:00 -05001766 * Helper action to set compound drawables on a TextView. Supports relative
1767 * (s/t/e/b) or cardinal (l/t/r/b) arrangement.
1768 */
1769 private class TextViewDrawableAction extends Action {
1770 public TextViewDrawableAction(int viewId, boolean isRelative, int d1, int d2, int d3, int d4) {
1771 this.viewId = viewId;
1772 this.isRelative = isRelative;
Dan Sandler912282e2015-07-28 22:49:30 -04001773 this.useIcons = false;
Daniel Sandler820ba322012-03-23 16:36:00 -05001774 this.d1 = d1;
1775 this.d2 = d2;
1776 this.d3 = d3;
1777 this.d4 = d4;
1778 }
1779
Dan Sandler912282e2015-07-28 22:49:30 -04001780 public TextViewDrawableAction(int viewId, boolean isRelative,
1781 Icon i1, Icon i2, Icon i3, Icon i4) {
1782 this.viewId = viewId;
1783 this.isRelative = isRelative;
1784 this.useIcons = true;
1785 this.i1 = i1;
1786 this.i2 = i2;
1787 this.i3 = i3;
1788 this.i4 = i4;
1789 }
1790
Daniel Sandler820ba322012-03-23 16:36:00 -05001791 public TextViewDrawableAction(Parcel parcel) {
1792 viewId = parcel.readInt();
1793 isRelative = (parcel.readInt() != 0);
Dan Sandler912282e2015-07-28 22:49:30 -04001794 useIcons = (parcel.readInt() != 0);
1795 if (useIcons) {
Sunny Goyal5b153922017-09-21 21:00:36 -07001796 i1 = parcel.readTypedObject(Icon.CREATOR);
1797 i2 = parcel.readTypedObject(Icon.CREATOR);
1798 i3 = parcel.readTypedObject(Icon.CREATOR);
1799 i4 = parcel.readTypedObject(Icon.CREATOR);
Dan Sandler912282e2015-07-28 22:49:30 -04001800 } else {
1801 d1 = parcel.readInt();
1802 d2 = parcel.readInt();
1803 d3 = parcel.readInt();
1804 d4 = parcel.readInt();
1805 }
Daniel Sandler820ba322012-03-23 16:36:00 -05001806 }
1807
1808 public void writeToParcel(Parcel dest, int flags) {
Daniel Sandler820ba322012-03-23 16:36:00 -05001809 dest.writeInt(viewId);
1810 dest.writeInt(isRelative ? 1 : 0);
Dan Sandler912282e2015-07-28 22:49:30 -04001811 dest.writeInt(useIcons ? 1 : 0);
1812 if (useIcons) {
Sunny Goyal5b153922017-09-21 21:00:36 -07001813 dest.writeTypedObject(i1, 0);
1814 dest.writeTypedObject(i2, 0);
1815 dest.writeTypedObject(i3, 0);
1816 dest.writeTypedObject(i4, 0);
Dan Sandler912282e2015-07-28 22:49:30 -04001817 } else {
1818 dest.writeInt(d1);
1819 dest.writeInt(d2);
1820 dest.writeInt(d3);
1821 dest.writeInt(d4);
1822 }
Daniel Sandler820ba322012-03-23 16:36:00 -05001823 }
1824
1825 @Override
Dianne Hackborna1940212012-06-28 16:07:22 -07001826 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
Alan Viverette8e1a7292017-02-27 10:57:58 -05001827 final TextView target = root.findViewById(viewId);
Daniel Sandler820ba322012-03-23 16:36:00 -05001828 if (target == null) return;
Sunny Goyaldd292f42015-12-02 14:29:27 -08001829 if (drawablesLoaded) {
1830 if (isRelative) {
1831 target.setCompoundDrawablesRelativeWithIntrinsicBounds(id1, id2, id3, id4);
1832 } else {
1833 target.setCompoundDrawablesWithIntrinsicBounds(id1, id2, id3, id4);
1834 }
1835 } else if (useIcons) {
Dan Sandler912282e2015-07-28 22:49:30 -04001836 final Context ctx = target.getContext();
1837 final Drawable id1 = i1 == null ? null : i1.loadDrawable(ctx);
1838 final Drawable id2 = i2 == null ? null : i2.loadDrawable(ctx);
1839 final Drawable id3 = i3 == null ? null : i3.loadDrawable(ctx);
1840 final Drawable id4 = i4 == null ? null : i4.loadDrawable(ctx);
1841 if (isRelative) {
1842 target.setCompoundDrawablesRelativeWithIntrinsicBounds(id1, id2, id3, id4);
1843 } else {
1844 target.setCompoundDrawablesWithIntrinsicBounds(id1, id2, id3, id4);
1845 }
Daniel Sandler820ba322012-03-23 16:36:00 -05001846 } else {
Dan Sandler912282e2015-07-28 22:49:30 -04001847 if (isRelative) {
1848 target.setCompoundDrawablesRelativeWithIntrinsicBounds(d1, d2, d3, d4);
1849 } else {
1850 target.setCompoundDrawablesWithIntrinsicBounds(d1, d2, d3, d4);
1851 }
Daniel Sandler820ba322012-03-23 16:36:00 -05001852 }
1853 }
1854
Sunny Goyaldd292f42015-12-02 14:29:27 -08001855 @Override
1856 public Action initActionAsync(ViewTree root, ViewGroup rootParent, OnClickHandler handler) {
Alan Viverette04fd4702017-04-13 16:37:06 -04001857 final TextView target = root.findViewById(viewId);
Sunny Goyaldd292f42015-12-02 14:29:27 -08001858 if (target == null) return ACTION_NOOP;
1859
1860 TextViewDrawableAction copy = useIcons ?
1861 new TextViewDrawableAction(viewId, isRelative, i1, i2, i3, i4) :
1862 new TextViewDrawableAction(viewId, isRelative, d1, d2, d3, d4);
1863
1864 // Load the drawables on the background thread.
1865 copy.drawablesLoaded = true;
1866 final Context ctx = target.getContext();
1867
1868 if (useIcons) {
1869 copy.id1 = i1 == null ? null : i1.loadDrawable(ctx);
1870 copy.id2 = i2 == null ? null : i2.loadDrawable(ctx);
1871 copy.id3 = i3 == null ? null : i3.loadDrawable(ctx);
1872 copy.id4 = i4 == null ? null : i4.loadDrawable(ctx);
1873 } else {
1874 copy.id1 = d1 == 0 ? null : ctx.getDrawable(d1);
1875 copy.id2 = d2 == 0 ? null : ctx.getDrawable(d2);
1876 copy.id3 = d3 == 0 ? null : ctx.getDrawable(d3);
1877 copy.id4 = d4 == 0 ? null : ctx.getDrawable(d4);
1878 }
1879 return copy;
1880 }
1881
Sunny Goyal5c022632016-02-17 16:30:41 -08001882 @Override
1883 public boolean prefersAsyncApply() {
1884 return useIcons;
1885 }
1886
Sunny Goyal5b153922017-09-21 21:00:36 -07001887 @Override
1888 public int getActionTag() {
1889 return TEXT_VIEW_DRAWABLE_ACTION_TAG;
Adam Cohenfbe44b72012-09-19 20:36:23 -07001890 }
1891
Jeff Sharkey23b31182018-04-18 21:32:12 -06001892 @Override
1893 public void visitUris(@NonNull Consumer<Uri> visitor) {
1894 if (useIcons) {
1895 visitIconUri(i1, visitor);
1896 visitIconUri(i2, visitor);
1897 visitIconUri(i3, visitor);
1898 visitIconUri(i4, visitor);
1899 }
1900 }
1901
Daniel Sandler820ba322012-03-23 16:36:00 -05001902 boolean isRelative = false;
Dan Sandler912282e2015-07-28 22:49:30 -04001903 boolean useIcons = false;
Daniel Sandler820ba322012-03-23 16:36:00 -05001904 int d1, d2, d3, d4;
Dan Sandler912282e2015-07-28 22:49:30 -04001905 Icon i1, i2, i3, i4;
Daniel Sandler820ba322012-03-23 16:36:00 -05001906
Sunny Goyaldd292f42015-12-02 14:29:27 -08001907 boolean drawablesLoaded = false;
1908 Drawable id1, id2, id3, id4;
Daniel Sandler820ba322012-03-23 16:36:00 -05001909 }
1910
1911 /**
Daniel Sandler99d1f742012-05-21 16:14:14 -04001912 * Helper action to set text size on a TextView in any supported units.
Daniel Sandler7264f712012-05-21 14:48:23 -04001913 */
1914 private class TextViewSizeAction extends Action {
1915 public TextViewSizeAction(int viewId, int units, float size) {
1916 this.viewId = viewId;
1917 this.units = units;
1918 this.size = size;
1919 }
1920
1921 public TextViewSizeAction(Parcel parcel) {
1922 viewId = parcel.readInt();
1923 units = parcel.readInt();
1924 size = parcel.readFloat();
1925 }
1926
1927 public void writeToParcel(Parcel dest, int flags) {
Daniel Sandler7264f712012-05-21 14:48:23 -04001928 dest.writeInt(viewId);
1929 dest.writeInt(units);
1930 dest.writeFloat(size);
1931 }
1932
1933 @Override
Dianne Hackborna1940212012-06-28 16:07:22 -07001934 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
Alan Viverette8e1a7292017-02-27 10:57:58 -05001935 final TextView target = root.findViewById(viewId);
Daniel Sandler7264f712012-05-21 14:48:23 -04001936 if (target == null) return;
1937 target.setTextSize(units, size);
1938 }
1939
Sunny Goyal5b153922017-09-21 21:00:36 -07001940 @Override
1941 public int getActionTag() {
1942 return TEXT_VIEW_SIZE_ACTION_TAG;
Adam Cohenfbe44b72012-09-19 20:36:23 -07001943 }
1944
Daniel Sandler7264f712012-05-21 14:48:23 -04001945 int units;
1946 float size;
Daniel Sandler7264f712012-05-21 14:48:23 -04001947 }
1948
1949 /**
Daniel Sandler99d1f742012-05-21 16:14:14 -04001950 * Helper action to set padding on a View.
1951 */
1952 private class ViewPaddingAction extends Action {
1953 public ViewPaddingAction(int viewId, int left, int top, int right, int bottom) {
1954 this.viewId = viewId;
1955 this.left = left;
1956 this.top = top;
1957 this.right = right;
1958 this.bottom = bottom;
1959 }
1960
1961 public ViewPaddingAction(Parcel parcel) {
1962 viewId = parcel.readInt();
1963 left = parcel.readInt();
1964 top = parcel.readInt();
1965 right = parcel.readInt();
1966 bottom = parcel.readInt();
1967 }
1968
1969 public void writeToParcel(Parcel dest, int flags) {
Daniel Sandler99d1f742012-05-21 16:14:14 -04001970 dest.writeInt(viewId);
1971 dest.writeInt(left);
1972 dest.writeInt(top);
1973 dest.writeInt(right);
1974 dest.writeInt(bottom);
1975 }
1976
1977 @Override
Dianne Hackborna1940212012-06-28 16:07:22 -07001978 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
Daniel Sandler99d1f742012-05-21 16:14:14 -04001979 final View target = root.findViewById(viewId);
1980 if (target == null) return;
1981 target.setPadding(left, top, right, bottom);
1982 }
1983
Sunny Goyal5b153922017-09-21 21:00:36 -07001984 @Override
1985 public int getActionTag() {
1986 return VIEW_PADDING_ACTION_TAG;
Adam Cohenfbe44b72012-09-19 20:36:23 -07001987 }
1988
Daniel Sandler99d1f742012-05-21 16:14:14 -04001989 int left, top, right, bottom;
Daniel Sandler99d1f742012-05-21 16:14:14 -04001990 }
1991
1992 /**
Adrian Roos9b123cf2016-02-04 14:55:57 -08001993 * Helper action to set layout params on a View.
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001994 */
Adrian Roos2d5dbba2016-06-08 17:11:53 -07001995 private static class LayoutParamAction extends Action {
Adrian Roos9b123cf2016-02-04 14:55:57 -08001996
1997 /** Set marginEnd */
Adrian Roos2d5dbba2016-06-08 17:11:53 -07001998 public static final int LAYOUT_MARGIN_END_DIMEN = 1;
Adrian Roos9b123cf2016-02-04 14:55:57 -08001999 /** Set width */
2000 public static final int LAYOUT_WIDTH = 2;
Adrian Roos2d5dbba2016-06-08 17:11:53 -07002001 public static final int LAYOUT_MARGIN_BOTTOM_DIMEN = 3;
Selim Cinek384804b2018-04-18 14:31:07 +08002002 public static final int LAYOUT_MARGIN_END = 4;
Adrian Roos9b123cf2016-02-04 14:55:57 -08002003
Sunny Goyal5b153922017-09-21 21:00:36 -07002004 final int mProperty;
2005 final int mValue;
2006
Adrian Roos9b123cf2016-02-04 14:55:57 -08002007 /**
2008 * @param viewId ID of the view alter
2009 * @param property which layout parameter to alter
2010 * @param value new value of the layout parameter
2011 */
2012 public LayoutParamAction(int viewId, int property, int value) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07002013 this.viewId = viewId;
Sunny Goyal5b153922017-09-21 21:00:36 -07002014 this.mProperty = property;
2015 this.mValue = value;
Selim Cinek65b2e7c2015-10-26 14:11:31 -07002016 }
2017
Adrian Roos9b123cf2016-02-04 14:55:57 -08002018 public LayoutParamAction(Parcel parcel) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07002019 viewId = parcel.readInt();
Sunny Goyal5b153922017-09-21 21:00:36 -07002020 mProperty = parcel.readInt();
2021 mValue = parcel.readInt();
Selim Cinek65b2e7c2015-10-26 14:11:31 -07002022 }
2023
2024 public void writeToParcel(Parcel dest, int flags) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07002025 dest.writeInt(viewId);
Sunny Goyal5b153922017-09-21 21:00:36 -07002026 dest.writeInt(mProperty);
2027 dest.writeInt(mValue);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07002028 }
2029
2030 @Override
2031 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
2032 final View target = root.findViewById(viewId);
2033 if (target == null) {
2034 return;
2035 }
2036 ViewGroup.LayoutParams layoutParams = target.getLayoutParams();
Adrian Roos9b123cf2016-02-04 14:55:57 -08002037 if (layoutParams == null) {
2038 return;
2039 }
Selim Cinek384804b2018-04-18 14:31:07 +08002040 int value = mValue;
Sunny Goyal5b153922017-09-21 21:00:36 -07002041 switch (mProperty) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07002042 case LAYOUT_MARGIN_END_DIMEN:
Selim Cinek384804b2018-04-18 14:31:07 +08002043 value = resolveDimenPixelOffset(target, mValue);
2044 // fall-through
2045 case LAYOUT_MARGIN_END:
Adrian Roos9b123cf2016-02-04 14:55:57 -08002046 if (layoutParams instanceof ViewGroup.MarginLayoutParams) {
Selim Cinek384804b2018-04-18 14:31:07 +08002047 ((ViewGroup.MarginLayoutParams) layoutParams).setMarginEnd(value);
Adrian Roos9b123cf2016-02-04 14:55:57 -08002048 target.setLayoutParams(layoutParams);
2049 }
2050 break;
Adrian Roos2d5dbba2016-06-08 17:11:53 -07002051 case LAYOUT_MARGIN_BOTTOM_DIMEN:
Adrian Roosc1a80b02016-04-05 14:54:55 -07002052 if (layoutParams instanceof ViewGroup.MarginLayoutParams) {
Sunny Goyal5b153922017-09-21 21:00:36 -07002053 int resolved = resolveDimenPixelOffset(target, mValue);
Adrian Roos2d5dbba2016-06-08 17:11:53 -07002054 ((ViewGroup.MarginLayoutParams) layoutParams).bottomMargin = resolved;
Adrian Roosc1a80b02016-04-05 14:54:55 -07002055 target.setLayoutParams(layoutParams);
2056 }
2057 break;
Adrian Roos9b123cf2016-02-04 14:55:57 -08002058 case LAYOUT_WIDTH:
Sunny Goyal5b153922017-09-21 21:00:36 -07002059 layoutParams.width = mValue;
Adrian Roos9b123cf2016-02-04 14:55:57 -08002060 target.setLayoutParams(layoutParams);
2061 break;
2062 default:
Sunny Goyal5b153922017-09-21 21:00:36 -07002063 throw new IllegalArgumentException("Unknown property " + mProperty);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07002064 }
2065 }
2066
Adrian Roos2d5dbba2016-06-08 17:11:53 -07002067 private static int resolveDimenPixelOffset(View target, int value) {
2068 if (value == 0) {
2069 return 0;
2070 }
2071 return target.getContext().getResources().getDimensionPixelOffset(value);
2072 }
2073
Sunny Goyal5b153922017-09-21 21:00:36 -07002074 @Override
2075 public int getActionTag() {
2076 return LAYOUT_PARAM_ACTION_TAG;
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01002077 }
2078
2079 @Override
Sunny Goyal5b153922017-09-21 21:00:36 -07002080 public String getUniqueKey() {
2081 return super.getUniqueKey() + mProperty;
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01002082 }
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01002083 }
2084
2085 /**
Adrian Roosfe84e1f2015-11-04 15:55:39 -08002086 * Helper action to add a view tag with RemoteInputs.
2087 */
2088 private class SetRemoteInputsAction extends Action {
2089
2090 public SetRemoteInputsAction(int viewId, RemoteInput[] remoteInputs) {
2091 this.viewId = viewId;
2092 this.remoteInputs = remoteInputs;
2093 }
2094
2095 public SetRemoteInputsAction(Parcel parcel) {
2096 viewId = parcel.readInt();
Adrian Roos5dd685f2016-02-24 12:05:51 -08002097 remoteInputs = parcel.createTypedArray(RemoteInput.CREATOR);
Adrian Roosfe84e1f2015-11-04 15:55:39 -08002098 }
2099
2100 public void writeToParcel(Parcel dest, int flags) {
Adrian Roosfe84e1f2015-11-04 15:55:39 -08002101 dest.writeInt(viewId);
Adrian Roos5dd685f2016-02-24 12:05:51 -08002102 dest.writeTypedArray(remoteInputs, flags);
Adrian Roosfe84e1f2015-11-04 15:55:39 -08002103 }
2104
2105 @Override
2106 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
Selim Cinek5d6ef8d2017-05-18 22:16:00 -07002107 final View target = root.findViewById(viewId);
Adrian Roosfe84e1f2015-11-04 15:55:39 -08002108 if (target == null) return;
2109
2110 target.setTagInternal(R.id.remote_input_tag, remoteInputs);
2111 }
2112
Sunny Goyal5b153922017-09-21 21:00:36 -07002113 @Override
2114 public int getActionTag() {
2115 return SET_REMOTE_INPUTS_ACTION_TAG;
Adrian Roosfe84e1f2015-11-04 15:55:39 -08002116 }
2117
2118 final Parcelable[] remoteInputs;
Adrian Roosfe84e1f2015-11-04 15:55:39 -08002119 }
2120
2121 /**
Selim Cinek87c31532017-08-18 18:53:44 -07002122 * Helper action to override all textViewColors
2123 */
2124 private class OverrideTextColorsAction extends Action {
2125
2126 private final int textColor;
2127
2128 public OverrideTextColorsAction(int textColor) {
2129 this.textColor = textColor;
2130 }
2131
2132 public OverrideTextColorsAction(Parcel parcel) {
2133 textColor = parcel.readInt();
2134 }
2135
2136 public void writeToParcel(Parcel dest, int flags) {
Selim Cinek87c31532017-08-18 18:53:44 -07002137 dest.writeInt(textColor);
2138 }
2139
2140 @Override
2141 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
2142 // Let's traverse the viewtree and override all textColors!
2143 Stack<View> viewsToProcess = new Stack<>();
2144 viewsToProcess.add(root);
2145 while (!viewsToProcess.isEmpty()) {
2146 View v = viewsToProcess.pop();
2147 if (v instanceof TextView) {
2148 TextView textView = (TextView) v;
Lucas Dupina291d192018-06-07 13:59:42 -07002149 textView.setText(ContrastColorUtil.clearColorSpans(textView.getText()));
Selim Cinek87c31532017-08-18 18:53:44 -07002150 textView.setTextColor(textColor);
2151 }
2152 if (v instanceof ViewGroup) {
2153 ViewGroup viewGroup = (ViewGroup) v;
2154 for (int i = 0; i < viewGroup.getChildCount(); i++) {
2155 viewsToProcess.push(viewGroup.getChildAt(i));
2156 }
2157 }
2158 }
2159 }
2160
Sunny Goyal5b153922017-09-21 21:00:36 -07002161 @Override
2162 public int getActionTag() {
2163 return OVERRIDE_TEXT_COLORS_TAG;
Selim Cinek87c31532017-08-18 18:53:44 -07002164 }
2165 }
2166
Tony Mak7d4b3a52018-11-27 17:29:36 +00002167 private class SetIntTagAction extends Action {
2168 private final int mViewId;
2169 private final int mKey;
2170 private final int mTag;
2171
2172 SetIntTagAction(int viewId, int key, int tag) {
2173 mViewId = viewId;
2174 mKey = key;
2175 mTag = tag;
2176 }
2177
2178 SetIntTagAction(Parcel parcel) {
2179 mViewId = parcel.readInt();
2180 mKey = parcel.readInt();
2181 mTag = parcel.readInt();
2182 }
2183
2184 public void writeToParcel(Parcel dest, int flags) {
2185 dest.writeInt(mViewId);
2186 dest.writeInt(mKey);
2187 dest.writeInt(mTag);
2188 }
2189
2190 @Override
2191 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
2192 final View target = root.findViewById(mViewId);
2193 if (target == null) return;
2194
2195 target.setTagInternal(mKey, mTag);
2196 }
2197
2198 @Override
2199 public int getActionTag() {
2200 return SET_INT_TAG_TAG;
2201 }
2202 }
2203
Selim Cinek87c31532017-08-18 18:53:44 -07002204 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002205 * Create a new RemoteViews object that will display the views contained
2206 * in the specified layout file.
Jim Millere667a7a2012-08-09 19:22:32 -07002207 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002208 * @param packageName Name of the package that contains the layout resource
2209 * @param layoutId The id of the layout resource
2210 */
2211 public RemoteViews(String packageName, int layoutId) {
Svet Ganov0da85b62014-08-06 14:11:37 -07002212 this(getApplicationInfo(packageName, UserHandle.myUserId()), layoutId);
2213 }
2214
2215 /**
2216 * Create a new RemoteViews object that will display the views contained
2217 * in the specified layout file.
2218 *
2219 * @param packageName Name of the package that contains the layout resource.
2220 * @param userId The user under which the package is running.
2221 * @param layoutId The id of the layout resource.
2222 *
2223 * @hide
2224 */
Sunny Goyalc12d31c2018-11-12 16:29:18 -08002225 public RemoteViews(String packageName, int userId, @LayoutRes int layoutId) {
Svet Ganov0da85b62014-08-06 14:11:37 -07002226 this(getApplicationInfo(packageName, userId), layoutId);
2227 }
2228
2229 /**
2230 * Create a new RemoteViews object that will display the views contained
2231 * in the specified layout file.
2232 *
2233 * @param application The application whose content is shown by the views.
2234 * @param layoutId The id of the layout resource.
Kenny Guy77320062014-08-27 21:37:15 +01002235 *
2236 * @hide
Svet Ganov0da85b62014-08-06 14:11:37 -07002237 */
Sunny Goyalc12d31c2018-11-12 16:29:18 -08002238 protected RemoteViews(ApplicationInfo application, @LayoutRes int layoutId) {
Svet Ganov0da85b62014-08-06 14:11:37 -07002239 mApplication = application;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002240 mLayoutId = layoutId;
Adam Cohen5d200642012-04-24 10:43:31 -07002241 mBitmapCache = new BitmapCache();
Adrian Roosfb921842017-10-26 14:49:56 +02002242 mClassCookies = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002243 }
2244
Adam Cohen5d200642012-04-24 10:43:31 -07002245 private boolean hasLandscapeAndPortraitLayouts() {
2246 return (mLandscape != null) && (mPortrait != null);
2247 }
2248
Jeff Sharkey6d515712012-09-20 16:06:08 -07002249 /**
Adam Cohen5d200642012-04-24 10:43:31 -07002250 * Create a new RemoteViews object that will inflate as the specified
2251 * landspace or portrait RemoteViews, depending on the current configuration.
2252 *
2253 * @param landscape The RemoteViews to inflate in landscape configuration
2254 * @param portrait The RemoteViews to inflate in portrait configuration
2255 */
2256 public RemoteViews(RemoteViews landscape, RemoteViews portrait) {
2257 if (landscape == null || portrait == null) {
2258 throw new RuntimeException("Both RemoteViews must be non-null");
2259 }
Sunny Goyaldd60f4d2017-10-18 15:22:42 -07002260 if (!landscape.hasSameAppInfo(portrait.mApplication)) {
Svet Ganov0da85b62014-08-06 14:11:37 -07002261 throw new RuntimeException("Both RemoteViews must share the same package and user");
Adam Cohen5d200642012-04-24 10:43:31 -07002262 }
Svet Ganov0da85b62014-08-06 14:11:37 -07002263 mApplication = portrait.mApplication;
Sunny Goyalc12d31c2018-11-12 16:29:18 -08002264 mLayoutId = portrait.mLayoutId;
2265 mLightBackgroundLayoutId = portrait.mLightBackgroundLayoutId;
Adam Cohen5d200642012-04-24 10:43:31 -07002266
2267 mLandscape = landscape;
2268 mPortrait = portrait;
2269
Adam Cohen5d200642012-04-24 10:43:31 -07002270 mBitmapCache = new BitmapCache();
2271 configureRemoteViewsAsChild(landscape);
2272 configureRemoteViewsAsChild(portrait);
Adrian Roosfb921842017-10-26 14:49:56 +02002273
2274 mClassCookies = (portrait.mClassCookies != null)
2275 ? portrait.mClassCookies : landscape.mClassCookies;
Sunny Goyal56333a82017-08-29 13:46:29 -07002276 }
Adam Cohen5d200642012-04-24 10:43:31 -07002277
Sunny Goyal56333a82017-08-29 13:46:29 -07002278 /**
2279 * Creates a copy of another RemoteViews.
2280 */
2281 public RemoteViews(RemoteViews src) {
2282 mBitmapCache = src.mBitmapCache;
2283 mApplication = src.mApplication;
2284 mIsRoot = src.mIsRoot;
2285 mLayoutId = src.mLayoutId;
Sunny Goyalc12d31c2018-11-12 16:29:18 -08002286 mLightBackgroundLayoutId = src.mLightBackgroundLayoutId;
2287 mApplyFlags = src.mApplyFlags;
Adrian Roosfb921842017-10-26 14:49:56 +02002288 mClassCookies = src.mClassCookies;
Sunny Goyal56333a82017-08-29 13:46:29 -07002289
2290 if (src.hasLandscapeAndPortraitLayouts()) {
2291 mLandscape = new RemoteViews(src.mLandscape);
2292 mPortrait = new RemoteViews(src.mPortrait);
Sunny Goyal56333a82017-08-29 13:46:29 -07002293 }
2294
2295 if (src.mActions != null) {
Sunny Goyal56333a82017-08-29 13:46:29 -07002296 Parcel p = Parcel.obtain();
Adrian Roosfb921842017-10-26 14:49:56 +02002297 p.putClassCookies(mClassCookies);
Sunny Goyal0ebf8812017-09-25 10:02:59 -07002298 src.writeActionsToParcel(p);
Sunny Goyal5b7689f2017-09-21 11:08:34 -07002299 p.setDataPosition(0);
2300 // Since src is already in memory, we do not care about stack overflow as it has
2301 // already been read once.
2302 readActionsFromParcel(p, 0);
Sunny Goyal56333a82017-08-29 13:46:29 -07002303 p.recycle();
2304 }
2305
2306 // Now that everything is initialized and duplicated, setting a new BitmapCache will
2307 // re-initialize the cache.
2308 setBitmapCache(new BitmapCache());
Adam Cohen5d200642012-04-24 10:43:31 -07002309 }
2310
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002311 /**
2312 * Reads a RemoteViews object from a parcel.
Jim Millere667a7a2012-08-09 19:22:32 -07002313 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002314 * @param parcel
2315 */
2316 public RemoteViews(Parcel parcel) {
Adrian Roosfb921842017-10-26 14:49:56 +02002317 this(parcel, null, null, 0, null);
Adam Cohen5d200642012-04-24 10:43:31 -07002318 }
Adam Cohenca6fd842010-09-03 18:10:35 -07002319
Adrian Roosfb921842017-10-26 14:49:56 +02002320 private RemoteViews(Parcel parcel, BitmapCache bitmapCache, ApplicationInfo info, int depth,
2321 Map<Class, Object> classCookies) {
Sunny Goyal692f8c92016-11-11 09:19:14 -08002322 if (depth > MAX_NESTED_VIEWS
2323 && (UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID)) {
2324 throw new IllegalArgumentException("Too many nested views.");
2325 }
2326 depth++;
2327
Adam Cohen5d200642012-04-24 10:43:31 -07002328 int mode = parcel.readInt();
2329
2330 // We only store a bitmap cache in the root of the RemoteViews.
2331 if (bitmapCache == null) {
2332 mBitmapCache = new BitmapCache(parcel);
Adrian Roosfb921842017-10-26 14:49:56 +02002333 // Store the class cookies such that they are available when we clone this RemoteView.
2334 mClassCookies = parcel.copyClassCookies();
Adam Cohen5d200642012-04-24 10:43:31 -07002335 } else {
2336 setBitmapCache(bitmapCache);
Adrian Roosfb921842017-10-26 14:49:56 +02002337 mClassCookies = classCookies;
Adam Cohen5d200642012-04-24 10:43:31 -07002338 setNotRoot();
2339 }
2340
2341 if (mode == MODE_NORMAL) {
Sunny Goyal5d8bcdf2016-10-27 16:11:29 -07002342 mApplication = parcel.readInt() == 0 ? info :
2343 ApplicationInfo.CREATOR.createFromParcel(parcel);
Adam Cohen5d200642012-04-24 10:43:31 -07002344 mLayoutId = parcel.readInt();
Sunny Goyalc12d31c2018-11-12 16:29:18 -08002345 mLightBackgroundLayoutId = parcel.readInt();
Adam Cohen5d200642012-04-24 10:43:31 -07002346
Sunny Goyal5b7689f2017-09-21 11:08:34 -07002347 readActionsFromParcel(parcel, depth);
Adam Cohen5d200642012-04-24 10:43:31 -07002348 } else {
2349 // MODE_HAS_LANDSCAPE_AND_PORTRAIT
Adrian Roosfb921842017-10-26 14:49:56 +02002350 mLandscape = new RemoteViews(parcel, mBitmapCache, info, depth, mClassCookies);
2351 mPortrait = new RemoteViews(parcel, mBitmapCache, mLandscape.mApplication, depth,
2352 mClassCookies);
Svet Ganov0da85b62014-08-06 14:11:37 -07002353 mApplication = mPortrait.mApplication;
Sunny Goyalc12d31c2018-11-12 16:29:18 -08002354 mLayoutId = mPortrait.mLayoutId;
2355 mLightBackgroundLayoutId = mPortrait.mLightBackgroundLayoutId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002356 }
Sunny Goyalc12d31c2018-11-12 16:29:18 -08002357 mApplyFlags = parcel.readInt();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002358 }
2359
Sunny Goyal5b7689f2017-09-21 11:08:34 -07002360 private void readActionsFromParcel(Parcel parcel, int depth) {
2361 int count = parcel.readInt();
2362 if (count > 0) {
2363 mActions = new ArrayList<>(count);
2364 for (int i = 0; i < count; i++) {
2365 mActions.add(getActionFromParcel(parcel, depth));
2366 }
2367 }
2368 }
2369
Sunny Goyal56333a82017-08-29 13:46:29 -07002370 private Action getActionFromParcel(Parcel parcel, int depth) {
2371 int tag = parcel.readInt();
2372 switch (tag) {
Sunny Goyal43c97042018-08-23 15:21:26 -07002373 case SET_ON_CLICK_RESPONSE_TAG:
2374 return new SetOnClickResponse(parcel);
Sunny Goyal5b153922017-09-21 21:00:36 -07002375 case SET_DRAWABLE_TINT_TAG:
2376 return new SetDrawableTint(parcel);
Sunny Goyal56333a82017-08-29 13:46:29 -07002377 case REFLECTION_ACTION_TAG:
2378 return new ReflectionAction(parcel);
2379 case VIEW_GROUP_ACTION_ADD_TAG:
Adrian Roosfb921842017-10-26 14:49:56 +02002380 return new ViewGroupActionAdd(parcel, mBitmapCache, mApplication, depth,
2381 mClassCookies);
Sunny Goyal56333a82017-08-29 13:46:29 -07002382 case VIEW_GROUP_ACTION_REMOVE_TAG:
2383 return new ViewGroupActionRemove(parcel);
Sunny Goyal5b153922017-09-21 21:00:36 -07002384 case VIEW_CONTENT_NAVIGATION_TAG:
2385 return new ViewContentNavigation(parcel);
Sunny Goyal56333a82017-08-29 13:46:29 -07002386 case SET_EMPTY_VIEW_ACTION_TAG:
2387 return new SetEmptyView(parcel);
2388 case SET_PENDING_INTENT_TEMPLATE_TAG:
2389 return new SetPendingIntentTemplate(parcel);
Sunny Goyal56333a82017-08-29 13:46:29 -07002390 case SET_REMOTE_VIEW_ADAPTER_INTENT_TAG:
2391 return new SetRemoteViewsAdapterIntent(parcel);
2392 case TEXT_VIEW_DRAWABLE_ACTION_TAG:
2393 return new TextViewDrawableAction(parcel);
2394 case TEXT_VIEW_SIZE_ACTION_TAG:
2395 return new TextViewSizeAction(parcel);
2396 case VIEW_PADDING_ACTION_TAG:
2397 return new ViewPaddingAction(parcel);
2398 case BITMAP_REFLECTION_ACTION_TAG:
2399 return new BitmapReflectionAction(parcel);
2400 case SET_REMOTE_VIEW_ADAPTER_LIST_TAG:
2401 return new SetRemoteViewsAdapterList(parcel);
Sunny Goyal56333a82017-08-29 13:46:29 -07002402 case SET_REMOTE_INPUTS_ACTION_TAG:
2403 return new SetRemoteInputsAction(parcel);
2404 case LAYOUT_PARAM_ACTION_TAG:
2405 return new LayoutParamAction(parcel);
2406 case OVERRIDE_TEXT_COLORS_TAG:
2407 return new OverrideTextColorsAction(parcel);
Gus Prevas9cc96602018-10-11 11:24:23 -04002408 case SET_RIPPLE_DRAWABLE_COLOR_TAG:
2409 return new SetRippleDrawableColor(parcel);
Tony Mak7d4b3a52018-11-27 17:29:36 +00002410 case SET_INT_TAG_TAG:
2411 return new SetIntTagAction(parcel);
Sunny Goyal56333a82017-08-29 13:46:29 -07002412 default:
2413 throw new ActionException("Tag " + tag + " not found");
2414 }
2415 };
2416
Alan Viverette0a14ba52017-06-14 16:54:05 -04002417 /**
2418 * Returns a deep copy of the RemoteViews object. The RemoteView may not be
2419 * attached to another RemoteView -- it must be the root of a hierarchy.
2420 *
Sunny Goyal56333a82017-08-29 13:46:29 -07002421 * @deprecated use {@link #RemoteViews(RemoteViews)} instead.
Alan Viverette0a14ba52017-06-14 16:54:05 -04002422 * @throws IllegalStateException if this is not the root of a RemoteView
2423 * hierarchy
2424 */
2425 @Override
Sunny Goyal56333a82017-08-29 13:46:29 -07002426 @Deprecated
Alan Viverette0a14ba52017-06-14 16:54:05 -04002427 public RemoteViews clone() {
Sunny Goyal56333a82017-08-29 13:46:29 -07002428 Preconditions.checkState(mIsRoot, "RemoteView has been attached to another RemoteView. "
2429 + "May only clone the root of a RemoteView hierarchy.");
Winson Chung3ec9a452010-09-23 16:40:28 -07002430
Sunny Goyal56333a82017-08-29 13:46:29 -07002431 return new RemoteViews(this);
Joe Onorato18e69df2010-05-17 22:26:12 -07002432 }
2433
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002434 public String getPackage() {
Svetoslavb6242442014-09-19 13:21:55 -07002435 return (mApplication != null) ? mApplication.packageName : null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002436 }
2437
Adam Cohen5d200642012-04-24 10:43:31 -07002438 /**
Adrian Roos7da889d2016-03-16 18:38:58 -07002439 * Returns the layout id of the root layout associated with this RemoteViews. In the case
Adam Cohen5d200642012-04-24 10:43:31 -07002440 * that the RemoteViews has both a landscape and portrait root, this will return the layout
2441 * id associated with the portrait layout.
2442 *
2443 * @return the layout id.
2444 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002445 public int getLayoutId() {
Sunny Goyalc12d31c2018-11-12 16:29:18 -08002446 return hasFlags(FLAG_USE_LIGHT_BACKGROUND_LAYOUT) && (mLightBackgroundLayoutId != 0)
2447 ? mLightBackgroundLayoutId : mLayoutId;
Adam Cohenca6fd842010-09-03 18:10:35 -07002448 }
2449
2450 /**
Adam Cohen5d200642012-04-24 10:43:31 -07002451 * Recursively sets BitmapCache in the hierarchy and update the bitmap ids.
2452 */
2453 private void setBitmapCache(BitmapCache bitmapCache) {
2454 mBitmapCache = bitmapCache;
2455 if (!hasLandscapeAndPortraitLayouts()) {
2456 if (mActions != null) {
2457 final int count = mActions.size();
2458 for (int i= 0; i < count; ++i) {
2459 mActions.get(i).setBitmapCache(bitmapCache);
2460 }
2461 }
2462 } else {
2463 mLandscape.setBitmapCache(bitmapCache);
2464 mPortrait.setBitmapCache(bitmapCache);
Winson Chung3ec9a452010-09-23 16:40:28 -07002465 }
2466 }
2467
2468 /**
2469 * Returns an estimate of the bitmap heap memory usage for this RemoteViews.
2470 */
Adam Cohen311c79c2012-05-10 14:44:38 -07002471 /** @hide */
Mathew Inwood3a75f262019-06-27 12:47:38 +01002472 @UnsupportedAppUsage
Adam Cohen311c79c2012-05-10 14:44:38 -07002473 public int estimateMemoryUsage() {
Sunny Goyal56333a82017-08-29 13:46:29 -07002474 return mBitmapCache.getBitmapMemory();
Winson Chung3ec9a452010-09-23 16:40:28 -07002475 }
2476
2477 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002478 * Add an action to be executed on the remote side when apply is called.
Jim Millere667a7a2012-08-09 19:22:32 -07002479 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002480 * @param a The action to add
2481 */
2482 private void addAction(Action a) {
Adam Cohen5d200642012-04-24 10:43:31 -07002483 if (hasLandscapeAndPortraitLayouts()) {
2484 throw new RuntimeException("RemoteViews specifying separate landscape and portrait" +
2485 " layouts cannot be modified. Instead, fully configure the landscape and" +
2486 " portrait layouts individually before constructing the combined layout.");
2487 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002488 if (mActions == null) {
Sunny Goyal56333a82017-08-29 13:46:29 -07002489 mActions = new ArrayList<>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002490 }
2491 mActions.add(a);
2492 }
Jeff Sharkey1162fd72009-11-04 17:58:08 -08002493
2494 /**
2495 * Equivalent to calling {@link ViewGroup#addView(View)} after inflating the
2496 * given {@link RemoteViews}. This allows users to build "nested"
2497 * {@link RemoteViews}. In cases where consumers of {@link RemoteViews} may
2498 * recycle layouts, use {@link #removeAllViews(int)} to clear any existing
2499 * children.
2500 *
2501 * @param viewId The id of the parent {@link ViewGroup} to add child into.
2502 * @param nestedView {@link RemoteViews} that describes the child.
2503 */
2504 public void addView(int viewId, RemoteViews nestedView) {
Anthony Chen8f5f3582017-04-11 11:18:37 -07002505 addAction(nestedView == null
2506 ? new ViewGroupActionRemove(viewId)
2507 : new ViewGroupActionAdd(viewId, nestedView));
2508 }
2509
2510 /**
2511 * Equivalent to calling {@link ViewGroup#addView(View, int)} after inflating the
2512 * given {@link RemoteViews}.
2513 *
2514 * @param viewId The id of the parent {@link ViewGroup} to add the child into.
Sunny Goyal271e3222017-08-29 16:05:47 -07002515 * @param nestedView {@link RemoteViews} of the child to add.
Anthony Chen8f5f3582017-04-11 11:18:37 -07002516 * @param index The position at which to add the child.
2517 *
2518 * @hide
2519 */
Mathew Inwood3a75f262019-06-27 12:47:38 +01002520 @UnsupportedAppUsage
Anthony Chen8f5f3582017-04-11 11:18:37 -07002521 public void addView(int viewId, RemoteViews nestedView, int index) {
2522 addAction(new ViewGroupActionAdd(viewId, nestedView, index));
Jeff Sharkey1162fd72009-11-04 17:58:08 -08002523 }
2524
2525 /**
2526 * Equivalent to calling {@link ViewGroup#removeAllViews()}.
2527 *
2528 * @param viewId The id of the parent {@link ViewGroup} to remove all
2529 * children from.
2530 */
2531 public void removeAllViews(int viewId) {
Anthony Chen8f5f3582017-04-11 11:18:37 -07002532 addAction(new ViewGroupActionRemove(viewId));
2533 }
2534
2535 /**
2536 * Removes all views in the {@link ViewGroup} specified by the {@code viewId} except for any
2537 * child that has the {@code viewIdToKeep} as its id.
2538 *
2539 * @param viewId The id of the parent {@link ViewGroup} to remove children from.
2540 * @param viewIdToKeep The id of a child that should not be removed.
2541 *
2542 * @hide
2543 */
2544 public void removeAllViewsExceptId(int viewId, int viewIdToKeep) {
2545 addAction(new ViewGroupActionRemove(viewId, viewIdToKeep));
Jeff Sharkey1162fd72009-11-04 17:58:08 -08002546 }
2547
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002548 /**
Adam Cohen0b96a572011-02-10 15:56:16 -08002549 * Equivalent to calling {@link AdapterViewAnimator#showNext()}
Adam Cohen2dd21972010-08-15 18:20:04 -07002550 *
Adam Cohen0b96a572011-02-10 15:56:16 -08002551 * @param viewId The id of the view on which to call {@link AdapterViewAnimator#showNext()}
Adam Cohen2dd21972010-08-15 18:20:04 -07002552 */
2553 public void showNext(int viewId) {
Sunny Goyal5b153922017-09-21 21:00:36 -07002554 addAction(new ViewContentNavigation(viewId, true /* next */));
Adam Cohen2dd21972010-08-15 18:20:04 -07002555 }
2556
2557 /**
Adam Cohen0b96a572011-02-10 15:56:16 -08002558 * Equivalent to calling {@link AdapterViewAnimator#showPrevious()}
Adam Cohen2dd21972010-08-15 18:20:04 -07002559 *
Adam Cohen0b96a572011-02-10 15:56:16 -08002560 * @param viewId The id of the view on which to call {@link AdapterViewAnimator#showPrevious()}
Adam Cohen2dd21972010-08-15 18:20:04 -07002561 */
2562 public void showPrevious(int viewId) {
Sunny Goyal5b153922017-09-21 21:00:36 -07002563 addAction(new ViewContentNavigation(viewId, false /* next */));
Adam Cohen2dd21972010-08-15 18:20:04 -07002564 }
2565
2566 /**
Adam Cohen0b96a572011-02-10 15:56:16 -08002567 * Equivalent to calling {@link AdapterViewAnimator#setDisplayedChild(int)}
2568 *
2569 * @param viewId The id of the view on which to call
2570 * {@link AdapterViewAnimator#setDisplayedChild(int)}
2571 */
2572 public void setDisplayedChild(int viewId, int childIndex) {
2573 setInt(viewId, "setDisplayedChild", childIndex);
2574 }
2575
2576 /**
Kirill Grouchnikov40a39782016-07-01 17:24:32 -04002577 * Equivalent to calling {@link View#setVisibility(int)}
Jim Millere667a7a2012-08-09 19:22:32 -07002578 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002579 * @param viewId The id of the view whose visibility should change
2580 * @param visibility The new visibility for the view
2581 */
2582 public void setViewVisibility(int viewId, int visibility) {
2583 setInt(viewId, "setVisibility", visibility);
2584 }
Adam Cohenca6fd842010-09-03 18:10:35 -07002585
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002586 /**
Kirill Grouchnikov40a39782016-07-01 17:24:32 -04002587 * Equivalent to calling {@link TextView#setText(CharSequence)}
Jim Millere667a7a2012-08-09 19:22:32 -07002588 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002589 * @param viewId The id of the view whose text should change
2590 * @param text The new text for the view
2591 */
2592 public void setTextViewText(int viewId, CharSequence text) {
2593 setCharSequence(viewId, "setText", text);
2594 }
Daniel Sandler7264f712012-05-21 14:48:23 -04002595
2596 /**
Daniel Sandler7264f712012-05-21 14:48:23 -04002597 * Equivalent to calling {@link TextView#setTextSize(int, float)}
Jim Millere667a7a2012-08-09 19:22:32 -07002598 *
Daniel Sandler7264f712012-05-21 14:48:23 -04002599 * @param viewId The id of the view whose text size should change
2600 * @param units The units of size (e.g. COMPLEX_UNIT_SP)
2601 * @param size The size of the text
2602 */
2603 public void setTextViewTextSize(int viewId, int units, float size) {
2604 addAction(new TextViewSizeAction(viewId, units, size));
2605 }
2606
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002607 /**
Jim Millere667a7a2012-08-09 19:22:32 -07002608 * Equivalent to calling
Daniel Sandler820ba322012-03-23 16:36:00 -05002609 * {@link TextView#setCompoundDrawablesWithIntrinsicBounds(int, int, int, int)}.
2610 *
2611 * @param viewId The id of the view whose text should change
2612 * @param left The id of a drawable to place to the left of the text, or 0
2613 * @param top The id of a drawable to place above the text, or 0
2614 * @param right The id of a drawable to place to the right of the text, or 0
Jim Millere667a7a2012-08-09 19:22:32 -07002615 * @param bottom The id of a drawable to place below the text, or 0
Daniel Sandler820ba322012-03-23 16:36:00 -05002616 */
2617 public void setTextViewCompoundDrawables(int viewId, int left, int top, int right, int bottom) {
2618 addAction(new TextViewDrawableAction(viewId, false, left, top, right, bottom));
2619 }
2620
2621 /**
Jim Millere667a7a2012-08-09 19:22:32 -07002622 * Equivalent to calling {@link
Daniel Sandler820ba322012-03-23 16:36:00 -05002623 * TextView#setCompoundDrawablesRelativeWithIntrinsicBounds(int, int, int, int)}.
2624 *
2625 * @param viewId The id of the view whose text should change
Jim Millere667a7a2012-08-09 19:22:32 -07002626 * @param start The id of a drawable to place before the text (relative to the
Daniel Sandler820ba322012-03-23 16:36:00 -05002627 * layout direction), or 0
2628 * @param top The id of a drawable to place above the text, or 0
2629 * @param end The id of a drawable to place after the text, or 0
Fabrice Di Meglio66388dc2012-05-03 18:51:57 -07002630 * @param bottom The id of a drawable to place below the text, or 0
Daniel Sandler820ba322012-03-23 16:36:00 -05002631 */
2632 public void setTextViewCompoundDrawablesRelative(int viewId, int start, int top, int end, int bottom) {
2633 addAction(new TextViewDrawableAction(viewId, true, start, top, end, bottom));
2634 }
2635
2636 /**
Dan Sandler912282e2015-07-28 22:49:30 -04002637 * Equivalent to calling {@link
2638 * TextView#setCompoundDrawablesWithIntrinsicBounds(Drawable, Drawable, Drawable, Drawable)}
2639 * using the drawables yielded by {@link Icon#loadDrawable(Context)}.
2640 *
2641 * @param viewId The id of the view whose text should change
2642 * @param left an Icon to place to the left of the text, or 0
2643 * @param top an Icon to place above the text, or 0
2644 * @param right an Icon to place to the right of the text, or 0
2645 * @param bottom an Icon to place below the text, or 0
2646 *
2647 * @hide
2648 */
2649 public void setTextViewCompoundDrawables(int viewId, Icon left, Icon top, Icon right, Icon bottom) {
2650 addAction(new TextViewDrawableAction(viewId, false, left, top, right, bottom));
2651 }
2652
2653 /**
2654 * Equivalent to calling {@link
2655 * TextView#setCompoundDrawablesRelativeWithIntrinsicBounds(Drawable, Drawable, Drawable, Drawable)}
2656 * using the drawables yielded by {@link Icon#loadDrawable(Context)}.
2657 *
2658 * @param viewId The id of the view whose text should change
2659 * @param start an Icon to place before the text (relative to the
2660 * layout direction), or 0
2661 * @param top an Icon to place above the text, or 0
2662 * @param end an Icon to place after the text, or 0
2663 * @param bottom an Icon to place below the text, or 0
2664 *
2665 * @hide
2666 */
2667 public void setTextViewCompoundDrawablesRelative(int viewId, Icon start, Icon top, Icon end, Icon bottom) {
2668 addAction(new TextViewDrawableAction(viewId, true, start, top, end, bottom));
2669 }
2670
2671 /**
Kirill Grouchnikov40a39782016-07-01 17:24:32 -04002672 * Equivalent to calling {@link ImageView#setImageResource(int)}
Jim Millere667a7a2012-08-09 19:22:32 -07002673 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002674 * @param viewId The id of the view whose drawable should change
2675 * @param srcId The new resource id for the drawable
2676 */
Jim Millere667a7a2012-08-09 19:22:32 -07002677 public void setImageViewResource(int viewId, int srcId) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002678 setInt(viewId, "setImageResource", srcId);
2679 }
2680
2681 /**
Kirill Grouchnikov40a39782016-07-01 17:24:32 -04002682 * Equivalent to calling {@link ImageView#setImageURI(Uri)}
Jim Millere667a7a2012-08-09 19:22:32 -07002683 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002684 * @param viewId The id of the view whose drawable should change
2685 * @param uri The Uri for the image
2686 */
2687 public void setImageViewUri(int viewId, Uri uri) {
2688 setUri(viewId, "setImageURI", uri);
2689 }
2690
2691 /**
Kirill Grouchnikov40a39782016-07-01 17:24:32 -04002692 * Equivalent to calling {@link ImageView#setImageBitmap(Bitmap)}
Jim Millere667a7a2012-08-09 19:22:32 -07002693 *
Scott Main93dc6422012-02-24 12:04:06 -08002694 * @param viewId The id of the view whose bitmap should change
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002695 * @param bitmap The new Bitmap for the drawable
2696 */
2697 public void setImageViewBitmap(int viewId, Bitmap bitmap) {
2698 setBitmap(viewId, "setImageBitmap", bitmap);
2699 }
2700
2701 /**
Kirill Grouchnikov40a39782016-07-01 17:24:32 -04002702 * Equivalent to calling {@link ImageView#setImageIcon(Icon)}
Dan Sandlera22a3802015-05-13 00:12:47 -04002703 *
2704 * @param viewId The id of the view whose bitmap should change
2705 * @param icon The new Icon for the ImageView
2706 */
2707 public void setImageViewIcon(int viewId, Icon icon) {
2708 setIcon(viewId, "setImageIcon", icon);
2709 }
2710
2711 /**
Kirill Grouchnikov40a39782016-07-01 17:24:32 -04002712 * Equivalent to calling {@link AdapterView#setEmptyView(View)}
Adam Cohen1480fdd2010-08-25 17:24:53 -07002713 *
2714 * @param viewId The id of the view on which to set the empty view
2715 * @param emptyViewId The view id of the empty view
2716 */
2717 public void setEmptyView(int viewId, int emptyViewId) {
2718 addAction(new SetEmptyView(viewId, emptyViewId));
2719 }
2720
2721 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002722 * Equivalent to calling {@link Chronometer#setBase Chronometer.setBase},
2723 * {@link Chronometer#setFormat Chronometer.setFormat},
2724 * and {@link Chronometer#start Chronometer.start()} or
2725 * {@link Chronometer#stop Chronometer.stop()}.
Jim Millere667a7a2012-08-09 19:22:32 -07002726 *
Scott Main93dc6422012-02-24 12:04:06 -08002727 * @param viewId The id of the {@link Chronometer} to change
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002728 * @param base The time at which the timer would have read 0:00. This
2729 * time should be based off of
2730 * {@link android.os.SystemClock#elapsedRealtime SystemClock.elapsedRealtime()}.
2731 * @param format The Chronometer format string, or null to
2732 * simply display the timer value.
2733 * @param started True if you want the clock to be started, false if not.
Selim Cinek81c23aa2016-02-25 16:23:13 -08002734 *
Selim Cinekc3b752e2016-04-20 16:13:59 -07002735 * @see #setChronometerCountDown(int, boolean)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002736 */
2737 public void setChronometer(int viewId, long base, String format, boolean started) {
2738 setLong(viewId, "setBase", base);
2739 setString(viewId, "setFormat", format);
2740 setBoolean(viewId, "setStarted", started);
2741 }
Jim Millere667a7a2012-08-09 19:22:32 -07002742
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002743 /**
Selim Cinek81c23aa2016-02-25 16:23:13 -08002744 * Equivalent to calling {@link Chronometer#setCountDown(boolean) Chronometer.setCountDown} on
2745 * the chronometer with the given viewId.
2746 *
2747 * @param viewId The id of the {@link Chronometer} to change
2748 * @param isCountDown True if you want the chronometer to count down to base instead of
2749 * counting up.
2750 */
Selim Cinekc3b752e2016-04-20 16:13:59 -07002751 public void setChronometerCountDown(int viewId, boolean isCountDown) {
Selim Cinek81c23aa2016-02-25 16:23:13 -08002752 setBoolean(viewId, "setCountDown", isCountDown);
2753 }
2754
2755 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002756 * Equivalent to calling {@link ProgressBar#setMax ProgressBar.setMax},
2757 * {@link ProgressBar#setProgress ProgressBar.setProgress}, and
2758 * {@link ProgressBar#setIndeterminate ProgressBar.setIndeterminate}
2759 *
2760 * If indeterminate is true, then the values for max and progress are ignored.
Jim Millere667a7a2012-08-09 19:22:32 -07002761 *
Scott Main93dc6422012-02-24 12:04:06 -08002762 * @param viewId The id of the {@link ProgressBar} to change
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002763 * @param max The 100% value for the progress bar
2764 * @param progress The current value of the progress bar.
Jim Millere667a7a2012-08-09 19:22:32 -07002765 * @param indeterminate True if the progress bar is indeterminate,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002766 * false if not.
2767 */
Jim Millere667a7a2012-08-09 19:22:32 -07002768 public void setProgressBar(int viewId, int max, int progress,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002769 boolean indeterminate) {
2770 setBoolean(viewId, "setIndeterminate", indeterminate);
2771 if (!indeterminate) {
2772 setInt(viewId, "setMax", max);
2773 setInt(viewId, "setProgress", progress);
2774 }
2775 }
Jim Millere667a7a2012-08-09 19:22:32 -07002776
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002777 /**
2778 * Equivalent to calling
2779 * {@link android.view.View#setOnClickListener(android.view.View.OnClickListener)}
Sunny Goyald7d0c692017-10-12 12:05:14 -07002780 * to launch the provided {@link PendingIntent}. The source bounds
2781 * ({@link Intent#getSourceBounds()}) of the intent will be set to the bounds of the clicked
2782 * view in screen space.
Sunny Goyal43c97042018-08-23 15:21:26 -07002783 * Note that any activity options associated with the mPendingIntent may get overridden
Sunny Goyald7d0c692017-10-12 12:05:14 -07002784 * before starting the intent.
Jim Millere667a7a2012-08-09 19:22:32 -07002785 *
Adam Cohen35ae9ca2010-09-20 15:20:41 -07002786 * When setting the on-click action of items within collections (eg. {@link ListView},
2787 * {@link StackView} etc.), this method will not work. Instead, use {@link
Kirill Grouchnikovc5b30102016-06-28 15:34:13 -04002788 * RemoteViews#setPendingIntentTemplate(int, PendingIntent)} in conjunction with
2789 * {@link RemoteViews#setOnClickFillInIntent(int, Intent)}.
Adam Cohen35ae9ca2010-09-20 15:20:41 -07002790 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002791 * @param viewId The id of the view that will trigger the {@link PendingIntent} when clicked
2792 * @param pendingIntent The {@link PendingIntent} to send when user clicks
2793 */
2794 public void setOnClickPendingIntent(int viewId, PendingIntent pendingIntent) {
Sunny Goyal43c97042018-08-23 15:21:26 -07002795 setOnClickResponse(viewId, RemoteResponse.fromPendingIntent(pendingIntent));
2796 }
2797
2798 /**
2799 * Equivalent of calling
2800 * {@link android.view.View#setOnClickListener(android.view.View.OnClickListener)}
2801 * to launch the provided {@link RemoteResponse}.
2802 *
2803 * @param viewId The id of the view that will trigger the {@link RemoteResponse} when clicked
2804 * @param response The {@link RemoteResponse} to send when user clicks
2805 */
Sunny Goyal40635d72019-03-01 11:46:44 -08002806 public void setOnClickResponse(int viewId, @NonNull RemoteResponse response) {
Sunny Goyal43c97042018-08-23 15:21:26 -07002807 addAction(new SetOnClickResponse(viewId, response));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002808 }
2809
2810 /**
Adam Cohen35ae9ca2010-09-20 15:20:41 -07002811 * When using collections (eg. {@link ListView}, {@link StackView} etc.) in widgets, it is very
2812 * costly to set PendingIntents on the individual items, and is hence not permitted. Instead
2813 * this method should be used to set a single PendingIntent template on the collection, and
2814 * individual items can differentiate their on-click behavior using
2815 * {@link RemoteViews#setOnClickFillInIntent(int, Intent)}.
Adam Cohenca6fd842010-09-03 18:10:35 -07002816 *
2817 * @param viewId The id of the collection who's children will use this PendingIntent template
2818 * when clicked
2819 * @param pendingIntentTemplate The {@link PendingIntent} to be combined with extras specified
2820 * by a child of viewId and executed when that child is clicked
2821 */
2822 public void setPendingIntentTemplate(int viewId, PendingIntent pendingIntentTemplate) {
2823 addAction(new SetPendingIntentTemplate(viewId, pendingIntentTemplate));
2824 }
2825
2826 /**
Adam Cohen35ae9ca2010-09-20 15:20:41 -07002827 * When using collections (eg. {@link ListView}, {@link StackView} etc.) in widgets, it is very
2828 * costly to set PendingIntents on the individual items, and is hence not permitted. Instead
2829 * a single PendingIntent template can be set on the collection, see {@link
2830 * RemoteViews#setPendingIntentTemplate(int, PendingIntent)}, and the individual on-click
2831 * action of a given item can be distinguished by setting a fillInIntent on that item. The
2832 * fillInIntent is then combined with the PendingIntent template in order to determine the final
2833 * intent which will be executed when the item is clicked. This works as follows: any fields
2834 * which are left blank in the PendingIntent template, but are provided by the fillInIntent
Kirill Grouchnikovc5b30102016-06-28 15:34:13 -04002835 * will be overwritten, and the resulting PendingIntent will be used. The rest
Adam Cohen35ae9ca2010-09-20 15:20:41 -07002836 * of the PendingIntent template will then be filled in with the associated fields that are
2837 * set in fillInIntent. See {@link Intent#fillIn(Intent, int)} for more details.
2838 *
2839 * @param viewId The id of the view on which to set the fillInIntent
2840 * @param fillInIntent The intent which will be combined with the parent's PendingIntent
2841 * in order to determine the on-click behavior of the view specified by viewId
2842 */
2843 public void setOnClickFillInIntent(int viewId, Intent fillInIntent) {
Sunny Goyal43c97042018-08-23 15:21:26 -07002844 setOnClickResponse(viewId, RemoteResponse.fromFillInIntent(fillInIntent));
Adam Cohen35ae9ca2010-09-20 15:20:41 -07002845 }
2846
2847 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002848 * @hide
Sunny Goyal5b153922017-09-21 21:00:36 -07002849 * Equivalent to calling
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002850 * {@link Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)},
Sunny Goyal5b153922017-09-21 21:00:36 -07002851 * on the {@link Drawable} of a given view.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002852 * <p>
Jim Millere667a7a2012-08-09 19:22:32 -07002853 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002854 * @param viewId The id of the view that contains the target
2855 * {@link Drawable}
2856 * @param targetBackground If true, apply these parameters to the
2857 * {@link Drawable} returned by
2858 * {@link android.view.View#getBackground()}. Otherwise, assume
2859 * the target view is an {@link ImageView} and apply them to
2860 * {@link ImageView#getDrawable()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002861 * @param colorFilter Specify a color for a
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02002862 * {@link android.graphics.ColorFilter} for this drawable. This will be ignored if
2863 * {@code mode} is {@code null}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002864 * @param mode Specify a PorterDuff mode for this drawable, or null to leave
2865 * unchanged.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002866 */
Sunny Goyal5b153922017-09-21 21:00:36 -07002867 public void setDrawableTint(int viewId, boolean targetBackground,
2868 int colorFilter, @NonNull PorterDuff.Mode mode) {
2869 addAction(new SetDrawableTint(viewId, targetBackground, colorFilter, mode));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002870 }
2871
2872 /**
Jorim Jaggief72a192014-08-26 21:57:46 +02002873 * @hide
Gus Prevas9cc96602018-10-11 11:24:23 -04002874 * Equivalent to calling
2875 * {@link RippleDrawable#setColor(ColorStateList)} on the {@link Drawable} of a given view,
2876 * assuming it's a {@link RippleDrawable}.
2877 * <p>
2878 *
2879 * @param viewId The id of the view that contains the target
2880 * {@link RippleDrawable}
2881 * @param colorStateList Specify a color for a
2882 * {@link ColorStateList} for this drawable.
2883 */
2884 public void setRippleDrawableColor(int viewId, ColorStateList colorStateList) {
2885 addAction(new SetRippleDrawableColor(viewId, colorStateList));
2886 }
2887
2888 /**
2889 * @hide
Jorim Jaggief72a192014-08-26 21:57:46 +02002890 * Equivalent to calling {@link android.widget.ProgressBar#setProgressTintList}.
2891 *
2892 * @param viewId The id of the view whose tint should change
2893 * @param tint the tint to apply, may be {@code null} to clear tint
2894 */
2895 public void setProgressTintList(int viewId, ColorStateList tint) {
2896 addAction(new ReflectionAction(viewId, "setProgressTintList",
2897 ReflectionAction.COLOR_STATE_LIST, tint));
2898 }
2899
2900 /**
2901 * @hide
2902 * Equivalent to calling {@link android.widget.ProgressBar#setProgressBackgroundTintList}.
2903 *
2904 * @param viewId The id of the view whose tint should change
2905 * @param tint the tint to apply, may be {@code null} to clear tint
2906 */
2907 public void setProgressBackgroundTintList(int viewId, ColorStateList tint) {
2908 addAction(new ReflectionAction(viewId, "setProgressBackgroundTintList",
2909 ReflectionAction.COLOR_STATE_LIST, tint));
2910 }
2911
2912 /**
2913 * @hide
2914 * Equivalent to calling {@link android.widget.ProgressBar#setIndeterminateTintList}.
2915 *
2916 * @param viewId The id of the view whose tint should change
2917 * @param tint the tint to apply, may be {@code null} to clear tint
2918 */
2919 public void setProgressIndeterminateTintList(int viewId, ColorStateList tint) {
2920 addAction(new ReflectionAction(viewId, "setIndeterminateTintList",
2921 ReflectionAction.COLOR_STATE_LIST, tint));
2922 }
2923
2924 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002925 * Equivalent to calling {@link android.widget.TextView#setTextColor(int)}.
Jim Millere667a7a2012-08-09 19:22:32 -07002926 *
Scott Main93dc6422012-02-24 12:04:06 -08002927 * @param viewId The id of the view whose text color should change
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002928 * @param color Sets the text color for all the states (normal, selected,
2929 * focused) to be this color.
2930 */
Tor Norbye80756e32015-03-02 09:39:27 -08002931 public void setTextColor(int viewId, @ColorInt int color) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002932 setInt(viewId, "setTextColor", color);
2933 }
2934
Joe Onorato592d0652009-03-24 22:25:52 -07002935 /**
Selim Cinek981962e2016-07-20 20:41:58 -07002936 * @hide
2937 * Equivalent to calling {@link android.widget.TextView#setTextColor(ColorStateList)}.
2938 *
2939 * @param viewId The id of the view whose text color should change
2940 * @param colors the text colors to set
2941 */
2942 public void setTextColor(int viewId, @ColorInt ColorStateList colors) {
2943 addAction(new ReflectionAction(viewId, "setTextColor", ReflectionAction.COLOR_STATE_LIST,
2944 colors));
2945 }
2946
2947 /**
Adam Cohen3b4ca102012-12-14 12:00:41 -08002948 * Equivalent to calling {@link android.widget.AbsListView#setRemoteViewsAdapter(Intent)}.
Winson Chung499cb9f2010-07-16 11:18:17 -07002949 *
Winson Chung037300b2011-03-29 15:40:16 -07002950 * @param appWidgetId The id of the app widget which contains the specified view. (This
2951 * parameter is ignored in this deprecated method)
Adam Cohen50f3d1b2012-12-11 18:36:07 -08002952 * @param viewId The id of the {@link AdapterView}
Winson Chung037300b2011-03-29 15:40:16 -07002953 * @param intent The intent of the service which will be
2954 * providing data to the RemoteViewsAdapter
2955 * @deprecated This method has been deprecated. See
2956 * {@link android.widget.RemoteViews#setRemoteAdapter(int, Intent)}
2957 */
2958 @Deprecated
2959 public void setRemoteAdapter(int appWidgetId, int viewId, Intent intent) {
2960 setRemoteAdapter(viewId, intent);
2961 }
2962
2963 /**
2964 * Equivalent to calling {@link android.widget.AbsListView#setRemoteViewsAdapter(Intent)}.
2965 * Can only be used for App Widgets.
2966 *
Adam Cohen50f3d1b2012-12-11 18:36:07 -08002967 * @param viewId The id of the {@link AdapterView}
Winson Chung81f39eb2011-01-11 18:05:01 -08002968 * @param intent The intent of the service which will be
2969 * providing data to the RemoteViewsAdapter
2970 */
Winson Chung037300b2011-03-29 15:40:16 -07002971 public void setRemoteAdapter(int viewId, Intent intent) {
2972 addAction(new SetRemoteViewsAdapterIntent(viewId, intent));
Winson Chung499cb9f2010-07-16 11:18:17 -07002973 }
2974
2975 /**
Adam Cohen50f3d1b2012-12-11 18:36:07 -08002976 * Creates a simple Adapter for the viewId specified. The viewId must point to an AdapterView,
2977 * ie. {@link ListView}, {@link GridView}, {@link StackView} or {@link AdapterViewAnimator}.
2978 * This is a simpler but less flexible approach to populating collection widgets. Its use is
2979 * encouraged for most scenarios, as long as the total memory within the list of RemoteViews
2980 * is relatively small (ie. doesn't contain large or numerous Bitmaps, see {@link
2981 * RemoteViews#setImageViewBitmap}). In the case of numerous images, the use of API is still
2982 * possible by setting image URIs instead of Bitmaps, see {@link RemoteViews#setImageViewUri}.
2983 *
2984 * This API is supported in the compatibility library for previous API levels, see
2985 * RemoteViewsCompat.
2986 *
2987 * @param viewId The id of the {@link AdapterView}
2988 * @param list The list of RemoteViews which will populate the view specified by viewId.
Adam Cohenb00d9f02013-01-10 14:12:52 -08002989 * @param viewTypeCount The maximum number of unique layout id's used to construct the list of
2990 * RemoteViews. This count cannot change during the life-cycle of a given widget, so this
2991 * parameter should account for the maximum possible number of types that may appear in the
2992 * See {@link Adapter#getViewTypeCount()}.
Adam Cohen33f3aab2013-04-17 13:48:17 -07002993 *
2994 * @hide
Adam Powellf4d604f2019-02-04 15:28:42 -08002995 * @deprecated this appears to have no users outside of UnsupportedAppUsage?
Adam Cohen50f3d1b2012-12-11 18:36:07 -08002996 */
Mathew Inwood3a75f262019-06-27 12:47:38 +01002997 @UnsupportedAppUsage
2998 @Deprecated
Adam Cohenb00d9f02013-01-10 14:12:52 -08002999 public void setRemoteAdapter(int viewId, ArrayList<RemoteViews> list, int viewTypeCount) {
3000 addAction(new SetRemoteViewsAdapterList(viewId, list, viewTypeCount));
Adam Cohen50f3d1b2012-12-11 18:36:07 -08003001 }
3002
3003 /**
Kirill Grouchnikov40a39782016-07-01 17:24:32 -04003004 * Equivalent to calling {@link ListView#smoothScrollToPosition(int)}.
Winson Chung499cb9f2010-07-16 11:18:17 -07003005 *
Scott Main93dc6422012-02-24 12:04:06 -08003006 * @param viewId The id of the view to change
Winson Chung499cb9f2010-07-16 11:18:17 -07003007 * @param position Scroll to this adapter position
3008 */
3009 public void setScrollPosition(int viewId, int position) {
3010 setInt(viewId, "smoothScrollToPosition", position);
3011 }
3012
3013 /**
Kirill Grouchnikov40a39782016-07-01 17:24:32 -04003014 * Equivalent to calling {@link ListView#smoothScrollByOffset(int)}.
Winson Chung499cb9f2010-07-16 11:18:17 -07003015 *
Scott Main93dc6422012-02-24 12:04:06 -08003016 * @param viewId The id of the view to change
Winson Chung95362592010-07-19 16:05:50 -07003017 * @param offset Scroll by this adapter position offset
Winson Chung499cb9f2010-07-16 11:18:17 -07003018 */
3019 public void setRelativeScrollPosition(int viewId, int offset) {
3020 setInt(viewId, "smoothScrollByOffset", offset);
3021 }
3022
3023 /**
Daniel Sandlerd5353b42012-06-21 09:28:07 -04003024 * Equivalent to calling {@link android.view.View#setPadding(int, int, int, int)}.
Daniel Sandler99d1f742012-05-21 16:14:14 -04003025 *
3026 * @param viewId The id of the view to change
3027 * @param left the left padding in pixels
3028 * @param top the top padding in pixels
3029 * @param right the right padding in pixels
3030 * @param bottom the bottom padding in pixels
3031 */
3032 public void setViewPadding(int viewId, int left, int top, int right, int bottom) {
3033 addAction(new ViewPaddingAction(viewId, left, top, right, bottom));
3034 }
3035
3036 /**
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003037 * @hide
3038 * Equivalent to calling {@link android.view.ViewGroup.MarginLayoutParams#setMarginEnd(int)}.
3039 * Only works if the {@link View#getLayoutParams()} supports margins.
3040 * Hidden for now since we don't want to support this for all different layout margins yet.
3041 *
3042 * @param viewId The id of the view to change
Adrian Roos2d5dbba2016-06-08 17:11:53 -07003043 * @param endMarginDimen a dimen resource to read the margin from or 0 to clear the margin.
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003044 */
Adrian Roos2d5dbba2016-06-08 17:11:53 -07003045 public void setViewLayoutMarginEndDimen(int viewId, @DimenRes int endMarginDimen) {
3046 addAction(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_MARGIN_END_DIMEN,
3047 endMarginDimen));
Adrian Roos9b123cf2016-02-04 14:55:57 -08003048 }
3049
3050 /**
Selim Cinek384804b2018-04-18 14:31:07 +08003051 * Equivalent to calling {@link android.view.ViewGroup.MarginLayoutParams#setMarginEnd(int)}.
3052 * Only works if the {@link View#getLayoutParams()} supports margins.
3053 * Hidden for now since we don't want to support this for all different layout margins yet.
3054 *
3055 * @param viewId The id of the view to change
3056 * @param endMargin a value in pixels for the end margin.
3057 * @hide
3058 */
3059 public void setViewLayoutMarginEnd(int viewId, @DimenRes int endMargin) {
3060 addAction(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_MARGIN_END,
3061 endMargin));
3062 }
3063
3064 /**
Adrian Roosc1a80b02016-04-05 14:54:55 -07003065 * Equivalent to setting {@link android.view.ViewGroup.MarginLayoutParams#bottomMargin}.
3066 *
Adrian Roos2d5dbba2016-06-08 17:11:53 -07003067 * @param bottomMarginDimen a dimen resource to read the margin from or 0 to clear the margin.
Adrian Roosc1a80b02016-04-05 14:54:55 -07003068 * @hide
3069 */
Adrian Roos2d5dbba2016-06-08 17:11:53 -07003070 public void setViewLayoutMarginBottomDimen(int viewId, @DimenRes int bottomMarginDimen) {
3071 addAction(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_MARGIN_BOTTOM_DIMEN,
3072 bottomMarginDimen));
Adrian Roosc1a80b02016-04-05 14:54:55 -07003073 }
3074
3075 /**
Adrian Roos9b123cf2016-02-04 14:55:57 -08003076 * Equivalent to setting {@link android.view.ViewGroup.LayoutParams#width}.
Adrian Roos2d5dbba2016-06-08 17:11:53 -07003077 *
3078 * @param layoutWidth one of 0, MATCH_PARENT or WRAP_CONTENT. Other sizes are not allowed
3079 * because they behave poorly when the density changes.
Adrian Roos9b123cf2016-02-04 14:55:57 -08003080 * @hide
3081 */
3082 public void setViewLayoutWidth(int viewId, int layoutWidth) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07003083 if (layoutWidth != 0 && layoutWidth != ViewGroup.LayoutParams.MATCH_PARENT
3084 && layoutWidth != ViewGroup.LayoutParams.WRAP_CONTENT) {
3085 throw new IllegalArgumentException("Only supports 0, WRAP_CONTENT and MATCH_PARENT");
3086 }
Adrian Roos9b123cf2016-02-04 14:55:57 -08003087 mActions.add(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_WIDTH, layoutWidth));
Selim Cinek65b2e7c2015-10-26 14:11:31 -07003088 }
3089
3090 /**
Joe Onorato592d0652009-03-24 22:25:52 -07003091 * Call a method taking one boolean on a view in the layout for this RemoteViews.
3092 *
Scott Main93dc6422012-02-24 12:04:06 -08003093 * @param viewId The id of the view on which to call the method.
Joe Onorato592d0652009-03-24 22:25:52 -07003094 * @param methodName The name of the method to call.
3095 * @param value The value to pass to the method.
3096 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003097 public void setBoolean(int viewId, String methodName, boolean value) {
3098 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.BOOLEAN, value));
3099 }
3100
Joe Onorato592d0652009-03-24 22:25:52 -07003101 /**
3102 * Call a method taking one byte on a view in the layout for this RemoteViews.
3103 *
Scott Main93dc6422012-02-24 12:04:06 -08003104 * @param viewId The id of the view on which to call the method.
Joe Onorato592d0652009-03-24 22:25:52 -07003105 * @param methodName The name of the method to call.
3106 * @param value The value to pass to the method.
3107 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003108 public void setByte(int viewId, String methodName, byte value) {
3109 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.BYTE, value));
3110 }
3111
Joe Onorato592d0652009-03-24 22:25:52 -07003112 /**
3113 * Call a method taking one short on a view in the layout for this RemoteViews.
3114 *
Scott Main93dc6422012-02-24 12:04:06 -08003115 * @param viewId The id of the view on which to call the method.
Joe Onorato592d0652009-03-24 22:25:52 -07003116 * @param methodName The name of the method to call.
3117 * @param value The value to pass to the method.
3118 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003119 public void setShort(int viewId, String methodName, short value) {
3120 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.SHORT, value));
3121 }
3122
Joe Onorato592d0652009-03-24 22:25:52 -07003123 /**
3124 * Call a method taking one int on a view in the layout for this RemoteViews.
3125 *
Scott Main93dc6422012-02-24 12:04:06 -08003126 * @param viewId The id of the view on which to call the method.
Joe Onorato592d0652009-03-24 22:25:52 -07003127 * @param methodName The name of the method to call.
3128 * @param value The value to pass to the method.
3129 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003130 public void setInt(int viewId, String methodName, int value) {
3131 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.INT, value));
3132 }
3133
Joe Onorato592d0652009-03-24 22:25:52 -07003134 /**
Selim Cinek396caca2018-04-10 17:46:46 -07003135 * Call a method taking one ColorStateList on a view in the layout for this RemoteViews.
3136 *
3137 * @param viewId The id of the view on which to call the method.
3138 * @param methodName The name of the method to call.
3139 * @param value The value to pass to the method.
3140 *
3141 * @hide
3142 */
3143 public void setColorStateList(int viewId, String methodName, ColorStateList value) {
3144 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.COLOR_STATE_LIST,
3145 value));
3146 }
3147
3148
3149 /**
Joe Onorato592d0652009-03-24 22:25:52 -07003150 * Call a method taking one long on a view in the layout for this RemoteViews.
3151 *
Scott Main93dc6422012-02-24 12:04:06 -08003152 * @param viewId The id of the view on which to call the method.
Joe Onorato592d0652009-03-24 22:25:52 -07003153 * @param methodName The name of the method to call.
3154 * @param value The value to pass to the method.
3155 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003156 public void setLong(int viewId, String methodName, long value) {
3157 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.LONG, value));
3158 }
3159
Joe Onorato592d0652009-03-24 22:25:52 -07003160 /**
3161 * Call a method taking one float on a view in the layout for this RemoteViews.
3162 *
Scott Main93dc6422012-02-24 12:04:06 -08003163 * @param viewId The id of the view on which to call the method.
Joe Onorato592d0652009-03-24 22:25:52 -07003164 * @param methodName The name of the method to call.
3165 * @param value The value to pass to the method.
3166 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003167 public void setFloat(int viewId, String methodName, float value) {
3168 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.FLOAT, value));
3169 }
3170
Joe Onorato592d0652009-03-24 22:25:52 -07003171 /**
3172 * Call a method taking one double on a view in the layout for this RemoteViews.
3173 *
Scott Main93dc6422012-02-24 12:04:06 -08003174 * @param viewId The id of the view on which to call the method.
Joe Onorato592d0652009-03-24 22:25:52 -07003175 * @param methodName The name of the method to call.
3176 * @param value The value to pass to the method.
3177 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003178 public void setDouble(int viewId, String methodName, double value) {
3179 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.DOUBLE, value));
3180 }
3181
Joe Onorato592d0652009-03-24 22:25:52 -07003182 /**
3183 * Call a method taking one char on a view in the layout for this RemoteViews.
3184 *
Scott Main93dc6422012-02-24 12:04:06 -08003185 * @param viewId The id of the view on which to call the method.
Joe Onorato592d0652009-03-24 22:25:52 -07003186 * @param methodName The name of the method to call.
3187 * @param value The value to pass to the method.
3188 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003189 public void setChar(int viewId, String methodName, char value) {
3190 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.CHAR, value));
3191 }
3192
Joe Onorato592d0652009-03-24 22:25:52 -07003193 /**
3194 * Call a method taking one String on a view in the layout for this RemoteViews.
3195 *
Scott Main93dc6422012-02-24 12:04:06 -08003196 * @param viewId The id of the view on which to call the method.
Joe Onorato592d0652009-03-24 22:25:52 -07003197 * @param methodName The name of the method to call.
3198 * @param value The value to pass to the method.
3199 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003200 public void setString(int viewId, String methodName, String value) {
3201 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.STRING, value));
3202 }
3203
Joe Onorato592d0652009-03-24 22:25:52 -07003204 /**
3205 * Call a method taking one CharSequence on a view in the layout for this RemoteViews.
3206 *
Scott Main93dc6422012-02-24 12:04:06 -08003207 * @param viewId The id of the view on which to call the method.
Joe Onorato592d0652009-03-24 22:25:52 -07003208 * @param methodName The name of the method to call.
3209 * @param value The value to pass to the method.
3210 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003211 public void setCharSequence(int viewId, String methodName, CharSequence value) {
3212 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.CHAR_SEQUENCE, value));
3213 }
3214
Joe Onorato592d0652009-03-24 22:25:52 -07003215 /**
3216 * Call a method taking one Uri on a view in the layout for this RemoteViews.
3217 *
Scott Main93dc6422012-02-24 12:04:06 -08003218 * @param viewId The id of the view on which to call the method.
Joe Onorato592d0652009-03-24 22:25:52 -07003219 * @param methodName The name of the method to call.
3220 * @param value The value to pass to the method.
3221 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003222 public void setUri(int viewId, String methodName, Uri value) {
Jeff Sharkeya14acd22013-04-02 18:27:45 -07003223 if (value != null) {
3224 // Resolve any filesystem path before sending remotely
3225 value = value.getCanonicalUri();
3226 if (StrictMode.vmFileUriExposureEnabled()) {
3227 value.checkFileUriExposed("RemoteViews.setUri()");
3228 }
3229 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003230 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.URI, value));
3231 }
3232
Joe Onorato592d0652009-03-24 22:25:52 -07003233 /**
3234 * Call a method taking one Bitmap on a view in the layout for this RemoteViews.
3235 * @more
3236 * <p class="note">The bitmap will be flattened into the parcel if this object is
3237 * sent across processes, so it may end up using a lot of memory, and may be fairly slow.</p>
3238 *
Scott Main93dc6422012-02-24 12:04:06 -08003239 * @param viewId The id of the view on which to call the method.
Joe Onorato592d0652009-03-24 22:25:52 -07003240 * @param methodName The name of the method to call.
3241 * @param value The value to pass to the method.
3242 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003243 public void setBitmap(int viewId, String methodName, Bitmap value) {
Adam Cohen5d200642012-04-24 10:43:31 -07003244 addAction(new BitmapReflectionAction(viewId, methodName, value));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003245 }
3246
3247 /**
Bjorn Bringertd755b062010-01-06 17:15:37 +00003248 * Call a method taking one Bundle on a view in the layout for this RemoteViews.
3249 *
Scott Main93dc6422012-02-24 12:04:06 -08003250 * @param viewId The id of the view on which to call the method.
Bjorn Bringertd755b062010-01-06 17:15:37 +00003251 * @param methodName The name of the method to call.
3252 * @param value The value to pass to the method.
3253 */
3254 public void setBundle(int viewId, String methodName, Bundle value) {
3255 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.BUNDLE, value));
3256 }
3257
3258 /**
Scott Main93dc6422012-02-24 12:04:06 -08003259 * Call a method taking one Intent on a view in the layout for this RemoteViews.
Winson Chung499cb9f2010-07-16 11:18:17 -07003260 *
Scott Main93dc6422012-02-24 12:04:06 -08003261 * @param viewId The id of the view on which to call the method.
3262 * @param methodName The name of the method to call.
3263 * @param value The {@link android.content.Intent} to pass the method.
Winson Chung499cb9f2010-07-16 11:18:17 -07003264 */
3265 public void setIntent(int viewId, String methodName, Intent value) {
3266 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.INTENT, value));
3267 }
3268
3269 /**
Dan Sandlera22a3802015-05-13 00:12:47 -04003270 * Call a method taking one Icon on a view in the layout for this RemoteViews.
3271 *
3272 * @param viewId The id of the view on which to call the method.
3273 * @param methodName The name of the method to call.
3274 * @param value The {@link android.graphics.drawable.Icon} to pass the method.
3275 */
3276 public void setIcon(int viewId, String methodName, Icon value) {
3277 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.ICON, value));
3278 }
3279
3280 /**
Svetoslav Ganov33aef982012-09-13 12:49:03 -07003281 * Equivalent to calling View.setContentDescription(CharSequence).
Svetoslav Ganove261e282011-10-18 17:47:04 -07003282 *
Svetoslav Ganov33aef982012-09-13 12:49:03 -07003283 * @param viewId The id of the view whose content description should change.
3284 * @param contentDescription The new content description for the view.
Svetoslav Ganove261e282011-10-18 17:47:04 -07003285 */
3286 public void setContentDescription(int viewId, CharSequence contentDescription) {
3287 setCharSequence(viewId, "setContentDescription", contentDescription);
3288 }
3289
Svetoslav Ganov33aef982012-09-13 12:49:03 -07003290 /**
Svetoslav6c702902014-10-09 18:40:56 -07003291 * Equivalent to calling {@link android.view.View#setAccessibilityTraversalBefore(int)}.
3292 *
3293 * @param viewId The id of the view whose before view in accessibility traversal to set.
3294 * @param nextId The id of the next in the accessibility traversal.
3295 **/
3296 public void setAccessibilityTraversalBefore(int viewId, int nextId) {
3297 setInt(viewId, "setAccessibilityTraversalBefore", nextId);
3298 }
3299
3300 /**
3301 * Equivalent to calling {@link android.view.View#setAccessibilityTraversalAfter(int)}.
3302 *
3303 * @param viewId The id of the view whose after view in accessibility traversal to set.
3304 * @param nextId The id of the next in the accessibility traversal.
3305 **/
3306 public void setAccessibilityTraversalAfter(int viewId, int nextId) {
3307 setInt(viewId, "setAccessibilityTraversalAfter", nextId);
3308 }
3309
3310 /**
Kirill Grouchnikov40a39782016-07-01 17:24:32 -04003311 * Equivalent to calling {@link View#setLabelFor(int)}.
Svetoslav Ganov33aef982012-09-13 12:49:03 -07003312 *
3313 * @param viewId The id of the view whose property to set.
3314 * @param labeledId The id of a view for which this view serves as a label.
3315 */
3316 public void setLabelFor(int viewId, int labeledId) {
3317 setInt(viewId, "setLabelFor", labeledId);
3318 }
3319
Sunny Goyalc12d31c2018-11-12 16:29:18 -08003320 /**
3321 * Provides an alternate layout ID, which can be used to inflate this view. This layout will be
3322 * used by the host when the widgets displayed on a light-background where foreground elements
3323 * and text can safely draw using a dark color without any additional background protection.
3324 */
3325 public void setLightBackgroundLayoutId(@LayoutRes int layoutId) {
3326 mLightBackgroundLayoutId = layoutId;
3327 }
3328
3329 /**
3330 * If this view supports dark text versions, creates a copy representing that version,
3331 * otherwise returns itself.
3332 * @hide
3333 */
3334 public RemoteViews getDarkTextViews() {
3335 if (hasFlags(FLAG_USE_LIGHT_BACKGROUND_LAYOUT)) {
3336 return this;
3337 }
3338
3339 try {
3340 addFlags(FLAG_USE_LIGHT_BACKGROUND_LAYOUT);
3341 return new RemoteViews(this);
3342 } finally {
3343 mApplyFlags &= ~FLAG_USE_LIGHT_BACKGROUND_LAYOUT;
3344 }
3345 }
3346
Adam Cohen5d200642012-04-24 10:43:31 -07003347 private RemoteViews getRemoteViewsToApply(Context context) {
3348 if (hasLandscapeAndPortraitLayouts()) {
3349 int orientation = context.getResources().getConfiguration().orientation;
3350 if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
3351 return mLandscape;
3352 } else {
3353 return mPortrait;
3354 }
3355 }
3356 return this;
3357 }
3358
Svetoslav Ganove261e282011-10-18 17:47:04 -07003359 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003360 * Inflates the view hierarchy represented by this object and applies
3361 * all of the actions.
Jim Millere667a7a2012-08-09 19:22:32 -07003362 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003363 * <p><strong>Caller beware: this may throw</strong>
Jim Millere667a7a2012-08-09 19:22:32 -07003364 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003365 * @param context Default context to use
3366 * @param parent Parent that the resulting view hierarchy will be attached to. This method
3367 * does <strong>not</strong> attach the hierarchy. The caller should do so when appropriate.
3368 * @return The inflated view hierarchy
3369 */
3370 public View apply(Context context, ViewGroup parent) {
Jim Millere667a7a2012-08-09 19:22:32 -07003371 return apply(context, parent, null);
Dianne Hackborn1927ae82012-06-22 15:21:36 -07003372 }
3373
Dianne Hackborna1940212012-06-28 16:07:22 -07003374 /** @hide */
3375 public View apply(Context context, ViewGroup parent, OnClickHandler handler) {
Adam Cohen5d200642012-04-24 10:43:31 -07003376 RemoteViews rvToApply = getRemoteViewsToApply(context);
3377
Sunny Goyaldd292f42015-12-02 14:29:27 -08003378 View result = inflateView(context, rvToApply, parent);
Sunny Goyaldd292f42015-12-02 14:29:27 -08003379 rvToApply.performApply(result, parent, handler);
Sunny Goyal40f589c2018-11-12 11:34:32 -08003380 return result;
3381 }
Sunny Goyaldd292f42015-12-02 14:29:27 -08003382
Sunny Goyal40f589c2018-11-12 11:34:32 -08003383 /** @hide */
3384 public View applyWithTheme(Context context, ViewGroup parent, OnClickHandler handler,
3385 @StyleRes int applyThemeResId) {
3386 RemoteViews rvToApply = getRemoteViewsToApply(context);
3387
3388 View result = inflateView(context, rvToApply, parent, applyThemeResId);
3389 rvToApply.performApply(result, parent, handler);
Sunny Goyaldd292f42015-12-02 14:29:27 -08003390 return result;
3391 }
3392
3393 private View inflateView(Context context, RemoteViews rv, ViewGroup parent) {
Sunny Goyal40f589c2018-11-12 11:34:32 -08003394 return inflateView(context, rv, parent, 0);
3395 }
3396
3397 private View inflateView(Context context, RemoteViews rv, ViewGroup parent,
3398 @StyleRes int applyThemeResId) {
Kenny Guy77320062014-08-27 21:37:15 +01003399 // RemoteViews may be built by an application installed in another
3400 // user. So build a context that loads resources from that user but
3401 // still returns the current users userId so settings like data / time formats
3402 // are loaded without requiring cross user persmissions.
3403 final Context contextForResources = getContextForResources(context);
Adrian Roos83fad0a2017-04-24 16:58:26 -07003404 Context inflationContext = new RemoteViewsContextWrapper(context, contextForResources);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003405
Dake Gu36b86c22018-04-16 12:49:30 -07003406 // If mApplyThemeResId is not given, Theme.DeviceDefault will be used.
Sunny Goyal40f589c2018-11-12 11:34:32 -08003407 if (applyThemeResId != 0) {
3408 inflationContext = new ContextThemeWrapper(inflationContext, applyThemeResId);
Dake Gu36b86c22018-04-16 12:49:30 -07003409 }
Romain Guya5475592009-07-01 17:20:08 -07003410 LayoutInflater inflater = (LayoutInflater)
Kenny Guy77320062014-08-27 21:37:15 +01003411 context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003412
Kenny Guy77320062014-08-27 21:37:15 +01003413 // Clone inflater so we load resources from correct context and
3414 // we don't add a filter to the static version returned by getSystemService.
3415 inflater = inflater.cloneInContext(inflationContext);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003416 inflater.setFilter(this);
Sunny Goyal49e66952016-05-17 11:57:53 -07003417 View v = inflater.inflate(rv.getLayoutId(), parent, false);
3418 v.setTagInternal(R.id.widget_frame, rv.getLayoutId());
3419 return v;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003420 }
Adam Cohen5d200642012-04-24 10:43:31 -07003421
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003422 /**
Sunny Goyaldd292f42015-12-02 14:29:27 -08003423 * Implement this interface to receive a callback when
3424 * {@link #applyAsync} or {@link #reapplyAsync} is finished.
3425 * @hide
3426 */
3427 public interface OnViewAppliedListener {
Ahan Wude396fa2018-05-08 20:42:24 +08003428 /**
3429 * Callback when the RemoteView has finished inflating,
3430 * but no actions have been applied yet.
3431 */
3432 default void onViewInflated(View v) {};
3433
Sunny Goyaldd292f42015-12-02 14:29:27 -08003434 void onViewApplied(View v);
3435
3436 void onError(Exception e);
3437 }
3438
3439 /**
3440 * Applies the views asynchronously, moving as much of the task on the background
3441 * thread as possible.
3442 *
Elliot Waite54de7742017-01-11 15:30:35 -08003443 * @see #apply(Context, ViewGroup)
Sunny Goyaldd292f42015-12-02 14:29:27 -08003444 * @param context Default context to use
3445 * @param parent Parent that the resulting view hierarchy will be attached to. This method
3446 * does <strong>not</strong> attach the hierarchy. The caller should do so when appropriate.
3447 * @param listener the callback to run when all actions have been applied. May be null.
3448 * @param executor The executor to use. If null {@link AsyncTask#THREAD_POOL_EXECUTOR} is used.
3449 * @return CancellationSignal
3450 * @hide
3451 */
3452 public CancellationSignal applyAsync(
3453 Context context, ViewGroup parent, Executor executor, OnViewAppliedListener listener) {
3454 return applyAsync(context, parent, executor, listener, null);
3455 }
3456
3457 private CancellationSignal startTaskOnExecutor(AsyncApplyTask task, Executor executor) {
3458 CancellationSignal cancelSignal = new CancellationSignal();
3459 cancelSignal.setOnCancelListener(task);
3460
3461 task.executeOnExecutor(executor == null ? AsyncTask.THREAD_POOL_EXECUTOR : executor);
3462 return cancelSignal;
3463 }
3464
3465 /** @hide */
3466 public CancellationSignal applyAsync(Context context, ViewGroup parent,
3467 Executor executor, OnViewAppliedListener listener, OnClickHandler handler) {
3468 return startTaskOnExecutor(getAsyncApplyTask(context, parent, listener, handler), executor);
3469 }
3470
3471 private AsyncApplyTask getAsyncApplyTask(Context context, ViewGroup parent,
3472 OnViewAppliedListener listener, OnClickHandler handler) {
3473 return new AsyncApplyTask(getRemoteViewsToApply(context), parent, context, listener,
3474 handler, null);
3475 }
3476
3477 private class AsyncApplyTask extends AsyncTask<Void, Void, ViewTree>
3478 implements CancellationSignal.OnCancelListener {
3479 final RemoteViews mRV;
3480 final ViewGroup mParent;
3481 final Context mContext;
3482 final OnViewAppliedListener mListener;
3483 final OnClickHandler mHandler;
3484
3485 private View mResult;
3486 private ViewTree mTree;
3487 private Action[] mActions;
3488 private Exception mError;
3489
3490 private AsyncApplyTask(
3491 RemoteViews rv, ViewGroup parent, Context context, OnViewAppliedListener listener,
3492 OnClickHandler handler, View result) {
3493 mRV = rv;
3494 mParent = parent;
3495 mContext = context;
3496 mListener = listener;
3497 mHandler = handler;
3498
3499 mResult = result;
Sunny Goyaldd292f42015-12-02 14:29:27 -08003500 }
3501
3502 @Override
3503 protected ViewTree doInBackground(Void... params) {
3504 try {
3505 if (mResult == null) {
3506 mResult = inflateView(mContext, mRV, mParent);
3507 }
3508
3509 mTree = new ViewTree(mResult);
3510 if (mRV.mActions != null) {
3511 int count = mRV.mActions.size();
3512 mActions = new Action[count];
3513 for (int i = 0; i < count && !isCancelled(); i++) {
Sunny Goyal7b0e2c72016-11-03 14:48:05 -07003514 // TODO: check if isCancelled in nested views.
Sunny Goyaldd292f42015-12-02 14:29:27 -08003515 mActions[i] = mRV.mActions.get(i).initActionAsync(mTree, mParent, mHandler);
3516 }
3517 } else {
3518 mActions = null;
3519 }
3520 return mTree;
3521 } catch (Exception e) {
3522 mError = e;
3523 return null;
3524 }
3525 }
3526
3527 @Override
3528 protected void onPostExecute(ViewTree viewTree) {
3529 if (mError == null) {
Ahan Wude396fa2018-05-08 20:42:24 +08003530 if (mListener != null) {
3531 mListener.onViewInflated(viewTree.mRoot);
3532 }
3533
Sunny Goyaldd292f42015-12-02 14:29:27 -08003534 try {
3535 if (mActions != null) {
3536 OnClickHandler handler = mHandler == null
3537 ? DEFAULT_ON_CLICK_HANDLER : mHandler;
3538 for (Action a : mActions) {
3539 a.apply(viewTree.mRoot, mParent, handler);
3540 }
3541 }
3542 } catch (Exception e) {
3543 mError = e;
3544 }
3545 }
3546
3547 if (mListener != null) {
3548 if (mError != null) {
3549 mListener.onError(mError);
3550 } else {
3551 mListener.onViewApplied(viewTree.mRoot);
3552 }
3553 } else if (mError != null) {
3554 if (mError instanceof ActionException) {
3555 throw (ActionException) mError;
3556 } else {
3557 throw new ActionException(mError);
3558 }
3559 }
3560 }
3561
3562 @Override
3563 public void onCancel() {
3564 cancel(true);
3565 }
3566 }
3567
3568 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003569 * Applies all of the actions to the provided view.
3570 *
3571 * <p><strong>Caller beware: this may throw</strong>
Jim Millere667a7a2012-08-09 19:22:32 -07003572 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003573 * @param v The view to apply the actions to. This should be the result of
3574 * the {@link #apply(Context,ViewGroup)} call.
3575 */
3576 public void reapply(Context context, View v) {
Jim Millere667a7a2012-08-09 19:22:32 -07003577 reapply(context, v, null);
Dianne Hackborna1940212012-06-28 16:07:22 -07003578 }
3579
3580 /** @hide */
3581 public void reapply(Context context, View v, OnClickHandler handler) {
Adam Cohen5d200642012-04-24 10:43:31 -07003582 RemoteViews rvToApply = getRemoteViewsToApply(context);
3583
3584 // In the case that a view has this RemoteViews applied in one orientation, is persisted
3585 // across orientation change, and has the RemoteViews re-applied in the new orientation,
3586 // we throw an exception, since the layouts may be completely unrelated.
3587 if (hasLandscapeAndPortraitLayouts()) {
Sunny Goyal49e66952016-05-17 11:57:53 -07003588 if ((Integer) v.getTag(R.id.widget_frame) != rvToApply.getLayoutId()) {
Adam Cohen5d200642012-04-24 10:43:31 -07003589 throw new RuntimeException("Attempting to re-apply RemoteViews to a view that" +
3590 " that does not share the same root layout id.");
3591 }
3592 }
3593
Dianne Hackborna1940212012-06-28 16:07:22 -07003594 rvToApply.performApply(v, (ViewGroup) v.getParent(), handler);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003595 }
3596
Sunny Goyaldd292f42015-12-02 14:29:27 -08003597 /**
3598 * Applies all the actions to the provided view, moving as much of the task on the background
3599 * thread as possible.
3600 *
Elliot Waite54de7742017-01-11 15:30:35 -08003601 * @see #reapply(Context, View)
Sunny Goyaldd292f42015-12-02 14:29:27 -08003602 * @param context Default context to use
3603 * @param v The view to apply the actions to. This should be the result of
3604 * the {@link #apply(Context,ViewGroup)} call.
3605 * @param listener the callback to run when all actions have been applied. May be null.
3606 * @param executor The executor to use. If null {@link AsyncTask#THREAD_POOL_EXECUTOR} is used
3607 * @return CancellationSignal
3608 * @hide
3609 */
3610 public CancellationSignal reapplyAsync(
3611 Context context, View v, Executor executor, OnViewAppliedListener listener) {
3612 return reapplyAsync(context, v, executor, listener, null);
3613 }
3614
3615 /** @hide */
3616 public CancellationSignal reapplyAsync(Context context, View v, Executor executor,
3617 OnViewAppliedListener listener, OnClickHandler handler) {
3618 RemoteViews rvToApply = getRemoteViewsToApply(context);
3619
3620 // In the case that a view has this RemoteViews applied in one orientation, is persisted
3621 // across orientation change, and has the RemoteViews re-applied in the new orientation,
3622 // we throw an exception, since the layouts may be completely unrelated.
3623 if (hasLandscapeAndPortraitLayouts()) {
Sunny Goyal49e66952016-05-17 11:57:53 -07003624 if ((Integer) v.getTag(R.id.widget_frame) != rvToApply.getLayoutId()) {
Sunny Goyaldd292f42015-12-02 14:29:27 -08003625 throw new RuntimeException("Attempting to re-apply RemoteViews to a view that" +
3626 " that does not share the same root layout id.");
3627 }
3628 }
3629
3630 return startTaskOnExecutor(new AsyncApplyTask(rvToApply, (ViewGroup) v.getParent(),
3631 context, listener, handler, v), executor);
3632 }
3633
Dianne Hackborna1940212012-06-28 16:07:22 -07003634 private void performApply(View v, ViewGroup parent, OnClickHandler handler) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003635 if (mActions != null) {
Jim Millere667a7a2012-08-09 19:22:32 -07003636 handler = handler == null ? DEFAULT_ON_CLICK_HANDLER : handler;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003637 final int count = mActions.size();
3638 for (int i = 0; i < count; i++) {
3639 Action a = mActions.get(i);
Dianne Hackborna1940212012-06-28 16:07:22 -07003640 a.apply(v, parent, handler);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003641 }
3642 }
3643 }
3644
Sunny Goyal5c022632016-02-17 16:30:41 -08003645 /**
3646 * Returns true if the RemoteViews contains potentially costly operations and should be
3647 * applied asynchronously.
3648 *
3649 * @hide
3650 */
3651 public boolean prefersAsyncApply() {
3652 if (mActions != null) {
3653 final int count = mActions.size();
3654 for (int i = 0; i < count; i++) {
3655 if (mActions.get(i).prefersAsyncApply()) {
3656 return true;
3657 }
3658 }
3659 }
3660 return false;
3661 }
3662
Kenny Guy77320062014-08-27 21:37:15 +01003663 private Context getContextForResources(Context context) {
Svetoslav976e8bd2014-07-16 15:12:03 -07003664 if (mApplication != null) {
3665 if (context.getUserId() == UserHandle.getUserId(mApplication.uid)
3666 && context.getPackageName().equals(mApplication.packageName)) {
3667 return context;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003668 }
Svetoslav976e8bd2014-07-16 15:12:03 -07003669 try {
3670 return context.createApplicationContext(mApplication,
3671 Context.CONTEXT_RESTRICTED);
3672 } catch (NameNotFoundException e) {
Svet Ganov0da85b62014-08-06 14:11:37 -07003673 Log.e(LOG_TAG, "Package name " + mApplication.packageName + " not found");
Svetoslav976e8bd2014-07-16 15:12:03 -07003674 }
3675 }
3676
3677 return context;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003678 }
3679
Christoph Studer4600f9b2014-07-22 22:44:43 +02003680 /**
3681 * Returns the number of actions in this RemoteViews. Can be used as a sequence number.
3682 *
3683 * @hide
3684 */
3685 public int getSequenceNumber() {
3686 return (mActions == null) ? 0 : mActions.size();
3687 }
3688
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003689 /* (non-Javadoc)
3690 * Used to restrict the views which can be inflated
Jim Millere667a7a2012-08-09 19:22:32 -07003691 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003692 * @see android.view.LayoutInflater.Filter#onLoadClass(java.lang.Class)
3693 */
Gilles Debunnee6ac8b92010-06-17 10:55:04 -07003694 public boolean onLoadClass(Class clazz) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003695 return clazz.isAnnotationPresent(RemoteView.class);
3696 }
Adam Cohen5d200642012-04-24 10:43:31 -07003697
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003698 public int describeContents() {
3699 return 0;
3700 }
3701
3702 public void writeToParcel(Parcel dest, int flags) {
Adam Cohen5d200642012-04-24 10:43:31 -07003703 if (!hasLandscapeAndPortraitLayouts()) {
3704 dest.writeInt(MODE_NORMAL);
3705 // We only write the bitmap cache if we are the root RemoteViews, as this cache
3706 // is shared by all children.
3707 if (mIsRoot) {
3708 mBitmapCache.writeBitmapsToParcel(dest, flags);
3709 }
Sunny Goyal5d8bcdf2016-10-27 16:11:29 -07003710 if (!mIsRoot && (flags & PARCELABLE_ELIDE_DUPLICATES) != 0) {
3711 dest.writeInt(0);
3712 } else {
3713 dest.writeInt(1);
3714 mApplication.writeToParcel(dest, flags);
3715 }
Adam Cohen5d200642012-04-24 10:43:31 -07003716 dest.writeInt(mLayoutId);
Sunny Goyalc12d31c2018-11-12 16:29:18 -08003717 dest.writeInt(mLightBackgroundLayoutId);
Sunny Goyal5b7689f2017-09-21 11:08:34 -07003718 writeActionsToParcel(dest);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003719 } else {
Adam Cohen5d200642012-04-24 10:43:31 -07003720 dest.writeInt(MODE_HAS_LANDSCAPE_AND_PORTRAIT);
3721 // We only write the bitmap cache if we are the root RemoteViews, as this cache
3722 // is shared by all children.
3723 if (mIsRoot) {
3724 mBitmapCache.writeBitmapsToParcel(dest, flags);
3725 }
3726 mLandscape.writeToParcel(dest, flags);
Sunny Goyal5d8bcdf2016-10-27 16:11:29 -07003727 // Both RemoteViews already share the same package and user
3728 mPortrait.writeToParcel(dest, flags | PARCELABLE_ELIDE_DUPLICATES);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003729 }
Sunny Goyalc12d31c2018-11-12 16:29:18 -08003730 dest.writeInt(mApplyFlags);
Svet Ganov0da85b62014-08-06 14:11:37 -07003731 }
Svetoslav976e8bd2014-07-16 15:12:03 -07003732
Sunny Goyal5b7689f2017-09-21 11:08:34 -07003733 private void writeActionsToParcel(Parcel parcel) {
3734 int count;
3735 if (mActions != null) {
3736 count = mActions.size();
3737 } else {
3738 count = 0;
3739 }
3740 parcel.writeInt(count);
3741 for (int i = 0; i < count; i++) {
3742 Action a = mActions.get(i);
Sunny Goyal5b153922017-09-21 21:00:36 -07003743 parcel.writeInt(a.getActionTag());
Sunny Goyal5b7689f2017-09-21 11:08:34 -07003744 a.writeToParcel(parcel, a.hasSameAppInfo(mApplication)
3745 ? PARCELABLE_ELIDE_DUPLICATES : 0);
3746 }
3747 }
3748
Svet Ganov0da85b62014-08-06 14:11:37 -07003749 private static ApplicationInfo getApplicationInfo(String packageName, int userId) {
Svetoslavb6242442014-09-19 13:21:55 -07003750 if (packageName == null) {
3751 return null;
3752 }
3753
Svet Ganov0da85b62014-08-06 14:11:37 -07003754 // Get the application for the passed in package and user.
3755 Application application = ActivityThread.currentApplication();
3756 if (application == null) {
3757 throw new IllegalStateException("Cannot create remote views out of an aplication.");
3758 }
3759
3760 ApplicationInfo applicationInfo = application.getApplicationInfo();
3761 if (UserHandle.getUserId(applicationInfo.uid) != userId
3762 || !applicationInfo.packageName.equals(packageName)) {
3763 try {
Svetoslav14494a82014-08-18 10:43:27 -07003764 Context context = application.getBaseContext().createPackageContextAsUser(
Svet Ganov0da85b62014-08-06 14:11:37 -07003765 packageName, 0, new UserHandle(userId));
3766 applicationInfo = context.getApplicationInfo();
3767 } catch (NameNotFoundException nnfe) {
3768 throw new IllegalArgumentException("No such package " + packageName);
3769 }
3770 }
3771
3772 return applicationInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003773 }
3774
3775 /**
Sunny Goyaldd60f4d2017-10-18 15:22:42 -07003776 * Returns true if the {@link #mApplication} is same as the provided info.
3777 *
3778 * @hide
3779 */
3780 public boolean hasSameAppInfo(ApplicationInfo info) {
3781 return mApplication.packageName.equals(info.packageName) && mApplication.uid == info.uid;
3782 }
3783
3784 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003785 * Parcelable.Creator that instantiates RemoteViews objects
3786 */
Jeff Sharkey9e8f83d2019-02-28 12:06:45 -07003787 public static final @android.annotation.NonNull Parcelable.Creator<RemoteViews> CREATOR = new Parcelable.Creator<RemoteViews>() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003788 public RemoteViews createFromParcel(Parcel parcel) {
3789 return new RemoteViews(parcel);
3790 }
3791
3792 public RemoteViews[] newArray(int size) {
3793 return new RemoteViews[size];
3794 }
3795 };
Sunny Goyaldd292f42015-12-02 14:29:27 -08003796
3797 /**
3798 * A representation of the view hierarchy. Only views which have a valid ID are added
3799 * and can be searched.
3800 */
3801 private static class ViewTree {
Anthony Chen8f5f3582017-04-11 11:18:37 -07003802 private static final int INSERT_AT_END_INDEX = -1;
Sunny Goyal7b0e2c72016-11-03 14:48:05 -07003803 private View mRoot;
Sunny Goyaldd292f42015-12-02 14:29:27 -08003804 private ArrayList<ViewTree> mChildren;
3805
3806 private ViewTree(View root) {
3807 mRoot = root;
3808 }
3809
3810 public void createTree() {
3811 if (mChildren != null) {
3812 return;
3813 }
3814
3815 mChildren = new ArrayList<>();
Sunny Goyal7b0e2c72016-11-03 14:48:05 -07003816 if (mRoot instanceof ViewGroup) {
Sunny Goyaldd292f42015-12-02 14:29:27 -08003817 ViewGroup vg = (ViewGroup) mRoot;
3818 int count = vg.getChildCount();
3819 for (int i = 0; i < count; i++) {
3820 addViewChild(vg.getChildAt(i));
3821 }
3822 }
3823 }
3824
3825 public ViewTree findViewTreeById(int id) {
3826 if (mRoot.getId() == id) {
3827 return this;
3828 }
3829 if (mChildren == null) {
3830 return null;
3831 }
3832 for (ViewTree tree : mChildren) {
3833 ViewTree result = tree.findViewTreeById(id);
3834 if (result != null) {
3835 return result;
3836 }
3837 }
3838 return null;
3839 }
3840
Sunny Goyal7b0e2c72016-11-03 14:48:05 -07003841 public void replaceView(View v) {
3842 mRoot = v;
3843 mChildren = null;
3844 createTree();
3845 }
3846
Alan Viverette04fd4702017-04-13 16:37:06 -04003847 public <T extends View> T findViewById(int id) {
Sunny Goyaldd292f42015-12-02 14:29:27 -08003848 if (mChildren == null) {
3849 return mRoot.findViewById(id);
3850 }
3851 ViewTree tree = findViewTreeById(id);
Alan Viverette04fd4702017-04-13 16:37:06 -04003852 return tree == null ? null : (T) tree.mRoot;
Sunny Goyaldd292f42015-12-02 14:29:27 -08003853 }
3854
3855 public void addChild(ViewTree child) {
Anthony Chen8f5f3582017-04-11 11:18:37 -07003856 addChild(child, INSERT_AT_END_INDEX);
3857 }
3858
3859 /**
3860 * Adds the given {@link ViewTree} as a child at the given index.
3861 *
3862 * @param index The position at which to add the child or -1 to add last.
3863 */
3864 public void addChild(ViewTree child, int index) {
Sunny Goyaldd292f42015-12-02 14:29:27 -08003865 if (mChildren == null) {
3866 mChildren = new ArrayList<>();
3867 }
3868 child.createTree();
Anthony Chen8f5f3582017-04-11 11:18:37 -07003869
3870 if (index == INSERT_AT_END_INDEX) {
3871 mChildren.add(child);
3872 return;
3873 }
3874
3875 mChildren.add(index, child);
Sunny Goyaldd292f42015-12-02 14:29:27 -08003876 }
3877
3878 private void addViewChild(View v) {
Sunny Goyal7b0e2c72016-11-03 14:48:05 -07003879 // ViewTree only contains Views which can be found using findViewById.
3880 // If isRootNamespace is true, this view is skipped.
3881 // @see ViewGroup#findViewTraversal(int)
3882 if (v.isRootNamespace()) {
3883 return;
3884 }
Sunny Goyaldd292f42015-12-02 14:29:27 -08003885 final ViewTree target;
3886
3887 // If the view has a valid id, i.e., if can be found using findViewById, add it to the
3888 // tree, otherwise skip this view and add its children instead.
3889 if (v.getId() != 0) {
3890 ViewTree tree = new ViewTree(v);
3891 mChildren.add(tree);
3892 target = tree;
3893 } else {
3894 target = this;
3895 }
3896
Sunny Goyal7b0e2c72016-11-03 14:48:05 -07003897 if (v instanceof ViewGroup) {
Sunny Goyaldd292f42015-12-02 14:29:27 -08003898 if (target.mChildren == null) {
3899 target.mChildren = new ArrayList<>();
3900 ViewGroup vg = (ViewGroup) v;
3901 int count = vg.getChildCount();
3902 for (int i = 0; i < count; i++) {
3903 target.addViewChild(vg.getChildAt(i));
3904 }
3905 }
3906 }
3907 }
3908 }
Sunny Goyal43c97042018-08-23 15:21:26 -07003909
3910 /**
3911 * Class representing a response to an action performed on any element of a RemoteViews.
3912 */
3913 public static class RemoteResponse {
3914
3915 private PendingIntent mPendingIntent;
3916 private Intent mFillIntent;
3917
3918 private IntArray mViewIds;
3919 private ArrayList<String> mElementNames;
3920
3921 /**
3922 * Creates a response which sends a pending intent as part of the response. The source
3923 * bounds ({@link Intent#getSourceBounds()}) of the intent will be set to the bounds of the
3924 * target view in screen space.
3925 * Note that any activity options associated with the mPendingIntent may get overridden
3926 * before starting the intent.
3927 *
3928 * @param pendingIntent The {@link PendingIntent} to send as part of the response
3929 */
Sunny Goyal40635d72019-03-01 11:46:44 -08003930 @NonNull
3931 public static RemoteResponse fromPendingIntent(@NonNull PendingIntent pendingIntent) {
Sunny Goyal43c97042018-08-23 15:21:26 -07003932 RemoteResponse response = new RemoteResponse();
3933 response.mPendingIntent = pendingIntent;
3934 return response;
3935 }
3936
3937 /**
3938 * When using collections (eg. {@link ListView}, {@link StackView} etc.) in widgets, it is
3939 * very costly to set PendingIntents on the individual items, and is hence not permitted.
3940 * Instead a single PendingIntent template can be set on the collection, see {@link
3941 * RemoteViews#setPendingIntentTemplate(int, PendingIntent)}, and the individual on-click
3942 * action of a given item can be distinguished by setting a fillInIntent on that item. The
3943 * fillInIntent is then combined with the PendingIntent template in order to determine the
3944 * final intent which will be executed when the item is clicked. This works as follows: any
3945 * fields which are left blank in the PendingIntent template, but are provided by the
3946 * fillInIntent will be overwritten, and the resulting PendingIntent will be used. The rest
3947 * of the PendingIntent template will then be filled in with the associated fields that are
3948 * set in fillInIntent. See {@link Intent#fillIn(Intent, int)} for more details.
3949 * Creates a response which sends a pending intent as part of the response. The source
3950 * bounds ({@link Intent#getSourceBounds()}) of the intent will be set to the bounds of the
3951 * target view in screen space.
3952 * Note that any activity options associated with the mPendingIntent may get overridden
3953 * before starting the intent.
3954 *
3955 * @param fillIntent The intent which will be combined with the parent's PendingIntent in
3956 * order to determine the behavior of the response
3957 *
3958 * @see RemoteViews#setPendingIntentTemplate(int, PendingIntent)
3959 * @see RemoteViews#setOnClickFillInIntent(int, Intent)
3960 * @return
3961 */
Sunny Goyal40635d72019-03-01 11:46:44 -08003962 @NonNull
3963 public static RemoteResponse fromFillInIntent(@NonNull Intent fillIntent) {
Sunny Goyal43c97042018-08-23 15:21:26 -07003964 RemoteResponse response = new RemoteResponse();
3965 response.mFillIntent = fillIntent;
3966 return response;
3967 }
3968
3969 /**
3970 * Adds a shared element to be transferred as part of the transition between Activities
3971 * using cross-Activity scene animations. The position of the first element will be used as
3972 * the epicenter for the exit Transition. The position of the associated shared element in
3973 * the launched Activity will be the epicenter of its entering Transition.
3974 *
3975 * @param viewId The id of the view to be shared as part of the transition
3976 * @param sharedElementName The shared element name for this view
3977 *
3978 * @see ActivityOptions#makeSceneTransitionAnimation(Activity, Pair[])
3979 */
Sunny Goyal40635d72019-03-01 11:46:44 -08003980 @NonNull
3981 public RemoteResponse addSharedElement(int viewId, @NonNull String sharedElementName) {
Sunny Goyal43c97042018-08-23 15:21:26 -07003982 if (mViewIds == null) {
3983 mViewIds = new IntArray();
3984 mElementNames = new ArrayList<>();
3985 }
3986 mViewIds.add(viewId);
3987 mElementNames.add(sharedElementName);
3988 return this;
3989 }
3990
3991 private void writeToParcel(Parcel dest, int flags) {
3992 PendingIntent.writePendingIntentOrNullToParcel(mPendingIntent, dest);
3993 if (mPendingIntent == null) {
3994 // Only write the intent if pending intent is null
3995 dest.writeTypedObject(mFillIntent, flags);
3996 }
3997 dest.writeIntArray(mViewIds == null ? null : mViewIds.toArray());
3998 dest.writeStringList(mElementNames);
3999 }
4000
4001 private void readFromParcel(Parcel parcel) {
4002 mPendingIntent = PendingIntent.readPendingIntentOrNullFromParcel(parcel);
4003 if (mPendingIntent == null) {
4004 mFillIntent = parcel.readTypedObject(Intent.CREATOR);
4005 }
4006 int[] viewIds = parcel.createIntArray();
4007 mViewIds = viewIds == null ? null : IntArray.wrap(viewIds);
4008 mElementNames = parcel.createStringArrayList();
4009 }
4010
4011 private void handleViewClick(View v, OnClickHandler handler) {
4012 final PendingIntent pi;
4013 if (mPendingIntent != null) {
4014 pi = mPendingIntent;
4015 } else if (mFillIntent != null) {
4016 // Insure that this view is a child of an AdapterView
4017 View parent = (View) v.getParent();
4018 // Break the for loop on the first encounter of:
4019 // 1) an AdapterView,
4020 // 2) an AppWidgetHostView that is not a RemoteViewsFrameLayout, or
4021 // 3) a null parent.
4022 // 2) and 3) are unexpected and catch the case where a child is not
4023 // correctly parented in an AdapterView.
4024 while (parent != null && !(parent instanceof AdapterView<?>)
4025 && !((parent instanceof AppWidgetHostView)
4026 && !(parent instanceof RemoteViewsAdapter.RemoteViewsFrameLayout))) {
4027 parent = (View) parent.getParent();
4028 }
4029
4030 if (!(parent instanceof AdapterView<?>)) {
4031 // Somehow they've managed to get this far without having
4032 // and AdapterView as a parent.
4033 Log.e(LOG_TAG, "Collection item doesn't have AdapterView parent");
4034 return;
4035 }
4036 // Insure that a template pending intent has been set on an ancestor
4037 if (!(parent.getTag() instanceof PendingIntent)) {
4038 Log.e(LOG_TAG, "Attempting setOnClickFillInIntent without"
4039 + " calling setPendingIntentTemplate on parent.");
4040 return;
4041 }
4042
4043 pi = (PendingIntent) parent.getTag();
4044 } else {
4045 Log.e(LOG_TAG, "Response has neither pendingIntent nor fillInIntent");
4046 return;
4047 }
4048
4049 handler.onClickHandler(v, pi, this);
4050 }
4051
4052 /** @hide */
4053 public Pair<Intent, ActivityOptions> getLaunchOptions(View view) {
4054 Intent intent = mPendingIntent != null ? new Intent() : new Intent(mFillIntent);
4055 intent.setSourceBounds(getSourceBounds(view));
4056
4057 ActivityOptions opts = null;
4058
4059 Context context = view.getContext();
4060 if (context.getResources().getBoolean(
4061 com.android.internal.R.bool.config_overrideRemoteViewsActivityTransition)) {
4062 TypedArray windowStyle = context.getTheme().obtainStyledAttributes(
4063 com.android.internal.R.styleable.Window);
4064 int windowAnimations = windowStyle.getResourceId(
4065 com.android.internal.R.styleable.Window_windowAnimationStyle, 0);
4066 TypedArray windowAnimationStyle = context.obtainStyledAttributes(
4067 windowAnimations, com.android.internal.R.styleable.WindowAnimation);
4068 int enterAnimationId = windowAnimationStyle.getResourceId(com.android.internal.R
4069 .styleable.WindowAnimation_activityOpenRemoteViewsEnterAnimation, 0);
4070 windowStyle.recycle();
4071 windowAnimationStyle.recycle();
4072
4073 if (enterAnimationId != 0) {
4074 opts = ActivityOptions.makeCustomAnimation(context,
4075 enterAnimationId, 0);
4076 opts.setPendingIntentLaunchFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
4077 }
4078 }
4079
4080 if (opts == null && mViewIds != null && mElementNames != null) {
4081 View parent = (View) view.getParent();
4082 while (parent != null && !(parent instanceof AppWidgetHostView)) {
4083 parent = (View) parent.getParent();
4084 }
4085 if (parent instanceof AppWidgetHostView) {
4086 opts = ((AppWidgetHostView) parent).createSharedElementActivityOptions(
4087 mViewIds.toArray(),
4088 mElementNames.toArray(new String[mElementNames.size()]), intent);
4089 }
4090 }
4091
4092 if (opts == null) {
4093 opts = ActivityOptions.makeBasic();
4094 opts.setPendingIntentLaunchFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
4095 }
4096 return Pair.create(intent, opts);
4097 }
4098 }
4099
4100 /** @hide */
4101 public static boolean startPendingIntent(View view, PendingIntent pendingIntent,
4102 Pair<Intent, ActivityOptions> options) {
4103 try {
4104 // TODO: Unregister this handler if PendingIntent.FLAG_ONE_SHOT?
4105 Context context = view.getContext();
4106 // The NEW_TASK flags are applied through the activity options and not as a part of
4107 // the call to startIntentSender() to ensure that they are consistently applied to
4108 // both mutable and immutable PendingIntents.
4109 context.startIntentSender(
4110 pendingIntent.getIntentSender(), options.first,
4111 0, 0, 0, options.second.toBundle());
4112 } catch (IntentSender.SendIntentException e) {
4113 Log.e(LOG_TAG, "Cannot send pending intent: ", e);
4114 return false;
4115 } catch (Exception e) {
4116 Log.e(LOG_TAG, "Cannot send pending intent due to unknown exception: ", e);
4117 return false;
4118 }
4119 return true;
4120 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004121}