blob: 08513aa8682e5c85a9ff6a087dbcc4a2c8b26565 [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
Wale Ogunwale0568aed2017-09-08 13:29:37 -070019import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
20
Tor Norbye80756e32015-03-02 09:39:27 -080021import android.annotation.ColorInt;
Adrian Roos2d5dbba2016-06-08 17:11:53 -070022import android.annotation.DimenRes;
Sunny Goyal5b153922017-09-21 21:00:36 -070023import android.annotation.NonNull;
Winson Chungdc6f79b2012-04-17 17:27:31 -070024import android.app.ActivityOptions;
Svetoslav976e8bd2014-07-16 15:12:03 -070025import android.app.ActivityThread;
Svet Ganov0da85b62014-08-06 14:11:37 -070026import android.app.Application;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080027import android.app.PendingIntent;
Adrian Roosfe84e1f2015-11-04 15:55:39 -080028import android.app.RemoteInput;
Adam Cohen1480fdd2010-08-25 17:24:53 -070029import android.appwidget.AppWidgetHostView;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080030import android.content.Context;
Kenny Guy77320062014-08-27 21:37:15 +010031import android.content.ContextWrapper;
Dianne Hackbornfa82f222009-09-17 15:14:12 -070032import android.content.Intent;
33import android.content.IntentSender;
Adam Cohenffc46a52012-04-30 19:54:39 -070034import android.content.pm.ApplicationInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080035import android.content.pm.PackageManager.NameNotFoundException;
Jorim Jaggief72a192014-08-26 21:57:46 +020036import android.content.res.ColorStateList;
Adam Cohen5d200642012-04-24 10:43:31 -070037import android.content.res.Configuration;
Kenny Guy77320062014-08-27 21:37:15 +010038import android.content.res.Resources;
Gus Prevas1ed322b2015-09-17 17:34:46 -040039import android.content.res.TypedArray;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080040import android.graphics.Bitmap;
41import android.graphics.PorterDuff;
Joe Onorato75970652009-12-02 23:04:55 -080042import android.graphics.Rect;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080043import android.graphics.drawable.Drawable;
Dan Sandlera22a3802015-05-13 00:12:47 -040044import android.graphics.drawable.Icon;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080045import android.net.Uri;
Sunny Goyaldd292f42015-12-02 14:29:27 -080046import android.os.AsyncTask;
Sunny Goyal692f8c92016-11-11 09:19:14 -080047import android.os.Binder;
Adam Cohenffc46a52012-04-30 19:54:39 -070048import android.os.Build;
Bjorn Bringertd755b062010-01-06 17:15:37 +000049import android.os.Bundle;
Sunny Goyaldd292f42015-12-02 14:29:27 -080050import android.os.CancellationSignal;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080051import android.os.Parcel;
52import android.os.Parcelable;
Sunny Goyal692f8c92016-11-11 09:19:14 -080053import android.os.Process;
Jeff Sharkeya14acd22013-04-02 18:27:45 -070054import android.os.StrictMode;
Jeff Sharkey6d515712012-09-20 16:06:08 -070055import android.os.UserHandle;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080056import android.text.TextUtils;
Romain Guye4d4e202013-07-22 13:02:02 -070057import android.util.ArrayMap;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080058import android.util.Log;
59import android.view.LayoutInflater;
Winson Chungdc6f79b2012-04-17 17:27:31 -070060import android.view.LayoutInflater.Filter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080061import android.view.RemotableViewMethod;
62import android.view.View;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080063import android.view.View.OnClickListener;
Winson Chungdc6f79b2012-04-17 17:27:31 -070064import android.view.ViewGroup;
Sunny Goyal7b0e2c72016-11-03 14:48:05 -070065import android.view.ViewStub;
Adam Cohena32edd42010-10-26 10:35:01 -070066import android.widget.AdapterView.OnItemClickListener;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080067
Gus Prevas1ed322b2015-09-17 17:34:46 -040068import com.android.internal.R;
Selim Cinek87c31532017-08-18 18:53:44 -070069import com.android.internal.util.NotificationColorUtil;
Adrian Roos7da889d2016-03-16 18:38:58 -070070import com.android.internal.util.Preconditions;
Gus Prevas1ed322b2015-09-17 17:34:46 -040071
Winson Chungdc6f79b2012-04-17 17:27:31 -070072import java.lang.annotation.ElementType;
73import java.lang.annotation.Retention;
74import java.lang.annotation.RetentionPolicy;
75import java.lang.annotation.Target;
Sunny Goyal271e3222017-08-29 16:05:47 -070076import java.lang.invoke.MethodHandle;
77import java.lang.invoke.MethodHandles;
78import java.lang.invoke.MethodType;
Winson Chungdc6f79b2012-04-17 17:27:31 -070079import java.lang.reflect.Method;
80import java.util.ArrayList;
Adam Cohenfbe44b72012-09-19 20:36:23 -070081import java.util.HashMap;
Adrian Roosfb921842017-10-26 14:49:56 +020082import java.util.Map;
Narayan Kamath607223f2018-02-19 14:09:02 +000083import java.util.Objects;
Selim Cinek87c31532017-08-18 18:53:44 -070084import java.util.Stack;
Sunny Goyaldd292f42015-12-02 14:29:27 -080085import java.util.concurrent.Executor;
Winson Chungdc6f79b2012-04-17 17:27:31 -070086
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080087/**
88 * A class that describes a view hierarchy that can be displayed in
89 * another process. The hierarchy is inflated from a layout resource
90 * file, and this class provides some basic operations for modifying
91 * the content of the inflated hierarchy.
Scott Main5b7903c2017-11-20 18:16:01 -080092 *
93 * <p>{@code RemoteViews} is limited to support for the following layouts:</p>
94 * <ul>
95 * <li>{@link android.widget.AdapterViewFlipper}</li>
96 * <li>{@link android.widget.FrameLayout}</li>
97 * <li>{@link android.widget.GridLayout}</li>
98 * <li>{@link android.widget.GridView}</li>
99 * <li>{@link android.widget.LinearLayout}</li>
100 * <li>{@link android.widget.ListView}</li>
101 * <li>{@link android.widget.RelativeLayout}</li>
102 * <li>{@link android.widget.StackView}</li>
103 * <li>{@link android.widget.ViewFlipper}</li>
104 * </ul>
105 * <p>And the following widgets:</p>
106 * <ul>
107 * <li>{@link android.widget.AnalogClock}</li>
108 * <li>{@link android.widget.Button}</li>
109 * <li>{@link android.widget.Chronometer}</li>
110 * <li>{@link android.widget.ImageButton}</li>
111 * <li>{@link android.widget.ImageView}</li>
112 * <li>{@link android.widget.ProgressBar}</li>
113 * <li>{@link android.widget.TextClock}</li>
114 * <li>{@link android.widget.TextView}</li>
115 * </ul>
116 * <p>Descendants of these classes are not supported.</p>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800117 */
118public class RemoteViews implements Parcelable, Filter {
Jim Millere667a7a2012-08-09 19:22:32 -0700119
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800120 private static final String LOG_TAG = "RemoteViews";
Jim Millere667a7a2012-08-09 19:22:32 -0700121
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800122 /**
Winson Chung81f39eb2011-01-11 18:05:01 -0800123 * The intent extra that contains the appWidgetId.
124 * @hide
125 */
126 static final String EXTRA_REMOTEADAPTER_APPWIDGET_ID = "remoteAdapterAppWidgetId";
127
128 /**
Sunny Goyal692f8c92016-11-11 09:19:14 -0800129 * Maximum depth of nested views calls from {@link #addView(int, RemoteViews)} and
130 * {@link #RemoteViews(RemoteViews, RemoteViews)}.
131 */
132 private static final int MAX_NESTED_VIEWS = 10;
133
Anthony Chen8f5f3582017-04-11 11:18:37 -0700134 // The unique identifiers for each custom {@link Action}.
135 private static final int SET_ON_CLICK_PENDING_INTENT_TAG = 1;
136 private static final int REFLECTION_ACTION_TAG = 2;
Sunny Goyal5b153922017-09-21 21:00:36 -0700137 private static final int SET_DRAWABLE_TINT_TAG = 3;
Anthony Chen8f5f3582017-04-11 11:18:37 -0700138 private static final int VIEW_GROUP_ACTION_ADD_TAG = 4;
Sunny Goyal5b153922017-09-21 21:00:36 -0700139 private static final int VIEW_CONTENT_NAVIGATION_TAG = 5;
Anthony Chen8f5f3582017-04-11 11:18:37 -0700140 private static final int SET_EMPTY_VIEW_ACTION_TAG = 6;
141 private static final int VIEW_GROUP_ACTION_REMOVE_TAG = 7;
142 private static final int SET_PENDING_INTENT_TEMPLATE_TAG = 8;
143 private static final int SET_ON_CLICK_FILL_IN_INTENT_TAG = 9;
144 private static final int SET_REMOTE_VIEW_ADAPTER_INTENT_TAG = 10;
145 private static final int TEXT_VIEW_DRAWABLE_ACTION_TAG = 11;
146 private static final int BITMAP_REFLECTION_ACTION_TAG = 12;
147 private static final int TEXT_VIEW_SIZE_ACTION_TAG = 13;
148 private static final int VIEW_PADDING_ACTION_TAG = 14;
149 private static final int SET_REMOTE_VIEW_ADAPTER_LIST_TAG = 15;
Anthony Chen8f5f3582017-04-11 11:18:37 -0700150 private static final int SET_REMOTE_INPUTS_ACTION_TAG = 18;
151 private static final int LAYOUT_PARAM_ACTION_TAG = 19;
Selim Cinek87c31532017-08-18 18:53:44 -0700152 private static final int OVERRIDE_TEXT_COLORS_TAG = 20;
Anthony Chen8f5f3582017-04-11 11:18:37 -0700153
Sunny Goyal692f8c92016-11-11 09:19:14 -0800154 /**
Svetoslav976e8bd2014-07-16 15:12:03 -0700155 * Application that hosts the remote views.
156 *
157 * @hide
Jeff Sharkey6d515712012-09-20 16:06:08 -0700158 */
Sunny Goyaldd60f4d2017-10-18 15:22:42 -0700159 public ApplicationInfo mApplication;
Jeff Sharkey6d515712012-09-20 16:06:08 -0700160
161 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800162 * The resource ID of the layout file. (Added to the parcel)
163 */
Gilles Debunne30301932010-06-16 18:32:00 -0700164 private final int mLayoutId;
Romain Guya5475592009-07-01 17:20:08 -0700165
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800166 /**
167 * An array of actions to perform on the view tree once it has been
168 * inflated
169 */
170 private ArrayList<Action> mActions;
Jim Millere667a7a2012-08-09 19:22:32 -0700171
Winson Chung3ec9a452010-09-23 16:40:28 -0700172 /**
Adam Cohen5d200642012-04-24 10:43:31 -0700173 * Maps bitmaps to unique indicies to avoid Bitmap duplication.
174 */
175 private BitmapCache mBitmapCache;
176
177 /**
178 * Indicates whether or not this RemoteViews object is contained as a child of any other
179 * RemoteViews.
180 */
181 private boolean mIsRoot = true;
182
183 /**
Selim Cinekfc8073c2017-08-16 17:50:20 -0700184 * Whether reapply is disallowed on this remoteview. This maybe be true if some actions modify
185 * the layout in a way that isn't recoverable, since views are being removed.
186 */
187 private boolean mReapplyDisallowed;
188
189 /**
Adam Cohen5d200642012-04-24 10:43:31 -0700190 * Constants to whether or not this RemoteViews is composed of a landscape and portrait
191 * RemoteViews.
192 */
193 private static final int MODE_NORMAL = 0;
194 private static final int MODE_HAS_LANDSCAPE_AND_PORTRAIT = 1;
195
196 /**
197 * Used in conjunction with the special constructor
198 * {@link #RemoteViews(RemoteViews, RemoteViews)} to keep track of the landscape and portrait
199 * RemoteViews.
200 */
201 private RemoteViews mLandscape = null;
202 private RemoteViews mPortrait = null;
203
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800204 /**
Adam Cohenca6fd842010-09-03 18:10:35 -0700205 * This flag indicates whether this RemoteViews object is being created from a
206 * RemoteViewsService for use as a child of a widget collection. This flag is used
207 * to determine whether or not certain features are available, in particular,
208 * setting on click extras and setting on click pending intents. The former is enabled,
209 * and the latter disabled when this flag is true.
210 */
Dianne Hackborn1927ae82012-06-22 15:21:36 -0700211 private boolean mIsWidgetCollectionChild = false;
212
Adrian Roosfb921842017-10-26 14:49:56 +0200213 /** Class cookies of the Parcel this instance was read from. */
214 private final Map<Class, Object> mClassCookies;
215
Dianne Hackborn1927ae82012-06-22 15:21:36 -0700216 private static final OnClickHandler DEFAULT_ON_CLICK_HANDLER = new OnClickHandler();
Adam Cohenca6fd842010-09-03 18:10:35 -0700217
Sunny Goyal271e3222017-08-29 16:05:47 -0700218 private static final ArrayMap<MethodKey, MethodArgs> sMethods = new ArrayMap<>();
Sunny Goyaldd292f42015-12-02 14:29:27 -0800219
Sunny Goyal271e3222017-08-29 16:05:47 -0700220 /**
221 * This key is used to perform lookups in sMethods without causing allocations.
222 */
223 private static final MethodKey sLookupKey = new MethodKey();
Romain Guye4d4e202013-07-22 13:02:02 -0700224
Adam Cohenca6fd842010-09-03 18:10:35 -0700225 /**
Adrian Roosfe84e1f2015-11-04 15:55:39 -0800226 * @hide
227 */
228 public void setRemoteInputs(int viewId, RemoteInput[] remoteInputs) {
229 mActions.add(new SetRemoteInputsAction(viewId, remoteInputs));
230 }
231
232 /**
Selim Cinekd0426622017-07-11 13:19:59 +0200233 * Reduces all images and ensures that they are all below the given sizes.
234 *
235 * @param maxWidth the maximum width allowed
236 * @param maxHeight the maximum height allowed
237 *
238 * @hide
239 */
240 public void reduceImageSizes(int maxWidth, int maxHeight) {
241 ArrayList<Bitmap> cache = mBitmapCache.mBitmaps;
242 for (int i = 0; i < cache.size(); i++) {
243 Bitmap bitmap = cache.get(i);
244 cache.set(i, Icon.scaleDownIfNecessary(bitmap, maxWidth, maxHeight));
245 }
246 }
247
248 /**
Selim Cinek87c31532017-08-18 18:53:44 -0700249 * Override all text colors in this layout and replace them by the given text color.
250 *
251 * @param textColor The color to use.
252 *
253 * @hide
254 */
255 public void overrideTextColors(int textColor) {
256 addAction(new OverrideTextColorsAction(textColor));
257 }
258
259 /**
Selim Cinekfc8073c2017-08-16 17:50:20 -0700260 * Set that it is disallowed to reapply another remoteview with the same layout as this view.
261 * This should be done if an action is destroying the view tree of the base layout.
262 *
263 * @hide
264 */
265 public void setReapplyDisallowed() {
266 mReapplyDisallowed = true;
267 }
268
269 /**
270 * @return Whether it is disallowed to reapply another remoteview with the same layout as this
271 * view. True if this remoteview has actions that destroyed view tree of the base layout.
272 *
273 * @hide
274 */
275 public boolean isReapplyDisallowed() {
276 return mReapplyDisallowed;
277 }
278
279 /**
Sunny Goyal271e3222017-08-29 16:05:47 -0700280 * Stores information related to reflection method lookup.
Romain Guy484f4d62013-07-22 16:39:16 -0700281 */
Sunny Goyal271e3222017-08-29 16:05:47 -0700282 static class MethodKey {
283 public Class targetClass;
284 public Class paramClass;
285 public String methodName;
Romain Guy484f4d62013-07-22 16:39:16 -0700286
287 @Override
288 public boolean equals(Object o) {
Sunny Goyal271e3222017-08-29 16:05:47 -0700289 if (!(o instanceof MethodKey)) {
Romain Guy484f4d62013-07-22 16:39:16 -0700290 return false;
291 }
Sunny Goyal271e3222017-08-29 16:05:47 -0700292 MethodKey p = (MethodKey) o;
Narayan Kamath607223f2018-02-19 14:09:02 +0000293 return Objects.equals(p.targetClass, targetClass)
294 && Objects.equals(p.paramClass, paramClass)
295 && Objects.equals(p.methodName, methodName);
Romain Guy484f4d62013-07-22 16:39:16 -0700296 }
297
298 @Override
299 public int hashCode() {
Sunny Goyal271e3222017-08-29 16:05:47 -0700300 return Objects.hashCode(targetClass) ^ Objects.hashCode(paramClass)
301 ^ Objects.hashCode(methodName);
302 }
303
304 public void set(Class targetClass, Class paramClass, String methodName) {
305 this.targetClass = targetClass;
306 this.paramClass = paramClass;
307 this.methodName = methodName;
Romain Guy484f4d62013-07-22 16:39:16 -0700308 }
309 }
310
Sunny Goyal271e3222017-08-29 16:05:47 -0700311
Romain Guy484f4d62013-07-22 16:39:16 -0700312 /**
Sunny Goyal271e3222017-08-29 16:05:47 -0700313 * Stores information related to reflection method lookup result.
Romain Guy484f4d62013-07-22 16:39:16 -0700314 */
Sunny Goyal271e3222017-08-29 16:05:47 -0700315 static class MethodArgs {
316 public MethodHandle syncMethod;
317 public MethodHandle asyncMethod;
318 public String asyncMethodName;
319 }
320
Romain Guy484f4d62013-07-22 16:39:16 -0700321 /**
Kirill Grouchnikov9091df02016-06-28 11:24:04 -0400322 * This annotation indicates that a subclass of View is allowed to be used
Romain Guya5475592009-07-01 17:20:08 -0700323 * with the {@link RemoteViews} mechanism.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800324 */
325 @Target({ ElementType.TYPE })
326 @Retention(RetentionPolicy.RUNTIME)
327 public @interface RemoteView {
328 }
329
330 /**
331 * Exception to send when something goes wrong executing an action
332 *
333 */
334 public static class ActionException extends RuntimeException {
335 public ActionException(Exception ex) {
336 super(ex);
337 }
338 public ActionException(String message) {
339 super(message);
340 }
Sunny Goyal271e3222017-08-29 16:05:47 -0700341 /**
342 * @hide
343 */
344 public ActionException(Throwable t) {
345 super(t);
346 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800347 }
Adam Cohena32edd42010-10-26 10:35:01 -0700348
Dianne Hackborn1927ae82012-06-22 15:21:36 -0700349 /** @hide */
350 public static class OnClickHandler {
Gus Prevas1ed322b2015-09-17 17:34:46 -0400351
352 private int mEnterAnimationId;
353
Dianne Hackborn1927ae82012-06-22 15:21:36 -0700354 public boolean onClickHandler(View view, PendingIntent pendingIntent,
Winson Chungae615982010-11-01 12:14:49 -0700355 Intent fillInIntent) {
Wale Ogunwale0568aed2017-09-08 13:29:37 -0700356 return onClickHandler(view, pendingIntent, fillInIntent, WINDOWING_MODE_UNDEFINED);
Jorim Jaggie6e108e2016-03-28 13:38:45 -0700357 }
358
359 public boolean onClickHandler(View view, PendingIntent pendingIntent,
Wale Ogunwale0568aed2017-09-08 13:29:37 -0700360 Intent fillInIntent, int windowingMode) {
Winson Chungae615982010-11-01 12:14:49 -0700361 try {
362 // TODO: Unregister this handler if PendingIntent.FLAG_ONE_SHOT?
Winson Chungdc6f79b2012-04-17 17:27:31 -0700363 Context context = view.getContext();
Gus Prevas1ed322b2015-09-17 17:34:46 -0400364 ActivityOptions opts;
365 if (mEnterAnimationId != 0) {
366 opts = ActivityOptions.makeCustomAnimation(context, mEnterAnimationId, 0);
367 } else {
Ian Lake95b193f2017-04-18 10:37:53 -0700368 opts = ActivityOptions.makeBasic();
Gus Prevas1ed322b2015-09-17 17:34:46 -0400369 }
Jorim Jaggie6e108e2016-03-28 13:38:45 -0700370
Wale Ogunwale0568aed2017-09-08 13:29:37 -0700371 if (windowingMode != WINDOWING_MODE_UNDEFINED) {
372 opts.setLaunchWindowingMode(windowingMode);
Jorim Jaggie6e108e2016-03-28 13:38:45 -0700373 }
Winson Chungae615982010-11-01 12:14:49 -0700374 context.startIntentSender(
375 pendingIntent.getIntentSender(), fillInIntent,
376 Intent.FLAG_ACTIVITY_NEW_TASK,
Winson Chungdc6f79b2012-04-17 17:27:31 -0700377 Intent.FLAG_ACTIVITY_NEW_TASK, 0, opts.toBundle());
Winson Chungae615982010-11-01 12:14:49 -0700378 } catch (IntentSender.SendIntentException e) {
379 android.util.Log.e(LOG_TAG, "Cannot send pending intent: ", e);
380 return false;
381 } catch (Exception e) {
382 android.util.Log.e(LOG_TAG, "Cannot send pending intent due to " +
383 "unknown exception: ", e);
384 return false;
385 }
386 return true;
387 }
Gus Prevas1ed322b2015-09-17 17:34:46 -0400388
389 public void setEnterAnimationId(int enterAnimationId) {
390 mEnterAnimationId = enterAnimationId;
391 }
Dianne Hackborn1927ae82012-06-22 15:21:36 -0700392 }
393
394 /**
395 * Base class for all actions that can be performed on an
396 * inflated view.
397 *
398 * SUBCLASSES MUST BE IMMUTABLE SO CLONE WORKS!!!!!
399 */
400 private abstract static class Action implements Parcelable {
Dianne Hackborna1940212012-06-28 16:07:22 -0700401 public abstract void apply(View root, ViewGroup rootParent,
402 OnClickHandler handler) throws ActionException;
Dianne Hackborn1927ae82012-06-22 15:21:36 -0700403
Adam Cohenfbe44b72012-09-19 20:36:23 -0700404 public static final int MERGE_REPLACE = 0;
405 public static final int MERGE_APPEND = 1;
406 public static final int MERGE_IGNORE = 2;
407
Dianne Hackborn1927ae82012-06-22 15:21:36 -0700408 public int describeContents() {
409 return 0;
410 }
411
Adam Cohen5d200642012-04-24 10:43:31 -0700412 public void setBitmapCache(BitmapCache bitmapCache) {
413 // Do nothing
414 }
Adam Cohenfbe44b72012-09-19 20:36:23 -0700415
416 public int mergeBehavior() {
417 return MERGE_REPLACE;
418 }
419
Sunny Goyal5b153922017-09-21 21:00:36 -0700420 public abstract int getActionTag();
Adam Cohenfbe44b72012-09-19 20:36:23 -0700421
422 public String getUniqueKey() {
Sunny Goyal5b153922017-09-21 21:00:36 -0700423 return (getActionTag() + "_" + viewId);
Adam Cohenfbe44b72012-09-19 20:36:23 -0700424 }
425
Sunny Goyaldd292f42015-12-02 14:29:27 -0800426 /**
427 * This is called on the background thread. It should perform any non-ui computations
428 * and return the final action which will run on the UI thread.
429 * Override this if some of the tasks can be performed async.
430 */
431 public Action initActionAsync(ViewTree root, ViewGroup rootParent, OnClickHandler handler) {
432 return this;
433 }
434
Sunny Goyal5c022632016-02-17 16:30:41 -0800435 public boolean prefersAsyncApply() {
436 return false;
437 }
438
Sunny Goyal5d8bcdf2016-10-27 16:11:29 -0700439 /**
440 * Overridden by subclasses which have (or inherit) an ApplicationInfo instance
441 * as member variable
442 */
443 public boolean hasSameAppInfo(ApplicationInfo parentInfo) {
444 return true;
445 }
446
Adam Cohenfbe44b72012-09-19 20:36:23 -0700447 int viewId;
448 }
449
Adam Cohenbd1e0072012-09-21 16:51:50 -0700450 /**
Sunny Goyaldd292f42015-12-02 14:29:27 -0800451 * Action class used during async inflation of RemoteViews. Subclasses are not parcelable.
452 */
453 private static abstract class RuntimeAction extends Action {
454 @Override
Sunny Goyal5b153922017-09-21 21:00:36 -0700455 public final int getActionTag() {
456 return 0;
Sunny Goyaldd292f42015-12-02 14:29:27 -0800457 }
458
459 @Override
460 public final void writeToParcel(Parcel dest, int flags) {
461 throw new UnsupportedOperationException();
462 }
463 }
464
465 // Constant used during async execution. It is not parcelable.
466 private static final Action ACTION_NOOP = new RuntimeAction() {
467 @Override
468 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) { }
469 };
470
471 /**
Adam Cohenbd1e0072012-09-21 16:51:50 -0700472 * Merges the passed RemoteViews actions with this RemoteViews actions according to
473 * action-specific merge rules.
Svetoslav976e8bd2014-07-16 15:12:03 -0700474 *
Adam Cohenbd1e0072012-09-21 16:51:50 -0700475 * @param newRv
Svetoslav976e8bd2014-07-16 15:12:03 -0700476 *
Adam Cohenbd1e0072012-09-21 16:51:50 -0700477 * @hide
478 */
Adam Cohenfbe44b72012-09-19 20:36:23 -0700479 public void mergeRemoteViews(RemoteViews newRv) {
Adam Cohen3ff2d862012-09-26 14:07:57 -0700480 if (newRv == null) return;
Adam Cohenfbe44b72012-09-19 20:36:23 -0700481 // We first copy the new RemoteViews, as the process of merging modifies the way the actions
482 // reference the bitmap cache. We don't want to modify the object as it may need to
483 // be merged and applied multiple times.
Sunny Goyal56333a82017-08-29 13:46:29 -0700484 RemoteViews copy = new RemoteViews(newRv);
Adam Cohenfbe44b72012-09-19 20:36:23 -0700485
486 HashMap<String, Action> map = new HashMap<String, Action>();
487 if (mActions == null) {
488 mActions = new ArrayList<Action>();
489 }
490
491 int count = mActions.size();
492 for (int i = 0; i < count; i++) {
493 Action a = mActions.get(i);
494 map.put(a.getUniqueKey(), a);
495 }
496
497 ArrayList<Action> newActions = copy.mActions;
498 if (newActions == null) return;
499 count = newActions.size();
500 for (int i = 0; i < count; i++) {
501 Action a = newActions.get(i);
502 String key = newActions.get(i).getUniqueKey();
Adam Cohen3ff2d862012-09-26 14:07:57 -0700503 int mergeBehavior = newActions.get(i).mergeBehavior();
Adam Cohenfbe44b72012-09-19 20:36:23 -0700504 if (map.containsKey(key) && mergeBehavior == Action.MERGE_REPLACE) {
505 mActions.remove(map.get(key));
506 map.remove(key);
507 }
508
509 // If the merge behavior is ignore, we don't bother keeping the extra action
510 if (mergeBehavior == Action.MERGE_REPLACE || mergeBehavior == Action.MERGE_APPEND) {
511 mActions.add(a);
512 }
513 }
514
515 // Because pruning can remove the need for bitmaps, we reconstruct the bitmap cache
516 mBitmapCache = new BitmapCache();
517 setBitmapCache(mBitmapCache);
Romain Guya5475592009-07-01 17:20:08 -0700518 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800519
Adrian Roos83fad0a2017-04-24 16:58:26 -0700520 private static class RemoteViewsContextWrapper extends ContextWrapper {
521 private final Context mContextForResources;
522
523 RemoteViewsContextWrapper(Context context, Context contextForResources) {
524 super(context);
525 mContextForResources = contextForResources;
526 }
527
528 @Override
529 public Resources getResources() {
530 return mContextForResources.getResources();
531 }
532
533 @Override
534 public Resources.Theme getTheme() {
535 return mContextForResources.getTheme();
536 }
537
538 @Override
539 public String getPackageName() {
540 return mContextForResources.getPackageName();
541 }
542 }
543
Adam Cohen1480fdd2010-08-25 17:24:53 -0700544 private class SetEmptyView extends Action {
Adam Cohen1480fdd2010-08-25 17:24:53 -0700545 int emptyViewId;
546
Adam Cohen1480fdd2010-08-25 17:24:53 -0700547 SetEmptyView(int viewId, int emptyViewId) {
548 this.viewId = viewId;
549 this.emptyViewId = emptyViewId;
550 }
551
552 SetEmptyView(Parcel in) {
553 this.viewId = in.readInt();
554 this.emptyViewId = in.readInt();
555 }
556
557 public void writeToParcel(Parcel out, int flags) {
Adam Cohen1480fdd2010-08-25 17:24:53 -0700558 out.writeInt(this.viewId);
559 out.writeInt(this.emptyViewId);
560 }
561
562 @Override
Dianne Hackborna1940212012-06-28 16:07:22 -0700563 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
Adam Cohen1480fdd2010-08-25 17:24:53 -0700564 final View view = root.findViewById(viewId);
565 if (!(view instanceof AdapterView<?>)) return;
566
567 AdapterView<?> adapterView = (AdapterView<?>) view;
568
569 final View emptyView = root.findViewById(emptyViewId);
570 if (emptyView == null) return;
571
572 adapterView.setEmptyView(emptyView);
573 }
Adam Cohenfbe44b72012-09-19 20:36:23 -0700574
Sunny Goyal5b153922017-09-21 21:00:36 -0700575 @Override
576 public int getActionTag() {
577 return SET_EMPTY_VIEW_ACTION_TAG;
Adam Cohenfbe44b72012-09-19 20:36:23 -0700578 }
Adam Cohen1480fdd2010-08-25 17:24:53 -0700579 }
580
Adam Cohen35ae9ca2010-09-20 15:20:41 -0700581 private class SetOnClickFillInIntent extends Action {
582 public SetOnClickFillInIntent(int id, Intent fillInIntent) {
583 this.viewId = id;
584 this.fillInIntent = fillInIntent;
585 }
586
587 public SetOnClickFillInIntent(Parcel parcel) {
588 viewId = parcel.readInt();
Sunny Goyal5b153922017-09-21 21:00:36 -0700589 fillInIntent = parcel.readTypedObject(Intent.CREATOR);
Adam Cohen35ae9ca2010-09-20 15:20:41 -0700590 }
591
592 public void writeToParcel(Parcel dest, int flags) {
Adam Cohen35ae9ca2010-09-20 15:20:41 -0700593 dest.writeInt(viewId);
Sunny Goyal5b153922017-09-21 21:00:36 -0700594 dest.writeTypedObject(fillInIntent, 0 /* no flags */);
Adam Cohen35ae9ca2010-09-20 15:20:41 -0700595 }
596
597 @Override
Dianne Hackborna1940212012-06-28 16:07:22 -0700598 public void apply(View root, ViewGroup rootParent, final OnClickHandler handler) {
Adam Cohen35ae9ca2010-09-20 15:20:41 -0700599 final View target = root.findViewById(viewId);
Joe Onorato2b69ce42010-10-31 11:35:41 -0700600 if (target == null) return;
Adam Cohen35ae9ca2010-09-20 15:20:41 -0700601
602 if (!mIsWidgetCollectionChild) {
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800603 Log.e(LOG_TAG, "The method setOnClickFillInIntent is available " +
Adam Cohen35ae9ca2010-09-20 15:20:41 -0700604 "only from RemoteViewsFactory (ie. on collection items).");
605 return;
606 }
Adam Cohena32edd42010-10-26 10:35:01 -0700607 if (target == root) {
608 target.setTagInternal(com.android.internal.R.id.fillInIntent, fillInIntent);
Romain Guye4d4e202013-07-22 13:02:02 -0700609 } else if (fillInIntent != null) {
Adam Cohen35ae9ca2010-09-20 15:20:41 -0700610 OnClickListener listener = new OnClickListener() {
611 public void onClick(View v) {
612 // Insure that this view is a child of an AdapterView
613 View parent = (View) v.getParent();
Sunny Goyalb880d162016-02-24 14:38:59 -0800614 // Break the for loop on the first encounter of:
615 // 1) an AdapterView,
616 // 2) an AppWidgetHostView that is not a RemoteViewsFrameLayout, or
617 // 3) a null parent.
618 // 2) and 3) are unexpected and catch the case where a child is not
619 // correctly parented in an AdapterView.
Adam Cohen85a08f12012-11-06 11:24:23 -0800620 while (parent != null && !(parent instanceof AdapterView<?>)
Sunny Goyalb880d162016-02-24 14:38:59 -0800621 && !((parent instanceof AppWidgetHostView) &&
622 !(parent instanceof RemoteViewsAdapter.RemoteViewsFrameLayout))) {
Adam Cohen35ae9ca2010-09-20 15:20:41 -0700623 parent = (View) parent.getParent();
624 }
625
Sunny Goyalb880d162016-02-24 14:38:59 -0800626 if (!(parent instanceof AdapterView<?>)) {
Adam Cohen35ae9ca2010-09-20 15:20:41 -0700627 // Somehow they've managed to get this far without having
628 // and AdapterView as a parent.
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800629 Log.e(LOG_TAG, "Collection item doesn't have AdapterView parent");
Adam Cohen35ae9ca2010-09-20 15:20:41 -0700630 return;
631 }
632
633 // Insure that a template pending intent has been set on an ancestor
634 if (!(parent.getTag() instanceof PendingIntent)) {
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800635 Log.e(LOG_TAG, "Attempting setOnClickFillInIntent without" +
Adam Cohena32edd42010-10-26 10:35:01 -0700636 " calling setPendingIntentTemplate on parent.");
Adam Cohen35ae9ca2010-09-20 15:20:41 -0700637 return;
638 }
639
640 PendingIntent pendingIntent = (PendingIntent) parent.getTag();
641
Romain Guye4d4e202013-07-22 13:02:02 -0700642 final Rect rect = getSourceBounds(v);
Adam Cohen35ae9ca2010-09-20 15:20:41 -0700643
644 fillInIntent.setSourceBounds(rect);
Dianne Hackborna1940212012-06-28 16:07:22 -0700645 handler.onClickHandler(v, pendingIntent, fillInIntent);
Adam Cohen35ae9ca2010-09-20 15:20:41 -0700646 }
647
648 };
649 target.setOnClickListener(listener);
650 }
651 }
652
Sunny Goyal5b153922017-09-21 21:00:36 -0700653 @Override
654 public int getActionTag() {
655 return SET_ON_CLICK_FILL_IN_INTENT_TAG;
Adam Cohenfbe44b72012-09-19 20:36:23 -0700656 }
657
Adam Cohen35ae9ca2010-09-20 15:20:41 -0700658 Intent fillInIntent;
Adam Cohen35ae9ca2010-09-20 15:20:41 -0700659 }
660
Adam Cohenca6fd842010-09-03 18:10:35 -0700661 private class SetPendingIntentTemplate extends Action {
662 public SetPendingIntentTemplate(int id, PendingIntent pendingIntentTemplate) {
663 this.viewId = id;
664 this.pendingIntentTemplate = pendingIntentTemplate;
665 }
666
667 public SetPendingIntentTemplate(Parcel parcel) {
668 viewId = parcel.readInt();
669 pendingIntentTemplate = PendingIntent.readPendingIntentOrNullFromParcel(parcel);
670 }
671
672 public void writeToParcel(Parcel dest, int flags) {
Adam Cohenca6fd842010-09-03 18:10:35 -0700673 dest.writeInt(viewId);
Sunny Goyal5b153922017-09-21 21:00:36 -0700674 PendingIntent.writePendingIntentOrNullToParcel(pendingIntentTemplate, dest);
Adam Cohenca6fd842010-09-03 18:10:35 -0700675 }
676
677 @Override
Dianne Hackborna1940212012-06-28 16:07:22 -0700678 public void apply(View root, ViewGroup rootParent, final OnClickHandler handler) {
Adam Cohenca6fd842010-09-03 18:10:35 -0700679 final View target = root.findViewById(viewId);
Joe Onorato2b69ce42010-10-31 11:35:41 -0700680 if (target == null) return;
Adam Cohenca6fd842010-09-03 18:10:35 -0700681
682 // If the view isn't an AdapterView, setting a PendingIntent template doesn't make sense
683 if (target instanceof AdapterView<?>) {
Adam Cohena32edd42010-10-26 10:35:01 -0700684 AdapterView<?> av = (AdapterView<?>) target;
Adam Cohenca6fd842010-09-03 18:10:35 -0700685 // The PendingIntent template is stored in the view's tag.
Adam Cohena32edd42010-10-26 10:35:01 -0700686 OnItemClickListener listener = new OnItemClickListener() {
687 public void onItemClick(AdapterView<?> parent, View view,
688 int position, long id) {
689 // The view should be a frame layout
690 if (view instanceof ViewGroup) {
691 ViewGroup vg = (ViewGroup) view;
692
693 // AdapterViews contain their children in a frame
694 // so we need to go one layer deeper here.
695 if (parent instanceof AdapterViewAnimator) {
696 vg = (ViewGroup) vg.getChildAt(0);
697 }
698 if (vg == null) return;
699
700 Intent fillInIntent = null;
701 int childCount = vg.getChildCount();
702 for (int i = 0; i < childCount; i++) {
703 Object tag = vg.getChildAt(i).getTag(com.android.internal.R.id.fillInIntent);
704 if (tag instanceof Intent) {
705 fillInIntent = (Intent) tag;
706 break;
707 }
708 }
709 if (fillInIntent == null) return;
710
Romain Guye4d4e202013-07-22 13:02:02 -0700711 final Rect rect = getSourceBounds(view);
Adam Cohena32edd42010-10-26 10:35:01 -0700712
713 final Intent intent = new Intent();
714 intent.setSourceBounds(rect);
Dianne Hackborna1940212012-06-28 16:07:22 -0700715 handler.onClickHandler(view, pendingIntentTemplate, fillInIntent);
Adam Cohena32edd42010-10-26 10:35:01 -0700716 }
717 }
718 };
719 av.setOnItemClickListener(listener);
720 av.setTag(pendingIntentTemplate);
Adam Cohenca6fd842010-09-03 18:10:35 -0700721 } else {
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800722 Log.e(LOG_TAG, "Cannot setPendingIntentTemplate on a view which is not" +
Adam Cohen35ae9ca2010-09-20 15:20:41 -0700723 "an AdapterView (id: " + viewId + ")");
Adam Cohenca6fd842010-09-03 18:10:35 -0700724 return;
725 }
726 }
727
Sunny Goyal5b153922017-09-21 21:00:36 -0700728 @Override
729 public int getActionTag() {
730 return SET_PENDING_INTENT_TEMPLATE_TAG;
Adam Cohenfbe44b72012-09-19 20:36:23 -0700731 }
732
Adam Cohenca6fd842010-09-03 18:10:35 -0700733 PendingIntent pendingIntentTemplate;
Adam Cohenca6fd842010-09-03 18:10:35 -0700734 }
735
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800736 private class SetRemoteViewsAdapterList extends Action {
Adam Cohenb00d9f02013-01-10 14:12:52 -0800737 public SetRemoteViewsAdapterList(int id, ArrayList<RemoteViews> list, int viewTypeCount) {
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800738 this.viewId = id;
739 this.list = list;
Adam Cohenb00d9f02013-01-10 14:12:52 -0800740 this.viewTypeCount = viewTypeCount;
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800741 }
742
743 public SetRemoteViewsAdapterList(Parcel parcel) {
744 viewId = parcel.readInt();
Adam Cohenb00d9f02013-01-10 14:12:52 -0800745 viewTypeCount = parcel.readInt();
Sunny Goyal5b153922017-09-21 21:00:36 -0700746 list = parcel.createTypedArrayList(RemoteViews.CREATOR);
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800747 }
748
749 public void writeToParcel(Parcel dest, int flags) {
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800750 dest.writeInt(viewId);
Adam Cohenb00d9f02013-01-10 14:12:52 -0800751 dest.writeInt(viewTypeCount);
Sunny Goyal5b153922017-09-21 21:00:36 -0700752 dest.writeTypedList(list, flags);
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800753 }
754
755 @Override
756 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
757 final View target = root.findViewById(viewId);
758 if (target == null) return;
759
760 // Ensure that we are applying to an AppWidget root
761 if (!(rootParent instanceof AppWidgetHostView)) {
762 Log.e(LOG_TAG, "SetRemoteViewsAdapterIntent action can only be used for " +
763 "AppWidgets (root id: " + viewId + ")");
764 return;
765 }
766 // Ensure that we are calling setRemoteAdapter on an AdapterView that supports it
767 if (!(target instanceof AbsListView) && !(target instanceof AdapterViewAnimator)) {
768 Log.e(LOG_TAG, "Cannot setRemoteViewsAdapter on a view which is not " +
769 "an AbsListView or AdapterViewAnimator (id: " + viewId + ")");
770 return;
771 }
772
773 if (target instanceof AbsListView) {
774 AbsListView v = (AbsListView) target;
775 Adapter a = v.getAdapter();
Adam Cohenb00d9f02013-01-10 14:12:52 -0800776 if (a instanceof RemoteViewsListAdapter && viewTypeCount <= a.getViewTypeCount()) {
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800777 ((RemoteViewsListAdapter) a).setViewsList(list);
778 } else {
Adam Cohenb00d9f02013-01-10 14:12:52 -0800779 v.setAdapter(new RemoteViewsListAdapter(v.getContext(), list, viewTypeCount));
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800780 }
781 } else if (target instanceof AdapterViewAnimator) {
782 AdapterViewAnimator v = (AdapterViewAnimator) target;
783 Adapter a = v.getAdapter();
Adam Cohenb00d9f02013-01-10 14:12:52 -0800784 if (a instanceof RemoteViewsListAdapter && viewTypeCount <= a.getViewTypeCount()) {
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800785 ((RemoteViewsListAdapter) a).setViewsList(list);
786 } else {
Adam Cohenb00d9f02013-01-10 14:12:52 -0800787 v.setAdapter(new RemoteViewsListAdapter(v.getContext(), list, viewTypeCount));
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800788 }
789 }
790 }
791
Sunny Goyal5b153922017-09-21 21:00:36 -0700792 @Override
793 public int getActionTag() {
794 return SET_REMOTE_VIEW_ADAPTER_LIST_TAG;
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800795 }
796
Adam Cohenb00d9f02013-01-10 14:12:52 -0800797 int viewTypeCount;
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800798 ArrayList<RemoteViews> list;
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800799 }
800
Winson Chung037300b2011-03-29 15:40:16 -0700801 private class SetRemoteViewsAdapterIntent extends Action {
802 public SetRemoteViewsAdapterIntent(int id, Intent intent) {
803 this.viewId = id;
804 this.intent = intent;
805 }
806
807 public SetRemoteViewsAdapterIntent(Parcel parcel) {
808 viewId = parcel.readInt();
Sunny Goyal5b153922017-09-21 21:00:36 -0700809 intent = parcel.readTypedObject(Intent.CREATOR);
Winson Chung037300b2011-03-29 15:40:16 -0700810 }
811
812 public void writeToParcel(Parcel dest, int flags) {
Winson Chung037300b2011-03-29 15:40:16 -0700813 dest.writeInt(viewId);
Sunny Goyal5b153922017-09-21 21:00:36 -0700814 dest.writeTypedObject(intent, flags);
Winson Chung037300b2011-03-29 15:40:16 -0700815 }
816
817 @Override
Dianne Hackborna1940212012-06-28 16:07:22 -0700818 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
Winson Chung037300b2011-03-29 15:40:16 -0700819 final View target = root.findViewById(viewId);
820 if (target == null) return;
821
822 // Ensure that we are applying to an AppWidget root
823 if (!(rootParent instanceof AppWidgetHostView)) {
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800824 Log.e(LOG_TAG, "SetRemoteViewsAdapterIntent action can only be used for " +
Winson Chung037300b2011-03-29 15:40:16 -0700825 "AppWidgets (root id: " + viewId + ")");
826 return;
827 }
828 // Ensure that we are calling setRemoteAdapter on an AdapterView that supports it
829 if (!(target instanceof AbsListView) && !(target instanceof AdapterViewAnimator)) {
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800830 Log.e(LOG_TAG, "Cannot setRemoteViewsAdapter on a view which is not " +
Winson Chung037300b2011-03-29 15:40:16 -0700831 "an AbsListView or AdapterViewAnimator (id: " + viewId + ")");
832 return;
833 }
834
835 // Embed the AppWidget Id for use in RemoteViewsAdapter when connecting to the intent
836 // RemoteViewsService
837 AppWidgetHostView host = (AppWidgetHostView) rootParent;
838 intent.putExtra(EXTRA_REMOTEADAPTER_APPWIDGET_ID, host.getAppWidgetId());
839 if (target instanceof AbsListView) {
840 AbsListView v = (AbsListView) target;
Sunny Goyal5c022632016-02-17 16:30:41 -0800841 v.setRemoteViewsAdapter(intent, isAsync);
Adam Cohena6a4cbc2012-09-26 17:36:40 -0700842 v.setRemoteViewsOnClickHandler(handler);
Winson Chung037300b2011-03-29 15:40:16 -0700843 } else if (target instanceof AdapterViewAnimator) {
844 AdapterViewAnimator v = (AdapterViewAnimator) target;
Sunny Goyal5c022632016-02-17 16:30:41 -0800845 v.setRemoteViewsAdapter(intent, isAsync);
Adam Cohena6a4cbc2012-09-26 17:36:40 -0700846 v.setRemoteViewsOnClickHandler(handler);
Winson Chung037300b2011-03-29 15:40:16 -0700847 }
848 }
849
Sunny Goyal5c022632016-02-17 16:30:41 -0800850 @Override
851 public Action initActionAsync(ViewTree root, ViewGroup rootParent,
852 OnClickHandler handler) {
853 SetRemoteViewsAdapterIntent copy = new SetRemoteViewsAdapterIntent(viewId, intent);
854 copy.isAsync = true;
855 return copy;
856 }
857
Sunny Goyal5b153922017-09-21 21:00:36 -0700858 @Override
859 public int getActionTag() {
860 return SET_REMOTE_VIEW_ADAPTER_INTENT_TAG;
Adam Cohenfbe44b72012-09-19 20:36:23 -0700861 }
862
Winson Chung037300b2011-03-29 15:40:16 -0700863 Intent intent;
Sunny Goyal5c022632016-02-17 16:30:41 -0800864 boolean isAsync = false;
Winson Chung037300b2011-03-29 15:40:16 -0700865 }
866
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800867 /**
868 * Equivalent to calling
869 * {@link android.view.View#setOnClickListener(android.view.View.OnClickListener)}
870 * to launch the provided {@link PendingIntent}.
871 */
872 private class SetOnClickPendingIntent extends Action {
873 public SetOnClickPendingIntent(int id, PendingIntent pendingIntent) {
874 this.viewId = id;
875 this.pendingIntent = pendingIntent;
876 }
Adam Cohenca6fd842010-09-03 18:10:35 -0700877
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800878 public SetOnClickPendingIntent(Parcel parcel) {
879 viewId = parcel.readInt();
Sunny Goyal5b153922017-09-21 21:00:36 -0700880 pendingIntent = PendingIntent.readPendingIntentOrNullFromParcel(parcel);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800881 }
Adam Cohenca6fd842010-09-03 18:10:35 -0700882
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800883 public void writeToParcel(Parcel dest, int flags) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800884 dest.writeInt(viewId);
Sunny Goyal5b153922017-09-21 21:00:36 -0700885 PendingIntent.writePendingIntentOrNullToParcel(pendingIntent, dest);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800886 }
Adam Cohenca6fd842010-09-03 18:10:35 -0700887
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800888 @Override
Dianne Hackborna1940212012-06-28 16:07:22 -0700889 public void apply(View root, ViewGroup rootParent, final OnClickHandler handler) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800890 final View target = root.findViewById(viewId);
Joe Onorato2b69ce42010-10-31 11:35:41 -0700891 if (target == null) return;
Adam Cohenca6fd842010-09-03 18:10:35 -0700892
893 // If the view is an AdapterView, setting a PendingIntent on click doesn't make much
894 // sense, do they mean to set a PendingIntent template for the AdapterView's children?
895 if (mIsWidgetCollectionChild) {
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800896 Log.w(LOG_TAG, "Cannot setOnClickPendingIntent for collection item " +
Adam Cohenc6151f22012-02-02 21:02:31 -0800897 "(id: " + viewId + ")");
Adam Cohenffc46a52012-04-30 19:54:39 -0700898 ApplicationInfo appInfo = root.getContext().getApplicationInfo();
899
900 // We let this slide for HC and ICS so as to not break compatibility. It should have
901 // been disabled from the outset, but was left open by accident.
902 if (appInfo != null &&
903 appInfo.targetSdkVersion >= Build.VERSION_CODES.JELLY_BEAN) {
904 return;
905 }
Adam Cohenca6fd842010-09-03 18:10:35 -0700906 }
907
Romain Guye4d4e202013-07-22 13:02:02 -0700908 // If the pendingIntent is null, we clear the onClickListener
909 OnClickListener listener = null;
910 if (pendingIntent != null) {
911 listener = new OnClickListener() {
912 public void onClick(View v) {
913 // Find target view location in screen coordinates and
914 // fill into PendingIntent before sending.
915 final Rect rect = getSourceBounds(v);
Jim Millere667a7a2012-08-09 19:22:32 -0700916
Romain Guye4d4e202013-07-22 13:02:02 -0700917 final Intent intent = new Intent();
918 intent.setSourceBounds(rect);
919 handler.onClickHandler(v, pendingIntent, intent);
920 }
921 };
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800922 }
Romain Guye4d4e202013-07-22 13:02:02 -0700923 target.setOnClickListener(listener);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800924 }
Adam Cohenc6151f22012-02-02 21:02:31 -0800925
Sunny Goyal5b153922017-09-21 21:00:36 -0700926 @Override
927 public int getActionTag() {
928 return SET_ON_CLICK_PENDING_INTENT_TAG;
Adam Cohenfbe44b72012-09-19 20:36:23 -0700929 }
930
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800931 PendingIntent pendingIntent;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800932 }
933
Romain Guye4d4e202013-07-22 13:02:02 -0700934 private static Rect getSourceBounds(View v) {
935 final float appScale = v.getContext().getResources()
936 .getCompatibilityInfo().applicationScale;
937 final int[] pos = new int[2];
938 v.getLocationOnScreen(pos);
939
940 final Rect rect = new Rect();
941 rect.left = (int) (pos[0] * appScale + 0.5f);
942 rect.top = (int) (pos[1] * appScale + 0.5f);
943 rect.right = (int) ((pos[0] + v.getWidth()) * appScale + 0.5f);
944 rect.bottom = (int) ((pos[1] + v.getHeight()) * appScale + 0.5f);
945 return rect;
946 }
947
Sunny Goyal271e3222017-08-29 16:05:47 -0700948 private MethodHandle getMethod(View view, String methodName, Class<?> paramType,
949 boolean async) {
950 MethodArgs result;
Romain Guye4d4e202013-07-22 13:02:02 -0700951 Class<? extends View> klass = view.getClass();
952
Sunny Goyal271e3222017-08-29 16:05:47 -0700953 synchronized (sMethods) {
954 // The key is defined by the view class, param class and method name.
955 sLookupKey.set(klass, paramType, methodName);
956 result = sMethods.get(sLookupKey);
Romain Guye4d4e202013-07-22 13:02:02 -0700957
Sunny Goyal271e3222017-08-29 16:05:47 -0700958 if (result == null) {
959 Method method;
Romain Guye4d4e202013-07-22 13:02:02 -0700960 try {
Romain Guy9870e5c2013-07-23 13:09:51 -0700961 if (paramType == null) {
962 method = klass.getMethod(methodName);
963 } else {
964 method = klass.getMethod(methodName, paramType);
965 }
Sunny Goyal271e3222017-08-29 16:05:47 -0700966 if (!method.isAnnotationPresent(RemotableViewMethod.class)) {
967 throw new ActionException("view: " + klass.getName()
968 + " can't use method with RemoteViews: "
969 + methodName + getParameters(paramType));
970 }
971
972 result = new MethodArgs();
973 result.syncMethod = MethodHandles.publicLookup().unreflect(method);
974 result.asyncMethodName =
975 method.getAnnotation(RemotableViewMethod.class).asyncImpl();
976 } catch (NoSuchMethodException | IllegalAccessException ex) {
Romain Guye4d4e202013-07-22 13:02:02 -0700977 throw new ActionException("view: " + klass.getName() + " doesn't have method: "
978 + methodName + getParameters(paramType));
979 }
980
Sunny Goyal271e3222017-08-29 16:05:47 -0700981 MethodKey key = new MethodKey();
982 key.set(klass, paramType, methodName);
983 sMethods.put(key, result);
Sunny Goyaldd292f42015-12-02 14:29:27 -0800984 }
985
Sunny Goyal271e3222017-08-29 16:05:47 -0700986 if (!async) {
987 return result.syncMethod;
988 }
989 // Check this so see if async method is implemented or not.
990 if (result.asyncMethodName.isEmpty()) {
991 return null;
992 }
993 // Async method is lazily loaded. If it is not yet loaded, load now.
994 if (result.asyncMethod == null) {
995 MethodType asyncType = result.syncMethod.type()
996 .dropParameterTypes(0, 1).changeReturnType(Runnable.class);
Sunny Goyaldd292f42015-12-02 14:29:27 -0800997 try {
Sunny Goyal271e3222017-08-29 16:05:47 -0700998 result.asyncMethod = MethodHandles.publicLookup().findVirtual(
999 klass, result.asyncMethodName, asyncType);
1000 } catch (NoSuchMethodException | IllegalAccessException ex) {
1001 throw new ActionException("Async implementation declared as "
1002 + result.asyncMethodName + " but not defined for " + methodName
1003 + ": public Runnable " + result.asyncMethodName + " ("
1004 + TextUtils.join(",", asyncType.parameterArray()) + ")");
Sunny Goyaldd292f42015-12-02 14:29:27 -08001005 }
1006 }
Sunny Goyal271e3222017-08-29 16:05:47 -07001007 return result.asyncMethod;
Sunny Goyaldd292f42015-12-02 14:29:27 -08001008 }
1009 }
1010
Romain Guye4d4e202013-07-22 13:02:02 -07001011 private static String getParameters(Class<?> paramType) {
1012 if (paramType == null) return "()";
1013 return "(" + paramType + ")";
1014 }
1015
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001016 /**
Sunny Goyal5b153922017-09-21 21:00:36 -07001017 * Equivalent to calling
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001018 * {@link Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)},
Sunny Goyal5b153922017-09-21 21:00:36 -07001019 * on the {@link Drawable} of a given view.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001020 * <p>
Sunny Goyal5b153922017-09-21 21:00:36 -07001021 * The operation will be performed on the {@link Drawable} returned by the
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001022 * target {@link View#getBackground()} by default. If targetBackground is false,
1023 * we assume the target is an {@link ImageView} and try applying the operations
1024 * to {@link ImageView#getDrawable()}.
1025 * <p>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001026 */
Sunny Goyal5b153922017-09-21 21:00:36 -07001027 private class SetDrawableTint extends Action {
1028 SetDrawableTint(int id, boolean targetBackground,
1029 int colorFilter, @NonNull PorterDuff.Mode mode) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001030 this.viewId = id;
1031 this.targetBackground = targetBackground;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001032 this.colorFilter = colorFilter;
1033 this.filterMode = mode;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001034 }
Jim Millere667a7a2012-08-09 19:22:32 -07001035
Sunny Goyal5b153922017-09-21 21:00:36 -07001036 SetDrawableTint(Parcel parcel) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001037 viewId = parcel.readInt();
1038 targetBackground = parcel.readInt() != 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001039 colorFilter = parcel.readInt();
Sunny Goyal5b153922017-09-21 21:00:36 -07001040 filterMode = PorterDuff.intToMode(parcel.readInt());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001041 }
Jim Millere667a7a2012-08-09 19:22:32 -07001042
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001043 public void writeToParcel(Parcel dest, int flags) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001044 dest.writeInt(viewId);
1045 dest.writeInt(targetBackground ? 1 : 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001046 dest.writeInt(colorFilter);
Sunny Goyal5b153922017-09-21 21:00:36 -07001047 dest.writeInt(PorterDuff.modeToInt(filterMode));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001048 }
Jim Millere667a7a2012-08-09 19:22:32 -07001049
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001050 @Override
Dianne Hackborna1940212012-06-28 16:07:22 -07001051 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001052 final View target = root.findViewById(viewId);
Joe Onorato2b69ce42010-10-31 11:35:41 -07001053 if (target == null) return;
Jim Millere667a7a2012-08-09 19:22:32 -07001054
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001055 // Pick the correct drawable to modify for this view
1056 Drawable targetDrawable = null;
1057 if (targetBackground) {
1058 targetDrawable = target.getBackground();
1059 } else if (target instanceof ImageView) {
1060 ImageView imageView = (ImageView) target;
1061 targetDrawable = imageView.getDrawable();
1062 }
Jim Millere667a7a2012-08-09 19:22:32 -07001063
Romain Guya5475592009-07-01 17:20:08 -07001064 if (targetDrawable != null) {
Sunny Goyal5b153922017-09-21 21:00:36 -07001065 targetDrawable.mutate().setColorFilter(colorFilter, filterMode);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001066 }
1067 }
Winson Chung3ec9a452010-09-23 16:40:28 -07001068
Sunny Goyal5b153922017-09-21 21:00:36 -07001069 @Override
1070 public int getActionTag() {
1071 return SET_DRAWABLE_TINT_TAG;
Adam Cohenfbe44b72012-09-19 20:36:23 -07001072 }
1073
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001074 boolean targetBackground;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001075 int colorFilter;
1076 PorterDuff.Mode filterMode;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001077 }
Jim Millere667a7a2012-08-09 19:22:32 -07001078
Sunny Goyal5b153922017-09-21 21:00:36 -07001079 private final class ViewContentNavigation extends Action {
1080 final boolean mNext;
Adam Cohen2dd21972010-08-15 18:20:04 -07001081
Sunny Goyal5b153922017-09-21 21:00:36 -07001082 ViewContentNavigation(int viewId, boolean next) {
Adam Cohen2dd21972010-08-15 18:20:04 -07001083 this.viewId = viewId;
Sunny Goyal5b153922017-09-21 21:00:36 -07001084 this.mNext = next;
Adam Cohen2dd21972010-08-15 18:20:04 -07001085 }
1086
Sunny Goyal5b153922017-09-21 21:00:36 -07001087 ViewContentNavigation(Parcel in) {
Adam Cohen2dd21972010-08-15 18:20:04 -07001088 this.viewId = in.readInt();
Sunny Goyal5b153922017-09-21 21:00:36 -07001089 this.mNext = in.readBoolean();
Adam Cohen2dd21972010-08-15 18:20:04 -07001090 }
1091
1092 public void writeToParcel(Parcel out, int flags) {
Adam Cohen2dd21972010-08-15 18:20:04 -07001093 out.writeInt(this.viewId);
Sunny Goyal5b153922017-09-21 21:00:36 -07001094 out.writeBoolean(this.mNext);
Adam Cohen2dd21972010-08-15 18:20:04 -07001095 }
1096
1097 @Override
Dianne Hackborna1940212012-06-28 16:07:22 -07001098 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
Adam Cohen2dd21972010-08-15 18:20:04 -07001099 final View view = root.findViewById(viewId);
Joe Onorato2b69ce42010-10-31 11:35:41 -07001100 if (view == null) return;
Adam Cohen2dd21972010-08-15 18:20:04 -07001101
Adam Cohen2dd21972010-08-15 18:20:04 -07001102 try {
Sunny Goyal5b153922017-09-21 21:00:36 -07001103 getMethod(view,
1104 mNext ? "showNext" : "showPrevious", null, false /* async */).invoke(view);
Sunny Goyal271e3222017-08-29 16:05:47 -07001105 } catch (Throwable ex) {
Adam Cohen2dd21972010-08-15 18:20:04 -07001106 throw new ActionException(ex);
1107 }
1108 }
Adam Cohenfbe44b72012-09-19 20:36:23 -07001109
1110 public int mergeBehavior() {
Sunny Goyal5b153922017-09-21 21:00:36 -07001111 return MERGE_IGNORE;
Adam Cohenfbe44b72012-09-19 20:36:23 -07001112 }
1113
Sunny Goyal5b153922017-09-21 21:00:36 -07001114 @Override
1115 public int getActionTag() {
1116 return VIEW_CONTENT_NAVIGATION_TAG;
Adam Cohenfbe44b72012-09-19 20:36:23 -07001117 }
Adam Cohen2dd21972010-08-15 18:20:04 -07001118 }
1119
Adam Cohen5d200642012-04-24 10:43:31 -07001120 private static class BitmapCache {
Sunny Goyal56333a82017-08-29 13:46:29 -07001121
Adam Cohen5d200642012-04-24 10:43:31 -07001122 ArrayList<Bitmap> mBitmaps;
Sunny Goyal56333a82017-08-29 13:46:29 -07001123 int mBitmapMemory = -1;
Adam Cohen5d200642012-04-24 10:43:31 -07001124
1125 public BitmapCache() {
Sunny Goyal56333a82017-08-29 13:46:29 -07001126 mBitmaps = new ArrayList<>();
Adam Cohen5d200642012-04-24 10:43:31 -07001127 }
1128
1129 public BitmapCache(Parcel source) {
Sunny Goyal5b153922017-09-21 21:00:36 -07001130 mBitmaps = source.createTypedArrayList(Bitmap.CREATOR);
Adam Cohen5d200642012-04-24 10:43:31 -07001131 }
1132
1133 public int getBitmapId(Bitmap b) {
1134 if (b == null) {
1135 return -1;
1136 } else {
1137 if (mBitmaps.contains(b)) {
1138 return mBitmaps.indexOf(b);
1139 } else {
1140 mBitmaps.add(b);
Sunny Goyal56333a82017-08-29 13:46:29 -07001141 mBitmapMemory = -1;
Adam Cohen5d200642012-04-24 10:43:31 -07001142 return (mBitmaps.size() - 1);
1143 }
1144 }
1145 }
1146
1147 public Bitmap getBitmapForId(int id) {
1148 if (id == -1 || id >= mBitmaps.size()) {
1149 return null;
1150 } else {
1151 return mBitmaps.get(id);
1152 }
1153 }
1154
1155 public void writeBitmapsToParcel(Parcel dest, int flags) {
Sunny Goyal5b153922017-09-21 21:00:36 -07001156 dest.writeTypedList(mBitmaps, flags);
Adam Cohen5d200642012-04-24 10:43:31 -07001157 }
1158
Sunny Goyal56333a82017-08-29 13:46:29 -07001159 public int getBitmapMemory() {
1160 if (mBitmapMemory < 0) {
1161 mBitmapMemory = 0;
1162 int count = mBitmaps.size();
1163 for (int i = 0; i < count; i++) {
1164 mBitmapMemory += mBitmaps.get(i).getAllocationByteCount();
Adam Cohen5d200642012-04-24 10:43:31 -07001165 }
1166 }
Sunny Goyal56333a82017-08-29 13:46:29 -07001167 return mBitmapMemory;
Adrian Roos7da889d2016-03-16 18:38:58 -07001168 }
Adam Cohen5d200642012-04-24 10:43:31 -07001169 }
1170
1171 private class BitmapReflectionAction extends Action {
1172 int bitmapId;
Adam Cohen5d200642012-04-24 10:43:31 -07001173 Bitmap bitmap;
1174 String methodName;
1175
1176 BitmapReflectionAction(int viewId, String methodName, Bitmap bitmap) {
1177 this.bitmap = bitmap;
1178 this.viewId = viewId;
1179 this.methodName = methodName;
1180 bitmapId = mBitmapCache.getBitmapId(bitmap);
1181 }
1182
1183 BitmapReflectionAction(Parcel in) {
1184 viewId = in.readInt();
1185 methodName = in.readString();
1186 bitmapId = in.readInt();
1187 bitmap = mBitmapCache.getBitmapForId(bitmapId);
1188 }
1189
1190 @Override
1191 public void writeToParcel(Parcel dest, int flags) {
Adam Cohen5d200642012-04-24 10:43:31 -07001192 dest.writeInt(viewId);
1193 dest.writeString(methodName);
1194 dest.writeInt(bitmapId);
1195 }
1196
1197 @Override
Dianne Hackborna1940212012-06-28 16:07:22 -07001198 public void apply(View root, ViewGroup rootParent,
1199 OnClickHandler handler) throws ActionException {
Adam Cohen5d200642012-04-24 10:43:31 -07001200 ReflectionAction ra = new ReflectionAction(viewId, methodName, ReflectionAction.BITMAP,
1201 bitmap);
Dianne Hackborna1940212012-06-28 16:07:22 -07001202 ra.apply(root, rootParent, handler);
Adam Cohen5d200642012-04-24 10:43:31 -07001203 }
1204
1205 @Override
1206 public void setBitmapCache(BitmapCache bitmapCache) {
1207 bitmapId = bitmapCache.getBitmapId(bitmap);
1208 }
1209
Sunny Goyal5b153922017-09-21 21:00:36 -07001210 @Override
1211 public int getActionTag() {
1212 return BITMAP_REFLECTION_ACTION_TAG;
Adam Cohenfbe44b72012-09-19 20:36:23 -07001213 }
Adam Cohen5d200642012-04-24 10:43:31 -07001214 }
1215
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001216 /**
1217 * Base class for the reflection actions.
1218 */
Romain Guy484f4d62013-07-22 16:39:16 -07001219 private final class ReflectionAction extends Action {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001220 static final int BOOLEAN = 1;
1221 static final int BYTE = 2;
1222 static final int SHORT = 3;
1223 static final int INT = 4;
1224 static final int LONG = 5;
1225 static final int FLOAT = 6;
1226 static final int DOUBLE = 7;
1227 static final int CHAR = 8;
1228 static final int STRING = 9;
1229 static final int CHAR_SEQUENCE = 10;
1230 static final int URI = 11;
Adam Cohenfbe44b72012-09-19 20:36:23 -07001231 // BITMAP actions are never stored in the list of actions. They are only used locally
1232 // to implement BitmapReflectionAction, which eliminates duplicates using BitmapCache.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001233 static final int BITMAP = 12;
Bjorn Bringertd755b062010-01-06 17:15:37 +00001234 static final int BUNDLE = 13;
Winson Chung499cb9f2010-07-16 11:18:17 -07001235 static final int INTENT = 14;
Jorim Jaggief72a192014-08-26 21:57:46 +02001236 static final int COLOR_STATE_LIST = 15;
Dan Sandlera22a3802015-05-13 00:12:47 -04001237 static final int ICON = 16;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001238
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001239 String methodName;
1240 int type;
1241 Object value;
1242
1243 ReflectionAction(int viewId, String methodName, int type, Object value) {
1244 this.viewId = viewId;
1245 this.methodName = methodName;
1246 this.type = type;
1247 this.value = value;
1248 }
1249
1250 ReflectionAction(Parcel in) {
1251 this.viewId = in.readInt();
1252 this.methodName = in.readString();
1253 this.type = in.readInt();
Romain Guya5475592009-07-01 17:20:08 -07001254 //noinspection ConstantIfStatement
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001255 if (false) {
Adam Cohen50f3d1b2012-12-11 18:36:07 -08001256 Log.d(LOG_TAG, "read viewId=0x" + Integer.toHexString(this.viewId)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001257 + " methodName=" + this.methodName + " type=" + this.type);
1258 }
Adam Cohenc6151f22012-02-02 21:02:31 -08001259
1260 // For some values that may have been null, we first check a flag to see if they were
1261 // written to the parcel.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001262 switch (this.type) {
1263 case BOOLEAN:
Sunny Goyal5b153922017-09-21 21:00:36 -07001264 this.value = in.readBoolean();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001265 break;
1266 case BYTE:
1267 this.value = in.readByte();
1268 break;
1269 case SHORT:
1270 this.value = (short)in.readInt();
1271 break;
1272 case INT:
1273 this.value = in.readInt();
1274 break;
1275 case LONG:
1276 this.value = in.readLong();
1277 break;
1278 case FLOAT:
1279 this.value = in.readFloat();
1280 break;
1281 case DOUBLE:
1282 this.value = in.readDouble();
1283 break;
1284 case CHAR:
1285 this.value = (char)in.readInt();
1286 break;
1287 case STRING:
1288 this.value = in.readString();
1289 break;
1290 case CHAR_SEQUENCE:
1291 this.value = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
1292 break;
1293 case URI:
Sunny Goyal5b153922017-09-21 21:00:36 -07001294 this.value = in.readTypedObject(Uri.CREATOR);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001295 break;
1296 case BITMAP:
Sunny Goyal5b153922017-09-21 21:00:36 -07001297 this.value = in.readTypedObject(Bitmap.CREATOR);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001298 break;
Bjorn Bringertd755b062010-01-06 17:15:37 +00001299 case BUNDLE:
1300 this.value = in.readBundle();
1301 break;
Winson Chung499cb9f2010-07-16 11:18:17 -07001302 case INTENT:
Sunny Goyal5b153922017-09-21 21:00:36 -07001303 this.value = in.readTypedObject(Intent.CREATOR);
Winson Chung499cb9f2010-07-16 11:18:17 -07001304 break;
Jorim Jaggief72a192014-08-26 21:57:46 +02001305 case COLOR_STATE_LIST:
Sunny Goyal5b153922017-09-21 21:00:36 -07001306 this.value = in.readTypedObject(ColorStateList.CREATOR);
Jorim Jaggief72a192014-08-26 21:57:46 +02001307 break;
Dan Sandlera22a3802015-05-13 00:12:47 -04001308 case ICON:
Sunny Goyal5b153922017-09-21 21:00:36 -07001309 this.value = in.readTypedObject(Icon.CREATOR);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001310 default:
1311 break;
1312 }
1313 }
1314
1315 public void writeToParcel(Parcel out, int flags) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001316 out.writeInt(this.viewId);
1317 out.writeString(this.methodName);
1318 out.writeInt(this.type);
Romain Guya5475592009-07-01 17:20:08 -07001319 //noinspection ConstantIfStatement
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001320 if (false) {
Adam Cohen50f3d1b2012-12-11 18:36:07 -08001321 Log.d(LOG_TAG, "write viewId=0x" + Integer.toHexString(this.viewId)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001322 + " methodName=" + this.methodName + " type=" + this.type);
1323 }
Adam Cohenc6151f22012-02-02 21:02:31 -08001324
1325 // For some values which are null, we record an integer flag to indicate whether
1326 // we have written a valid value to the parcel.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001327 switch (this.type) {
1328 case BOOLEAN:
Sunny Goyal5b153922017-09-21 21:00:36 -07001329 out.writeBoolean((Boolean) this.value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001330 break;
1331 case BYTE:
Romain Guya5475592009-07-01 17:20:08 -07001332 out.writeByte((Byte) this.value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001333 break;
1334 case SHORT:
Romain Guya5475592009-07-01 17:20:08 -07001335 out.writeInt((Short) this.value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001336 break;
1337 case INT:
Romain Guya5475592009-07-01 17:20:08 -07001338 out.writeInt((Integer) this.value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001339 break;
1340 case LONG:
Romain Guya5475592009-07-01 17:20:08 -07001341 out.writeLong((Long) this.value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001342 break;
1343 case FLOAT:
Romain Guya5475592009-07-01 17:20:08 -07001344 out.writeFloat((Float) this.value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001345 break;
1346 case DOUBLE:
Romain Guya5475592009-07-01 17:20:08 -07001347 out.writeDouble((Double) this.value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001348 break;
1349 case CHAR:
1350 out.writeInt((int)((Character)this.value).charValue());
1351 break;
1352 case STRING:
1353 out.writeString((String)this.value);
1354 break;
1355 case CHAR_SEQUENCE:
Jim Millere667a7a2012-08-09 19:22:32 -07001356 TextUtils.writeToParcel((CharSequence)this.value, out, flags);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001357 break;
Bjorn Bringertd755b062010-01-06 17:15:37 +00001358 case BUNDLE:
1359 out.writeBundle((Bundle) this.value);
1360 break;
Sunny Goyal56333a82017-08-29 13:46:29 -07001361 case URI:
1362 case BITMAP:
Winson Chung499cb9f2010-07-16 11:18:17 -07001363 case INTENT:
Jorim Jaggief72a192014-08-26 21:57:46 +02001364 case COLOR_STATE_LIST:
Dan Sandlera22a3802015-05-13 00:12:47 -04001365 case ICON:
Sunny Goyal5b153922017-09-21 21:00:36 -07001366 out.writeTypedObject((Parcelable) this.value, flags);
Dan Sandlera22a3802015-05-13 00:12:47 -04001367 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001368 default:
1369 break;
1370 }
1371 }
1372
Romain Guye4d4e202013-07-22 13:02:02 -07001373 private Class<?> getParameterType() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001374 switch (this.type) {
1375 case BOOLEAN:
1376 return boolean.class;
1377 case BYTE:
1378 return byte.class;
1379 case SHORT:
1380 return short.class;
1381 case INT:
1382 return int.class;
1383 case LONG:
1384 return long.class;
1385 case FLOAT:
1386 return float.class;
1387 case DOUBLE:
1388 return double.class;
1389 case CHAR:
1390 return char.class;
1391 case STRING:
1392 return String.class;
1393 case CHAR_SEQUENCE:
1394 return CharSequence.class;
1395 case URI:
1396 return Uri.class;
1397 case BITMAP:
1398 return Bitmap.class;
Bjorn Bringertd755b062010-01-06 17:15:37 +00001399 case BUNDLE:
1400 return Bundle.class;
Winson Chung499cb9f2010-07-16 11:18:17 -07001401 case INTENT:
1402 return Intent.class;
Jorim Jaggief72a192014-08-26 21:57:46 +02001403 case COLOR_STATE_LIST:
1404 return ColorStateList.class;
Dan Sandlera22a3802015-05-13 00:12:47 -04001405 case ICON:
1406 return Icon.class;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001407 default:
1408 return null;
1409 }
1410 }
1411
1412 @Override
Dianne Hackborna1940212012-06-28 16:07:22 -07001413 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001414 final View view = root.findViewById(viewId);
Joe Onorato2b69ce42010-10-31 11:35:41 -07001415 if (view == null) return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001416
Romain Guye4d4e202013-07-22 13:02:02 -07001417 Class<?> param = getParameterType();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001418 if (param == null) {
1419 throw new ActionException("bad type: " + this.type);
1420 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001421 try {
Sunny Goyal271e3222017-08-29 16:05:47 -07001422 getMethod(view, this.methodName, param, false /* async */).invoke(view, this.value);
1423 } catch (Throwable ex) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001424 throw new ActionException(ex);
1425 }
1426 }
Winson Chung3ec9a452010-09-23 16:40:28 -07001427
Sunny Goyaldd292f42015-12-02 14:29:27 -08001428 @Override
1429 public Action initActionAsync(ViewTree root, ViewGroup rootParent, OnClickHandler handler) {
1430 final View view = root.findViewById(viewId);
1431 if (view == null) return ACTION_NOOP;
1432
1433 Class<?> param = getParameterType();
1434 if (param == null) {
1435 throw new ActionException("bad type: " + this.type);
1436 }
1437
1438 try {
Sunny Goyal271e3222017-08-29 16:05:47 -07001439 MethodHandle method = getMethod(view, this.methodName, param, true /* async */);
Sunny Goyaldd292f42015-12-02 14:29:27 -08001440
Sunny Goyal271e3222017-08-29 16:05:47 -07001441 if (method != null) {
1442 Runnable endAction = (Runnable) method.invoke(view, this.value);
Sunny Goyaldd292f42015-12-02 14:29:27 -08001443 if (endAction == null) {
1444 return ACTION_NOOP;
1445 } else {
Sunny Goyal7b0e2c72016-11-03 14:48:05 -07001446 // Special case view stub
1447 if (endAction instanceof ViewStub.ViewReplaceRunnable) {
1448 root.createTree();
1449 // Replace child tree
1450 root.findViewTreeById(viewId).replaceView(
1451 ((ViewStub.ViewReplaceRunnable) endAction).view);
1452 }
Sunny Goyaldd292f42015-12-02 14:29:27 -08001453 return new RunnableAction(endAction);
1454 }
1455 }
Sunny Goyal271e3222017-08-29 16:05:47 -07001456 } catch (Throwable ex) {
Sunny Goyaldd292f42015-12-02 14:29:27 -08001457 throw new ActionException(ex);
1458 }
1459
1460 return this;
1461 }
1462
Adam Cohenfbe44b72012-09-19 20:36:23 -07001463 public int mergeBehavior() {
1464 // smoothScrollBy is cumulative, everything else overwites.
1465 if (methodName.equals("smoothScrollBy")) {
1466 return MERGE_APPEND;
1467 } else {
1468 return MERGE_REPLACE;
Winson Chung3ec9a452010-09-23 16:40:28 -07001469 }
1470 }
Adam Cohenfbe44b72012-09-19 20:36:23 -07001471
Sunny Goyal5b153922017-09-21 21:00:36 -07001472 @Override
1473 public int getActionTag() {
1474 return REFLECTION_ACTION_TAG;
1475 }
1476
1477 @Override
1478 public String getUniqueKey() {
Adam Cohenfbe44b72012-09-19 20:36:23 -07001479 // Each type of reflection action corresponds to a setter, so each should be seen as
1480 // unique from the standpoint of merging.
Sunny Goyal5b153922017-09-21 21:00:36 -07001481 return super.getUniqueKey() + this.methodName + this.type;
Adam Cohenfbe44b72012-09-19 20:36:23 -07001482 }
Sunny Goyal5c022632016-02-17 16:30:41 -08001483
1484 @Override
1485 public boolean prefersAsyncApply() {
1486 return this.type == URI || this.type == ICON;
1487 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001488 }
1489
Sunny Goyaldd292f42015-12-02 14:29:27 -08001490 /**
1491 * This is only used for async execution of actions and it not parcelable.
1492 */
1493 private static final class RunnableAction extends RuntimeAction {
1494 private final Runnable mRunnable;
1495
1496 RunnableAction(Runnable r) {
1497 mRunnable = r;
1498 }
1499
1500 @Override
1501 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
1502 mRunnable.run();
1503 }
1504 }
1505
Adam Cohen5d200642012-04-24 10:43:31 -07001506 private void configureRemoteViewsAsChild(RemoteViews rv) {
Adam Cohen5d200642012-04-24 10:43:31 -07001507 rv.setBitmapCache(mBitmapCache);
1508 rv.setNotRoot();
1509 }
1510
1511 void setNotRoot() {
1512 mIsRoot = false;
1513 }
1514
Jeff Sharkey1162fd72009-11-04 17:58:08 -08001515 /**
Anthony Chen8f5f3582017-04-11 11:18:37 -07001516 * ViewGroup methods that are related to adding Views.
Jeff Sharkey1162fd72009-11-04 17:58:08 -08001517 */
Anthony Chen8f5f3582017-04-11 11:18:37 -07001518 private class ViewGroupActionAdd extends Action {
1519 private RemoteViews mNestedViews;
1520 private int mIndex;
1521
1522 ViewGroupActionAdd(int viewId, RemoteViews nestedViews) {
1523 this(viewId, nestedViews, -1 /* index */);
1524 }
1525
1526 ViewGroupActionAdd(int viewId, RemoteViews nestedViews, int index) {
Jeff Sharkey1162fd72009-11-04 17:58:08 -08001527 this.viewId = viewId;
Anthony Chen8f5f3582017-04-11 11:18:37 -07001528 mNestedViews = nestedViews;
1529 mIndex = index;
Adam Cohenc431c152012-04-26 18:42:17 -07001530 if (nestedViews != null) {
1531 configureRemoteViewsAsChild(nestedViews);
1532 }
Jeff Sharkey1162fd72009-11-04 17:58:08 -08001533 }
1534
Anthony Chen8f5f3582017-04-11 11:18:37 -07001535 ViewGroupActionAdd(Parcel parcel, BitmapCache bitmapCache, ApplicationInfo info,
Adrian Roosfb921842017-10-26 14:49:56 +02001536 int depth, Map<Class, Object> classCookies) {
Jeff Sharkey1162fd72009-11-04 17:58:08 -08001537 viewId = parcel.readInt();
Anthony Chenea202f62017-07-05 11:22:25 -07001538 mIndex = parcel.readInt();
Adrian Roosfb921842017-10-26 14:49:56 +02001539 mNestedViews = new RemoteViews(parcel, bitmapCache, info, depth, classCookies);
Jeff Sharkey1162fd72009-11-04 17:58:08 -08001540 }
1541
1542 public void writeToParcel(Parcel dest, int flags) {
Jeff Sharkey1162fd72009-11-04 17:58:08 -08001543 dest.writeInt(viewId);
Anthony Chenea202f62017-07-05 11:22:25 -07001544 dest.writeInt(mIndex);
Anthony Chen8f5f3582017-04-11 11:18:37 -07001545 mNestedViews.writeToParcel(dest, flags);
Jeff Sharkey1162fd72009-11-04 17:58:08 -08001546 }
1547
1548 @Override
Sunny Goyal5d8bcdf2016-10-27 16:11:29 -07001549 public boolean hasSameAppInfo(ApplicationInfo parentInfo) {
Sunny Goyaldd60f4d2017-10-18 15:22:42 -07001550 return mNestedViews.hasSameAppInfo(parentInfo);
Sunny Goyal5d8bcdf2016-10-27 16:11:29 -07001551 }
1552
1553 @Override
Dianne Hackborna1940212012-06-28 16:07:22 -07001554 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
Jeff Sharkey1162fd72009-11-04 17:58:08 -08001555 final Context context = root.getContext();
Alan Viverette8e1a7292017-02-27 10:57:58 -05001556 final ViewGroup target = root.findViewById(viewId);
Anthony Chen8f5f3582017-04-11 11:18:37 -07001557
1558 if (target == null) {
1559 return;
Jeff Sharkey1162fd72009-11-04 17:58:08 -08001560 }
Anthony Chen8f5f3582017-04-11 11:18:37 -07001561
1562 // Inflate nested views and add as children
1563 target.addView(mNestedViews.apply(context, target, handler), mIndex);
Jeff Sharkey1162fd72009-11-04 17:58:08 -08001564 }
1565
Winson Chung3ec9a452010-09-23 16:40:28 -07001566 @Override
Sunny Goyaldd292f42015-12-02 14:29:27 -08001567 public Action initActionAsync(ViewTree root, ViewGroup rootParent, OnClickHandler handler) {
1568 // In the async implementation, update the view tree so that subsequent calls to
Anthony Chen8f5f3582017-04-11 11:18:37 -07001569 // findViewById return the current view.
Sunny Goyaldd292f42015-12-02 14:29:27 -08001570 root.createTree();
1571 ViewTree target = root.findViewTreeById(viewId);
1572 if ((target == null) || !(target.mRoot instanceof ViewGroup)) {
1573 return ACTION_NOOP;
1574 }
Sunny Goyal7b0e2c72016-11-03 14:48:05 -07001575 final ViewGroup targetVg = (ViewGroup) target.mRoot;
Sunny Goyaldd292f42015-12-02 14:29:27 -08001576
Anthony Chen8f5f3582017-04-11 11:18:37 -07001577 // Inflate nested views and perform all the async tasks for the child remoteView.
1578 final Context context = root.mRoot.getContext();
1579 final AsyncApplyTask task = mNestedViews.getAsyncApplyTask(
1580 context, targetVg, null, handler);
1581 final ViewTree tree = task.doInBackground();
Sunny Goyaldd292f42015-12-02 14:29:27 -08001582
Anthony Chen8f5f3582017-04-11 11:18:37 -07001583 if (tree == null) {
1584 throw new ActionException(task.mError);
Sunny Goyaldd292f42015-12-02 14:29:27 -08001585 }
Anthony Chen8f5f3582017-04-11 11:18:37 -07001586
1587 // Update the global view tree, so that next call to findViewTreeById
1588 // goes through the subtree as well.
1589 target.addChild(tree, mIndex);
1590
1591 return new RuntimeAction() {
1592 @Override
1593 public void apply(View root, ViewGroup rootParent, OnClickHandler handler)
1594 throws ActionException {
1595 task.onPostExecute(tree);
1596 targetVg.addView(task.mResult, mIndex);
1597 }
1598 };
Sunny Goyaldd292f42015-12-02 14:29:27 -08001599 }
1600
1601 @Override
Adam Cohen5d200642012-04-24 10:43:31 -07001602 public void setBitmapCache(BitmapCache bitmapCache) {
Anthony Chen8f5f3582017-04-11 11:18:37 -07001603 mNestedViews.setBitmapCache(bitmapCache);
Winson Chung3ec9a452010-09-23 16:40:28 -07001604 }
1605
Anthony Chen8f5f3582017-04-11 11:18:37 -07001606 @Override
Adam Cohenfbe44b72012-09-19 20:36:23 -07001607 public int mergeBehavior() {
1608 return MERGE_APPEND;
1609 }
1610
Sunny Goyal5c022632016-02-17 16:30:41 -08001611 @Override
1612 public boolean prefersAsyncApply() {
Anthony Chen8f5f3582017-04-11 11:18:37 -07001613 return mNestedViews.prefersAsyncApply();
Sunny Goyal5c022632016-02-17 16:30:41 -08001614 }
1615
Anthony Chen8f5f3582017-04-11 11:18:37 -07001616 @Override
Sunny Goyal5b153922017-09-21 21:00:36 -07001617 public int getActionTag() {
1618 return VIEW_GROUP_ACTION_ADD_TAG;
Anthony Chen8f5f3582017-04-11 11:18:37 -07001619 }
1620 }
1621
1622 /**
1623 * ViewGroup methods related to removing child views.
1624 */
1625 private class ViewGroupActionRemove extends Action {
1626 /**
1627 * Id that indicates that all child views of the affected ViewGroup should be removed.
1628 *
1629 * <p>Using -2 because the default id is -1. This avoids accidentally matching that.
1630 */
1631 private static final int REMOVE_ALL_VIEWS_ID = -2;
1632
1633 private int mViewIdToKeep;
1634
1635 ViewGroupActionRemove(int viewId) {
1636 this(viewId, REMOVE_ALL_VIEWS_ID);
1637 }
1638
1639 ViewGroupActionRemove(int viewId, int viewIdToKeep) {
1640 this.viewId = viewId;
1641 mViewIdToKeep = viewIdToKeep;
1642 }
1643
1644 ViewGroupActionRemove(Parcel parcel) {
1645 viewId = parcel.readInt();
1646 mViewIdToKeep = parcel.readInt();
1647 }
1648
1649 public void writeToParcel(Parcel dest, int flags) {
Anthony Chen8f5f3582017-04-11 11:18:37 -07001650 dest.writeInt(viewId);
1651 dest.writeInt(mViewIdToKeep);
1652 }
1653
1654 @Override
1655 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
1656 final ViewGroup target = root.findViewById(viewId);
1657
1658 if (target == null) {
1659 return;
1660 }
1661
1662 if (mViewIdToKeep == REMOVE_ALL_VIEWS_ID) {
1663 target.removeAllViews();
1664 return;
1665 }
1666
1667 removeAllViewsExceptIdToKeep(target);
1668 }
1669
1670 @Override
1671 public Action initActionAsync(ViewTree root, ViewGroup rootParent, OnClickHandler handler) {
1672 // In the async implementation, update the view tree so that subsequent calls to
1673 // findViewById return the current view.
1674 root.createTree();
1675 ViewTree target = root.findViewTreeById(viewId);
1676
1677 if ((target == null) || !(target.mRoot instanceof ViewGroup)) {
1678 return ACTION_NOOP;
1679 }
1680
1681 final ViewGroup targetVg = (ViewGroup) target.mRoot;
1682
1683 // Clear all children when nested views omitted
1684 target.mChildren = null;
1685 return new RuntimeAction() {
1686 @Override
1687 public void apply(View root, ViewGroup rootParent, OnClickHandler handler)
1688 throws ActionException {
1689 if (mViewIdToKeep == REMOVE_ALL_VIEWS_ID) {
1690 targetVg.removeAllViews();
1691 return;
1692 }
1693
1694 removeAllViewsExceptIdToKeep(targetVg);
1695 }
1696 };
1697 }
1698
1699 /**
1700 * Iterates through the children in the given ViewGroup and removes all the views that
1701 * do not have an id of {@link #mViewIdToKeep}.
1702 */
1703 private void removeAllViewsExceptIdToKeep(ViewGroup viewGroup) {
1704 // Otherwise, remove all the views that do not match the id to keep.
1705 int index = viewGroup.getChildCount() - 1;
1706 while (index >= 0) {
1707 if (viewGroup.getChildAt(index).getId() != mViewIdToKeep) {
1708 viewGroup.removeViewAt(index);
1709 }
1710 index--;
1711 }
1712 }
1713
1714 @Override
Sunny Goyal5b153922017-09-21 21:00:36 -07001715 public int getActionTag() {
1716 return VIEW_GROUP_ACTION_REMOVE_TAG;
Anthony Chen8f5f3582017-04-11 11:18:37 -07001717 }
1718
1719 @Override
1720 public int mergeBehavior() {
1721 return MERGE_APPEND;
1722 }
Jeff Sharkey1162fd72009-11-04 17:58:08 -08001723 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001724
1725 /**
Daniel Sandler820ba322012-03-23 16:36:00 -05001726 * Helper action to set compound drawables on a TextView. Supports relative
1727 * (s/t/e/b) or cardinal (l/t/r/b) arrangement.
1728 */
1729 private class TextViewDrawableAction extends Action {
1730 public TextViewDrawableAction(int viewId, boolean isRelative, int d1, int d2, int d3, int d4) {
1731 this.viewId = viewId;
1732 this.isRelative = isRelative;
Dan Sandler912282e2015-07-28 22:49:30 -04001733 this.useIcons = false;
Daniel Sandler820ba322012-03-23 16:36:00 -05001734 this.d1 = d1;
1735 this.d2 = d2;
1736 this.d3 = d3;
1737 this.d4 = d4;
1738 }
1739
Dan Sandler912282e2015-07-28 22:49:30 -04001740 public TextViewDrawableAction(int viewId, boolean isRelative,
1741 Icon i1, Icon i2, Icon i3, Icon i4) {
1742 this.viewId = viewId;
1743 this.isRelative = isRelative;
1744 this.useIcons = true;
1745 this.i1 = i1;
1746 this.i2 = i2;
1747 this.i3 = i3;
1748 this.i4 = i4;
1749 }
1750
Daniel Sandler820ba322012-03-23 16:36:00 -05001751 public TextViewDrawableAction(Parcel parcel) {
1752 viewId = parcel.readInt();
1753 isRelative = (parcel.readInt() != 0);
Dan Sandler912282e2015-07-28 22:49:30 -04001754 useIcons = (parcel.readInt() != 0);
1755 if (useIcons) {
Sunny Goyal5b153922017-09-21 21:00:36 -07001756 i1 = parcel.readTypedObject(Icon.CREATOR);
1757 i2 = parcel.readTypedObject(Icon.CREATOR);
1758 i3 = parcel.readTypedObject(Icon.CREATOR);
1759 i4 = parcel.readTypedObject(Icon.CREATOR);
Dan Sandler912282e2015-07-28 22:49:30 -04001760 } else {
1761 d1 = parcel.readInt();
1762 d2 = parcel.readInt();
1763 d3 = parcel.readInt();
1764 d4 = parcel.readInt();
1765 }
Daniel Sandler820ba322012-03-23 16:36:00 -05001766 }
1767
1768 public void writeToParcel(Parcel dest, int flags) {
Daniel Sandler820ba322012-03-23 16:36:00 -05001769 dest.writeInt(viewId);
1770 dest.writeInt(isRelative ? 1 : 0);
Dan Sandler912282e2015-07-28 22:49:30 -04001771 dest.writeInt(useIcons ? 1 : 0);
1772 if (useIcons) {
Sunny Goyal5b153922017-09-21 21:00:36 -07001773 dest.writeTypedObject(i1, 0);
1774 dest.writeTypedObject(i2, 0);
1775 dest.writeTypedObject(i3, 0);
1776 dest.writeTypedObject(i4, 0);
Dan Sandler912282e2015-07-28 22:49:30 -04001777 } else {
1778 dest.writeInt(d1);
1779 dest.writeInt(d2);
1780 dest.writeInt(d3);
1781 dest.writeInt(d4);
1782 }
Daniel Sandler820ba322012-03-23 16:36:00 -05001783 }
1784
1785 @Override
Dianne Hackborna1940212012-06-28 16:07:22 -07001786 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
Alan Viverette8e1a7292017-02-27 10:57:58 -05001787 final TextView target = root.findViewById(viewId);
Daniel Sandler820ba322012-03-23 16:36:00 -05001788 if (target == null) return;
Sunny Goyaldd292f42015-12-02 14:29:27 -08001789 if (drawablesLoaded) {
1790 if (isRelative) {
1791 target.setCompoundDrawablesRelativeWithIntrinsicBounds(id1, id2, id3, id4);
1792 } else {
1793 target.setCompoundDrawablesWithIntrinsicBounds(id1, id2, id3, id4);
1794 }
1795 } else if (useIcons) {
Dan Sandler912282e2015-07-28 22:49:30 -04001796 final Context ctx = target.getContext();
1797 final Drawable id1 = i1 == null ? null : i1.loadDrawable(ctx);
1798 final Drawable id2 = i2 == null ? null : i2.loadDrawable(ctx);
1799 final Drawable id3 = i3 == null ? null : i3.loadDrawable(ctx);
1800 final Drawable id4 = i4 == null ? null : i4.loadDrawable(ctx);
1801 if (isRelative) {
1802 target.setCompoundDrawablesRelativeWithIntrinsicBounds(id1, id2, id3, id4);
1803 } else {
1804 target.setCompoundDrawablesWithIntrinsicBounds(id1, id2, id3, id4);
1805 }
Daniel Sandler820ba322012-03-23 16:36:00 -05001806 } else {
Dan Sandler912282e2015-07-28 22:49:30 -04001807 if (isRelative) {
1808 target.setCompoundDrawablesRelativeWithIntrinsicBounds(d1, d2, d3, d4);
1809 } else {
1810 target.setCompoundDrawablesWithIntrinsicBounds(d1, d2, d3, d4);
1811 }
Daniel Sandler820ba322012-03-23 16:36:00 -05001812 }
1813 }
1814
Sunny Goyaldd292f42015-12-02 14:29:27 -08001815 @Override
1816 public Action initActionAsync(ViewTree root, ViewGroup rootParent, OnClickHandler handler) {
Alan Viverette04fd4702017-04-13 16:37:06 -04001817 final TextView target = root.findViewById(viewId);
Sunny Goyaldd292f42015-12-02 14:29:27 -08001818 if (target == null) return ACTION_NOOP;
1819
1820 TextViewDrawableAction copy = useIcons ?
1821 new TextViewDrawableAction(viewId, isRelative, i1, i2, i3, i4) :
1822 new TextViewDrawableAction(viewId, isRelative, d1, d2, d3, d4);
1823
1824 // Load the drawables on the background thread.
1825 copy.drawablesLoaded = true;
1826 final Context ctx = target.getContext();
1827
1828 if (useIcons) {
1829 copy.id1 = i1 == null ? null : i1.loadDrawable(ctx);
1830 copy.id2 = i2 == null ? null : i2.loadDrawable(ctx);
1831 copy.id3 = i3 == null ? null : i3.loadDrawable(ctx);
1832 copy.id4 = i4 == null ? null : i4.loadDrawable(ctx);
1833 } else {
1834 copy.id1 = d1 == 0 ? null : ctx.getDrawable(d1);
1835 copy.id2 = d2 == 0 ? null : ctx.getDrawable(d2);
1836 copy.id3 = d3 == 0 ? null : ctx.getDrawable(d3);
1837 copy.id4 = d4 == 0 ? null : ctx.getDrawable(d4);
1838 }
1839 return copy;
1840 }
1841
Sunny Goyal5c022632016-02-17 16:30:41 -08001842 @Override
1843 public boolean prefersAsyncApply() {
1844 return useIcons;
1845 }
1846
Sunny Goyal5b153922017-09-21 21:00:36 -07001847 @Override
1848 public int getActionTag() {
1849 return TEXT_VIEW_DRAWABLE_ACTION_TAG;
Adam Cohenfbe44b72012-09-19 20:36:23 -07001850 }
1851
Daniel Sandler820ba322012-03-23 16:36:00 -05001852 boolean isRelative = false;
Dan Sandler912282e2015-07-28 22:49:30 -04001853 boolean useIcons = false;
Daniel Sandler820ba322012-03-23 16:36:00 -05001854 int d1, d2, d3, d4;
Dan Sandler912282e2015-07-28 22:49:30 -04001855 Icon i1, i2, i3, i4;
Daniel Sandler820ba322012-03-23 16:36:00 -05001856
Sunny Goyaldd292f42015-12-02 14:29:27 -08001857 boolean drawablesLoaded = false;
1858 Drawable id1, id2, id3, id4;
Daniel Sandler820ba322012-03-23 16:36:00 -05001859 }
1860
1861 /**
Daniel Sandler99d1f742012-05-21 16:14:14 -04001862 * Helper action to set text size on a TextView in any supported units.
Daniel Sandler7264f712012-05-21 14:48:23 -04001863 */
1864 private class TextViewSizeAction extends Action {
1865 public TextViewSizeAction(int viewId, int units, float size) {
1866 this.viewId = viewId;
1867 this.units = units;
1868 this.size = size;
1869 }
1870
1871 public TextViewSizeAction(Parcel parcel) {
1872 viewId = parcel.readInt();
1873 units = parcel.readInt();
1874 size = parcel.readFloat();
1875 }
1876
1877 public void writeToParcel(Parcel dest, int flags) {
Daniel Sandler7264f712012-05-21 14:48:23 -04001878 dest.writeInt(viewId);
1879 dest.writeInt(units);
1880 dest.writeFloat(size);
1881 }
1882
1883 @Override
Dianne Hackborna1940212012-06-28 16:07:22 -07001884 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
Alan Viverette8e1a7292017-02-27 10:57:58 -05001885 final TextView target = root.findViewById(viewId);
Daniel Sandler7264f712012-05-21 14:48:23 -04001886 if (target == null) return;
1887 target.setTextSize(units, size);
1888 }
1889
Sunny Goyal5b153922017-09-21 21:00:36 -07001890 @Override
1891 public int getActionTag() {
1892 return TEXT_VIEW_SIZE_ACTION_TAG;
Adam Cohenfbe44b72012-09-19 20:36:23 -07001893 }
1894
Daniel Sandler7264f712012-05-21 14:48:23 -04001895 int units;
1896 float size;
Daniel Sandler7264f712012-05-21 14:48:23 -04001897 }
1898
1899 /**
Daniel Sandler99d1f742012-05-21 16:14:14 -04001900 * Helper action to set padding on a View.
1901 */
1902 private class ViewPaddingAction extends Action {
1903 public ViewPaddingAction(int viewId, int left, int top, int right, int bottom) {
1904 this.viewId = viewId;
1905 this.left = left;
1906 this.top = top;
1907 this.right = right;
1908 this.bottom = bottom;
1909 }
1910
1911 public ViewPaddingAction(Parcel parcel) {
1912 viewId = parcel.readInt();
1913 left = parcel.readInt();
1914 top = parcel.readInt();
1915 right = parcel.readInt();
1916 bottom = parcel.readInt();
1917 }
1918
1919 public void writeToParcel(Parcel dest, int flags) {
Daniel Sandler99d1f742012-05-21 16:14:14 -04001920 dest.writeInt(viewId);
1921 dest.writeInt(left);
1922 dest.writeInt(top);
1923 dest.writeInt(right);
1924 dest.writeInt(bottom);
1925 }
1926
1927 @Override
Dianne Hackborna1940212012-06-28 16:07:22 -07001928 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
Daniel Sandler99d1f742012-05-21 16:14:14 -04001929 final View target = root.findViewById(viewId);
1930 if (target == null) return;
1931 target.setPadding(left, top, right, bottom);
1932 }
1933
Sunny Goyal5b153922017-09-21 21:00:36 -07001934 @Override
1935 public int getActionTag() {
1936 return VIEW_PADDING_ACTION_TAG;
Adam Cohenfbe44b72012-09-19 20:36:23 -07001937 }
1938
Daniel Sandler99d1f742012-05-21 16:14:14 -04001939 int left, top, right, bottom;
Daniel Sandler99d1f742012-05-21 16:14:14 -04001940 }
1941
1942 /**
Adrian Roos9b123cf2016-02-04 14:55:57 -08001943 * Helper action to set layout params on a View.
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001944 */
Adrian Roos2d5dbba2016-06-08 17:11:53 -07001945 private static class LayoutParamAction extends Action {
Adrian Roos9b123cf2016-02-04 14:55:57 -08001946
1947 /** Set marginEnd */
Adrian Roos2d5dbba2016-06-08 17:11:53 -07001948 public static final int LAYOUT_MARGIN_END_DIMEN = 1;
Adrian Roos9b123cf2016-02-04 14:55:57 -08001949 /** Set width */
1950 public static final int LAYOUT_WIDTH = 2;
Adrian Roos2d5dbba2016-06-08 17:11:53 -07001951 public static final int LAYOUT_MARGIN_BOTTOM_DIMEN = 3;
Adrian Roos9b123cf2016-02-04 14:55:57 -08001952
Sunny Goyal5b153922017-09-21 21:00:36 -07001953 final int mProperty;
1954 final int mValue;
1955
Adrian Roos9b123cf2016-02-04 14:55:57 -08001956 /**
1957 * @param viewId ID of the view alter
1958 * @param property which layout parameter to alter
1959 * @param value new value of the layout parameter
1960 */
1961 public LayoutParamAction(int viewId, int property, int value) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001962 this.viewId = viewId;
Sunny Goyal5b153922017-09-21 21:00:36 -07001963 this.mProperty = property;
1964 this.mValue = value;
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001965 }
1966
Adrian Roos9b123cf2016-02-04 14:55:57 -08001967 public LayoutParamAction(Parcel parcel) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001968 viewId = parcel.readInt();
Sunny Goyal5b153922017-09-21 21:00:36 -07001969 mProperty = parcel.readInt();
1970 mValue = parcel.readInt();
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001971 }
1972
1973 public void writeToParcel(Parcel dest, int flags) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001974 dest.writeInt(viewId);
Sunny Goyal5b153922017-09-21 21:00:36 -07001975 dest.writeInt(mProperty);
1976 dest.writeInt(mValue);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001977 }
1978
1979 @Override
1980 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
1981 final View target = root.findViewById(viewId);
1982 if (target == null) {
1983 return;
1984 }
1985 ViewGroup.LayoutParams layoutParams = target.getLayoutParams();
Adrian Roos9b123cf2016-02-04 14:55:57 -08001986 if (layoutParams == null) {
1987 return;
1988 }
Sunny Goyal5b153922017-09-21 21:00:36 -07001989 switch (mProperty) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07001990 case LAYOUT_MARGIN_END_DIMEN:
Adrian Roos9b123cf2016-02-04 14:55:57 -08001991 if (layoutParams instanceof ViewGroup.MarginLayoutParams) {
Sunny Goyal5b153922017-09-21 21:00:36 -07001992 int resolved = resolveDimenPixelOffset(target, mValue);
Adrian Roos2d5dbba2016-06-08 17:11:53 -07001993 ((ViewGroup.MarginLayoutParams) layoutParams).setMarginEnd(resolved);
Adrian Roos9b123cf2016-02-04 14:55:57 -08001994 target.setLayoutParams(layoutParams);
1995 }
1996 break;
Adrian Roos2d5dbba2016-06-08 17:11:53 -07001997 case LAYOUT_MARGIN_BOTTOM_DIMEN:
Adrian Roosc1a80b02016-04-05 14:54:55 -07001998 if (layoutParams instanceof ViewGroup.MarginLayoutParams) {
Sunny Goyal5b153922017-09-21 21:00:36 -07001999 int resolved = resolveDimenPixelOffset(target, mValue);
Adrian Roos2d5dbba2016-06-08 17:11:53 -07002000 ((ViewGroup.MarginLayoutParams) layoutParams).bottomMargin = resolved;
Adrian Roosc1a80b02016-04-05 14:54:55 -07002001 target.setLayoutParams(layoutParams);
2002 }
2003 break;
Adrian Roos9b123cf2016-02-04 14:55:57 -08002004 case LAYOUT_WIDTH:
Sunny Goyal5b153922017-09-21 21:00:36 -07002005 layoutParams.width = mValue;
Adrian Roos9b123cf2016-02-04 14:55:57 -08002006 target.setLayoutParams(layoutParams);
2007 break;
2008 default:
Sunny Goyal5b153922017-09-21 21:00:36 -07002009 throw new IllegalArgumentException("Unknown property " + mProperty);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07002010 }
2011 }
2012
Adrian Roos2d5dbba2016-06-08 17:11:53 -07002013 private static int resolveDimenPixelOffset(View target, int value) {
2014 if (value == 0) {
2015 return 0;
2016 }
2017 return target.getContext().getResources().getDimensionPixelOffset(value);
2018 }
2019
Sunny Goyal5b153922017-09-21 21:00:36 -07002020 @Override
2021 public int getActionTag() {
2022 return LAYOUT_PARAM_ACTION_TAG;
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01002023 }
2024
2025 @Override
Sunny Goyal5b153922017-09-21 21:00:36 -07002026 public String getUniqueKey() {
2027 return super.getUniqueKey() + mProperty;
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01002028 }
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01002029 }
2030
2031 /**
Adrian Roosfe84e1f2015-11-04 15:55:39 -08002032 * Helper action to add a view tag with RemoteInputs.
2033 */
2034 private class SetRemoteInputsAction extends Action {
2035
2036 public SetRemoteInputsAction(int viewId, RemoteInput[] remoteInputs) {
2037 this.viewId = viewId;
2038 this.remoteInputs = remoteInputs;
2039 }
2040
2041 public SetRemoteInputsAction(Parcel parcel) {
2042 viewId = parcel.readInt();
Adrian Roos5dd685f2016-02-24 12:05:51 -08002043 remoteInputs = parcel.createTypedArray(RemoteInput.CREATOR);
Adrian Roosfe84e1f2015-11-04 15:55:39 -08002044 }
2045
2046 public void writeToParcel(Parcel dest, int flags) {
Adrian Roosfe84e1f2015-11-04 15:55:39 -08002047 dest.writeInt(viewId);
Adrian Roos5dd685f2016-02-24 12:05:51 -08002048 dest.writeTypedArray(remoteInputs, flags);
Adrian Roosfe84e1f2015-11-04 15:55:39 -08002049 }
2050
2051 @Override
2052 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
Selim Cinek5d6ef8d2017-05-18 22:16:00 -07002053 final View target = root.findViewById(viewId);
Adrian Roosfe84e1f2015-11-04 15:55:39 -08002054 if (target == null) return;
2055
2056 target.setTagInternal(R.id.remote_input_tag, remoteInputs);
2057 }
2058
Sunny Goyal5b153922017-09-21 21:00:36 -07002059 @Override
2060 public int getActionTag() {
2061 return SET_REMOTE_INPUTS_ACTION_TAG;
Adrian Roosfe84e1f2015-11-04 15:55:39 -08002062 }
2063
2064 final Parcelable[] remoteInputs;
Adrian Roosfe84e1f2015-11-04 15:55:39 -08002065 }
2066
2067 /**
Selim Cinek87c31532017-08-18 18:53:44 -07002068 * Helper action to override all textViewColors
2069 */
2070 private class OverrideTextColorsAction extends Action {
2071
2072 private final int textColor;
2073
2074 public OverrideTextColorsAction(int textColor) {
2075 this.textColor = textColor;
2076 }
2077
2078 public OverrideTextColorsAction(Parcel parcel) {
2079 textColor = parcel.readInt();
2080 }
2081
2082 public void writeToParcel(Parcel dest, int flags) {
Selim Cinek87c31532017-08-18 18:53:44 -07002083 dest.writeInt(textColor);
2084 }
2085
2086 @Override
2087 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
2088 // Let's traverse the viewtree and override all textColors!
2089 Stack<View> viewsToProcess = new Stack<>();
2090 viewsToProcess.add(root);
2091 while (!viewsToProcess.isEmpty()) {
2092 View v = viewsToProcess.pop();
2093 if (v instanceof TextView) {
2094 TextView textView = (TextView) v;
2095 textView.setText(NotificationColorUtil.clearColorSpans(textView.getText()));
2096 textView.setTextColor(textColor);
2097 }
2098 if (v instanceof ViewGroup) {
2099 ViewGroup viewGroup = (ViewGroup) v;
2100 for (int i = 0; i < viewGroup.getChildCount(); i++) {
2101 viewsToProcess.push(viewGroup.getChildAt(i));
2102 }
2103 }
2104 }
2105 }
2106
Sunny Goyal5b153922017-09-21 21:00:36 -07002107 @Override
2108 public int getActionTag() {
2109 return OVERRIDE_TEXT_COLORS_TAG;
Selim Cinek87c31532017-08-18 18:53:44 -07002110 }
2111 }
2112
2113 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002114 * Create a new RemoteViews object that will display the views contained
2115 * in the specified layout file.
Jim Millere667a7a2012-08-09 19:22:32 -07002116 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002117 * @param packageName Name of the package that contains the layout resource
2118 * @param layoutId The id of the layout resource
2119 */
2120 public RemoteViews(String packageName, int layoutId) {
Svet Ganov0da85b62014-08-06 14:11:37 -07002121 this(getApplicationInfo(packageName, UserHandle.myUserId()), layoutId);
2122 }
2123
2124 /**
2125 * Create a new RemoteViews object that will display the views contained
2126 * in the specified layout file.
2127 *
2128 * @param packageName Name of the package that contains the layout resource.
2129 * @param userId The user under which the package is running.
2130 * @param layoutId The id of the layout resource.
2131 *
2132 * @hide
2133 */
2134 public RemoteViews(String packageName, int userId, int layoutId) {
2135 this(getApplicationInfo(packageName, userId), layoutId);
2136 }
2137
2138 /**
2139 * Create a new RemoteViews object that will display the views contained
2140 * in the specified layout file.
2141 *
2142 * @param application The application whose content is shown by the views.
2143 * @param layoutId The id of the layout resource.
Kenny Guy77320062014-08-27 21:37:15 +01002144 *
2145 * @hide
Svet Ganov0da85b62014-08-06 14:11:37 -07002146 */
Kenny Guy77320062014-08-27 21:37:15 +01002147 protected RemoteViews(ApplicationInfo application, int layoutId) {
Svet Ganov0da85b62014-08-06 14:11:37 -07002148 mApplication = application;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002149 mLayoutId = layoutId;
Adam Cohen5d200642012-04-24 10:43:31 -07002150 mBitmapCache = new BitmapCache();
Adrian Roosfb921842017-10-26 14:49:56 +02002151 mClassCookies = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002152 }
2153
Adam Cohen5d200642012-04-24 10:43:31 -07002154 private boolean hasLandscapeAndPortraitLayouts() {
2155 return (mLandscape != null) && (mPortrait != null);
2156 }
2157
Jeff Sharkey6d515712012-09-20 16:06:08 -07002158 /**
Adam Cohen5d200642012-04-24 10:43:31 -07002159 * Create a new RemoteViews object that will inflate as the specified
2160 * landspace or portrait RemoteViews, depending on the current configuration.
2161 *
2162 * @param landscape The RemoteViews to inflate in landscape configuration
2163 * @param portrait The RemoteViews to inflate in portrait configuration
2164 */
2165 public RemoteViews(RemoteViews landscape, RemoteViews portrait) {
2166 if (landscape == null || portrait == null) {
2167 throw new RuntimeException("Both RemoteViews must be non-null");
2168 }
Sunny Goyaldd60f4d2017-10-18 15:22:42 -07002169 if (!landscape.hasSameAppInfo(portrait.mApplication)) {
Svet Ganov0da85b62014-08-06 14:11:37 -07002170 throw new RuntimeException("Both RemoteViews must share the same package and user");
Adam Cohen5d200642012-04-24 10:43:31 -07002171 }
Svet Ganov0da85b62014-08-06 14:11:37 -07002172 mApplication = portrait.mApplication;
Adam Cohen5d200642012-04-24 10:43:31 -07002173 mLayoutId = portrait.getLayoutId();
2174
2175 mLandscape = landscape;
2176 mPortrait = portrait;
2177
Adam Cohen5d200642012-04-24 10:43:31 -07002178 mBitmapCache = new BitmapCache();
2179 configureRemoteViewsAsChild(landscape);
2180 configureRemoteViewsAsChild(portrait);
Adrian Roosfb921842017-10-26 14:49:56 +02002181
2182 mClassCookies = (portrait.mClassCookies != null)
2183 ? portrait.mClassCookies : landscape.mClassCookies;
Sunny Goyal56333a82017-08-29 13:46:29 -07002184 }
Adam Cohen5d200642012-04-24 10:43:31 -07002185
Sunny Goyal56333a82017-08-29 13:46:29 -07002186 /**
2187 * Creates a copy of another RemoteViews.
2188 */
2189 public RemoteViews(RemoteViews src) {
2190 mBitmapCache = src.mBitmapCache;
2191 mApplication = src.mApplication;
2192 mIsRoot = src.mIsRoot;
2193 mLayoutId = src.mLayoutId;
2194 mIsWidgetCollectionChild = src.mIsWidgetCollectionChild;
2195 mReapplyDisallowed = src.mReapplyDisallowed;
Adrian Roosfb921842017-10-26 14:49:56 +02002196 mClassCookies = src.mClassCookies;
Sunny Goyal56333a82017-08-29 13:46:29 -07002197
2198 if (src.hasLandscapeAndPortraitLayouts()) {
2199 mLandscape = new RemoteViews(src.mLandscape);
2200 mPortrait = new RemoteViews(src.mPortrait);
Sunny Goyal56333a82017-08-29 13:46:29 -07002201 }
2202
2203 if (src.mActions != null) {
Sunny Goyal56333a82017-08-29 13:46:29 -07002204 Parcel p = Parcel.obtain();
Adrian Roosfb921842017-10-26 14:49:56 +02002205 p.putClassCookies(mClassCookies);
Sunny Goyal0ebf8812017-09-25 10:02:59 -07002206 src.writeActionsToParcel(p);
Sunny Goyal5b7689f2017-09-21 11:08:34 -07002207 p.setDataPosition(0);
2208 // Since src is already in memory, we do not care about stack overflow as it has
2209 // already been read once.
2210 readActionsFromParcel(p, 0);
Sunny Goyal56333a82017-08-29 13:46:29 -07002211 p.recycle();
2212 }
2213
2214 // Now that everything is initialized and duplicated, setting a new BitmapCache will
2215 // re-initialize the cache.
2216 setBitmapCache(new BitmapCache());
Adam Cohen5d200642012-04-24 10:43:31 -07002217 }
2218
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002219 /**
2220 * Reads a RemoteViews object from a parcel.
Jim Millere667a7a2012-08-09 19:22:32 -07002221 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002222 * @param parcel
2223 */
2224 public RemoteViews(Parcel parcel) {
Adrian Roosfb921842017-10-26 14:49:56 +02002225 this(parcel, null, null, 0, null);
Adam Cohen5d200642012-04-24 10:43:31 -07002226 }
Adam Cohenca6fd842010-09-03 18:10:35 -07002227
Adrian Roosfb921842017-10-26 14:49:56 +02002228 private RemoteViews(Parcel parcel, BitmapCache bitmapCache, ApplicationInfo info, int depth,
2229 Map<Class, Object> classCookies) {
Sunny Goyal692f8c92016-11-11 09:19:14 -08002230 if (depth > MAX_NESTED_VIEWS
2231 && (UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID)) {
2232 throw new IllegalArgumentException("Too many nested views.");
2233 }
2234 depth++;
2235
Adam Cohen5d200642012-04-24 10:43:31 -07002236 int mode = parcel.readInt();
2237
2238 // We only store a bitmap cache in the root of the RemoteViews.
2239 if (bitmapCache == null) {
2240 mBitmapCache = new BitmapCache(parcel);
Adrian Roosfb921842017-10-26 14:49:56 +02002241 // Store the class cookies such that they are available when we clone this RemoteView.
2242 mClassCookies = parcel.copyClassCookies();
Adam Cohen5d200642012-04-24 10:43:31 -07002243 } else {
2244 setBitmapCache(bitmapCache);
Adrian Roosfb921842017-10-26 14:49:56 +02002245 mClassCookies = classCookies;
Adam Cohen5d200642012-04-24 10:43:31 -07002246 setNotRoot();
2247 }
2248
2249 if (mode == MODE_NORMAL) {
Sunny Goyal5d8bcdf2016-10-27 16:11:29 -07002250 mApplication = parcel.readInt() == 0 ? info :
2251 ApplicationInfo.CREATOR.createFromParcel(parcel);
Adam Cohen5d200642012-04-24 10:43:31 -07002252 mLayoutId = parcel.readInt();
Romain Guye4d4e202013-07-22 13:02:02 -07002253 mIsWidgetCollectionChild = parcel.readInt() == 1;
Adam Cohen5d200642012-04-24 10:43:31 -07002254
Sunny Goyal5b7689f2017-09-21 11:08:34 -07002255 readActionsFromParcel(parcel, depth);
Adam Cohen5d200642012-04-24 10:43:31 -07002256 } else {
2257 // MODE_HAS_LANDSCAPE_AND_PORTRAIT
Adrian Roosfb921842017-10-26 14:49:56 +02002258 mLandscape = new RemoteViews(parcel, mBitmapCache, info, depth, mClassCookies);
2259 mPortrait = new RemoteViews(parcel, mBitmapCache, mLandscape.mApplication, depth,
2260 mClassCookies);
Svet Ganov0da85b62014-08-06 14:11:37 -07002261 mApplication = mPortrait.mApplication;
Adam Cohen5d200642012-04-24 10:43:31 -07002262 mLayoutId = mPortrait.getLayoutId();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002263 }
Selim Cinekfc8073c2017-08-16 17:50:20 -07002264 mReapplyDisallowed = parcel.readInt() == 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002265 }
2266
Sunny Goyal5b7689f2017-09-21 11:08:34 -07002267 private void readActionsFromParcel(Parcel parcel, int depth) {
2268 int count = parcel.readInt();
2269 if (count > 0) {
2270 mActions = new ArrayList<>(count);
2271 for (int i = 0; i < count; i++) {
2272 mActions.add(getActionFromParcel(parcel, depth));
2273 }
2274 }
2275 }
2276
Sunny Goyal56333a82017-08-29 13:46:29 -07002277 private Action getActionFromParcel(Parcel parcel, int depth) {
2278 int tag = parcel.readInt();
2279 switch (tag) {
2280 case SET_ON_CLICK_PENDING_INTENT_TAG:
2281 return new SetOnClickPendingIntent(parcel);
Sunny Goyal5b153922017-09-21 21:00:36 -07002282 case SET_DRAWABLE_TINT_TAG:
2283 return new SetDrawableTint(parcel);
Sunny Goyal56333a82017-08-29 13:46:29 -07002284 case REFLECTION_ACTION_TAG:
2285 return new ReflectionAction(parcel);
2286 case VIEW_GROUP_ACTION_ADD_TAG:
Adrian Roosfb921842017-10-26 14:49:56 +02002287 return new ViewGroupActionAdd(parcel, mBitmapCache, mApplication, depth,
2288 mClassCookies);
Sunny Goyal56333a82017-08-29 13:46:29 -07002289 case VIEW_GROUP_ACTION_REMOVE_TAG:
2290 return new ViewGroupActionRemove(parcel);
Sunny Goyal5b153922017-09-21 21:00:36 -07002291 case VIEW_CONTENT_NAVIGATION_TAG:
2292 return new ViewContentNavigation(parcel);
Sunny Goyal56333a82017-08-29 13:46:29 -07002293 case SET_EMPTY_VIEW_ACTION_TAG:
2294 return new SetEmptyView(parcel);
2295 case SET_PENDING_INTENT_TEMPLATE_TAG:
2296 return new SetPendingIntentTemplate(parcel);
2297 case SET_ON_CLICK_FILL_IN_INTENT_TAG:
2298 return new SetOnClickFillInIntent(parcel);
2299 case SET_REMOTE_VIEW_ADAPTER_INTENT_TAG:
2300 return new SetRemoteViewsAdapterIntent(parcel);
2301 case TEXT_VIEW_DRAWABLE_ACTION_TAG:
2302 return new TextViewDrawableAction(parcel);
2303 case TEXT_VIEW_SIZE_ACTION_TAG:
2304 return new TextViewSizeAction(parcel);
2305 case VIEW_PADDING_ACTION_TAG:
2306 return new ViewPaddingAction(parcel);
2307 case BITMAP_REFLECTION_ACTION_TAG:
2308 return new BitmapReflectionAction(parcel);
2309 case SET_REMOTE_VIEW_ADAPTER_LIST_TAG:
2310 return new SetRemoteViewsAdapterList(parcel);
Sunny Goyal56333a82017-08-29 13:46:29 -07002311 case SET_REMOTE_INPUTS_ACTION_TAG:
2312 return new SetRemoteInputsAction(parcel);
2313 case LAYOUT_PARAM_ACTION_TAG:
2314 return new LayoutParamAction(parcel);
2315 case OVERRIDE_TEXT_COLORS_TAG:
2316 return new OverrideTextColorsAction(parcel);
2317 default:
2318 throw new ActionException("Tag " + tag + " not found");
2319 }
2320 };
2321
Alan Viverette0a14ba52017-06-14 16:54:05 -04002322 /**
2323 * Returns a deep copy of the RemoteViews object. The RemoteView may not be
2324 * attached to another RemoteView -- it must be the root of a hierarchy.
2325 *
Sunny Goyal56333a82017-08-29 13:46:29 -07002326 * @deprecated use {@link #RemoteViews(RemoteViews)} instead.
Alan Viverette0a14ba52017-06-14 16:54:05 -04002327 * @throws IllegalStateException if this is not the root of a RemoteView
2328 * hierarchy
2329 */
2330 @Override
Sunny Goyal56333a82017-08-29 13:46:29 -07002331 @Deprecated
Alan Viverette0a14ba52017-06-14 16:54:05 -04002332 public RemoteViews clone() {
Sunny Goyal56333a82017-08-29 13:46:29 -07002333 Preconditions.checkState(mIsRoot, "RemoteView has been attached to another RemoteView. "
2334 + "May only clone the root of a RemoteView hierarchy.");
Winson Chung3ec9a452010-09-23 16:40:28 -07002335
Sunny Goyal56333a82017-08-29 13:46:29 -07002336 return new RemoteViews(this);
Joe Onorato18e69df2010-05-17 22:26:12 -07002337 }
2338
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002339 public String getPackage() {
Svetoslavb6242442014-09-19 13:21:55 -07002340 return (mApplication != null) ? mApplication.packageName : null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002341 }
2342
Adam Cohen5d200642012-04-24 10:43:31 -07002343 /**
Adrian Roos7da889d2016-03-16 18:38:58 -07002344 * Returns the layout id of the root layout associated with this RemoteViews. In the case
Adam Cohen5d200642012-04-24 10:43:31 -07002345 * that the RemoteViews has both a landscape and portrait root, this will return the layout
2346 * id associated with the portrait layout.
2347 *
2348 * @return the layout id.
2349 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002350 public int getLayoutId() {
2351 return mLayoutId;
2352 }
2353
Winson Chung3ec9a452010-09-23 16:40:28 -07002354 /*
Adam Cohenca6fd842010-09-03 18:10:35 -07002355 * This flag indicates whether this RemoteViews object is being created from a
2356 * RemoteViewsService for use as a child of a widget collection. This flag is used
2357 * to determine whether or not certain features are available, in particular,
2358 * setting on click extras and setting on click pending intents. The former is enabled,
2359 * and the latter disabled when this flag is true.
2360 */
2361 void setIsWidgetCollectionChild(boolean isWidgetCollectionChild) {
2362 mIsWidgetCollectionChild = isWidgetCollectionChild;
2363 }
2364
2365 /**
Adam Cohen5d200642012-04-24 10:43:31 -07002366 * Recursively sets BitmapCache in the hierarchy and update the bitmap ids.
2367 */
2368 private void setBitmapCache(BitmapCache bitmapCache) {
2369 mBitmapCache = bitmapCache;
2370 if (!hasLandscapeAndPortraitLayouts()) {
2371 if (mActions != null) {
2372 final int count = mActions.size();
2373 for (int i= 0; i < count; ++i) {
2374 mActions.get(i).setBitmapCache(bitmapCache);
2375 }
2376 }
2377 } else {
2378 mLandscape.setBitmapCache(bitmapCache);
2379 mPortrait.setBitmapCache(bitmapCache);
Winson Chung3ec9a452010-09-23 16:40:28 -07002380 }
2381 }
2382
2383 /**
2384 * Returns an estimate of the bitmap heap memory usage for this RemoteViews.
2385 */
Adam Cohen311c79c2012-05-10 14:44:38 -07002386 /** @hide */
2387 public int estimateMemoryUsage() {
Sunny Goyal56333a82017-08-29 13:46:29 -07002388 return mBitmapCache.getBitmapMemory();
Winson Chung3ec9a452010-09-23 16:40:28 -07002389 }
2390
2391 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002392 * Add an action to be executed on the remote side when apply is called.
Jim Millere667a7a2012-08-09 19:22:32 -07002393 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002394 * @param a The action to add
2395 */
2396 private void addAction(Action a) {
Adam Cohen5d200642012-04-24 10:43:31 -07002397 if (hasLandscapeAndPortraitLayouts()) {
2398 throw new RuntimeException("RemoteViews specifying separate landscape and portrait" +
2399 " layouts cannot be modified. Instead, fully configure the landscape and" +
2400 " portrait layouts individually before constructing the combined layout.");
2401 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002402 if (mActions == null) {
Sunny Goyal56333a82017-08-29 13:46:29 -07002403 mActions = new ArrayList<>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002404 }
2405 mActions.add(a);
2406 }
Jeff Sharkey1162fd72009-11-04 17:58:08 -08002407
2408 /**
2409 * Equivalent to calling {@link ViewGroup#addView(View)} after inflating the
2410 * given {@link RemoteViews}. This allows users to build "nested"
2411 * {@link RemoteViews}. In cases where consumers of {@link RemoteViews} may
2412 * recycle layouts, use {@link #removeAllViews(int)} to clear any existing
2413 * children.
2414 *
2415 * @param viewId The id of the parent {@link ViewGroup} to add child into.
2416 * @param nestedView {@link RemoteViews} that describes the child.
2417 */
2418 public void addView(int viewId, RemoteViews nestedView) {
Anthony Chen8f5f3582017-04-11 11:18:37 -07002419 addAction(nestedView == null
2420 ? new ViewGroupActionRemove(viewId)
2421 : new ViewGroupActionAdd(viewId, nestedView));
2422 }
2423
2424 /**
2425 * Equivalent to calling {@link ViewGroup#addView(View, int)} after inflating the
2426 * given {@link RemoteViews}.
2427 *
2428 * @param viewId The id of the parent {@link ViewGroup} to add the child into.
Sunny Goyal271e3222017-08-29 16:05:47 -07002429 * @param nestedView {@link RemoteViews} of the child to add.
Anthony Chen8f5f3582017-04-11 11:18:37 -07002430 * @param index The position at which to add the child.
2431 *
2432 * @hide
2433 */
2434 public void addView(int viewId, RemoteViews nestedView, int index) {
2435 addAction(new ViewGroupActionAdd(viewId, nestedView, index));
Jeff Sharkey1162fd72009-11-04 17:58:08 -08002436 }
2437
2438 /**
2439 * Equivalent to calling {@link ViewGroup#removeAllViews()}.
2440 *
2441 * @param viewId The id of the parent {@link ViewGroup} to remove all
2442 * children from.
2443 */
2444 public void removeAllViews(int viewId) {
Anthony Chen8f5f3582017-04-11 11:18:37 -07002445 addAction(new ViewGroupActionRemove(viewId));
2446 }
2447
2448 /**
2449 * Removes all views in the {@link ViewGroup} specified by the {@code viewId} except for any
2450 * child that has the {@code viewIdToKeep} as its id.
2451 *
2452 * @param viewId The id of the parent {@link ViewGroup} to remove children from.
2453 * @param viewIdToKeep The id of a child that should not be removed.
2454 *
2455 * @hide
2456 */
2457 public void removeAllViewsExceptId(int viewId, int viewIdToKeep) {
2458 addAction(new ViewGroupActionRemove(viewId, viewIdToKeep));
Jeff Sharkey1162fd72009-11-04 17:58:08 -08002459 }
2460
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002461 /**
Adam Cohen0b96a572011-02-10 15:56:16 -08002462 * Equivalent to calling {@link AdapterViewAnimator#showNext()}
Adam Cohen2dd21972010-08-15 18:20:04 -07002463 *
Adam Cohen0b96a572011-02-10 15:56:16 -08002464 * @param viewId The id of the view on which to call {@link AdapterViewAnimator#showNext()}
Adam Cohen2dd21972010-08-15 18:20:04 -07002465 */
2466 public void showNext(int viewId) {
Sunny Goyal5b153922017-09-21 21:00:36 -07002467 addAction(new ViewContentNavigation(viewId, true /* next */));
Adam Cohen2dd21972010-08-15 18:20:04 -07002468 }
2469
2470 /**
Adam Cohen0b96a572011-02-10 15:56:16 -08002471 * Equivalent to calling {@link AdapterViewAnimator#showPrevious()}
Adam Cohen2dd21972010-08-15 18:20:04 -07002472 *
Adam Cohen0b96a572011-02-10 15:56:16 -08002473 * @param viewId The id of the view on which to call {@link AdapterViewAnimator#showPrevious()}
Adam Cohen2dd21972010-08-15 18:20:04 -07002474 */
2475 public void showPrevious(int viewId) {
Sunny Goyal5b153922017-09-21 21:00:36 -07002476 addAction(new ViewContentNavigation(viewId, false /* next */));
Adam Cohen2dd21972010-08-15 18:20:04 -07002477 }
2478
2479 /**
Adam Cohen0b96a572011-02-10 15:56:16 -08002480 * Equivalent to calling {@link AdapterViewAnimator#setDisplayedChild(int)}
2481 *
2482 * @param viewId The id of the view on which to call
2483 * {@link AdapterViewAnimator#setDisplayedChild(int)}
2484 */
2485 public void setDisplayedChild(int viewId, int childIndex) {
2486 setInt(viewId, "setDisplayedChild", childIndex);
2487 }
2488
2489 /**
Kirill Grouchnikov40a39782016-07-01 17:24:32 -04002490 * Equivalent to calling {@link View#setVisibility(int)}
Jim Millere667a7a2012-08-09 19:22:32 -07002491 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002492 * @param viewId The id of the view whose visibility should change
2493 * @param visibility The new visibility for the view
2494 */
2495 public void setViewVisibility(int viewId, int visibility) {
2496 setInt(viewId, "setVisibility", visibility);
2497 }
Adam Cohenca6fd842010-09-03 18:10:35 -07002498
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002499 /**
Kirill Grouchnikov40a39782016-07-01 17:24:32 -04002500 * Equivalent to calling {@link TextView#setText(CharSequence)}
Jim Millere667a7a2012-08-09 19:22:32 -07002501 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002502 * @param viewId The id of the view whose text should change
2503 * @param text The new text for the view
2504 */
2505 public void setTextViewText(int viewId, CharSequence text) {
2506 setCharSequence(viewId, "setText", text);
2507 }
Daniel Sandler7264f712012-05-21 14:48:23 -04002508
2509 /**
Daniel Sandler7264f712012-05-21 14:48:23 -04002510 * Equivalent to calling {@link TextView#setTextSize(int, float)}
Jim Millere667a7a2012-08-09 19:22:32 -07002511 *
Daniel Sandler7264f712012-05-21 14:48:23 -04002512 * @param viewId The id of the view whose text size should change
2513 * @param units The units of size (e.g. COMPLEX_UNIT_SP)
2514 * @param size The size of the text
2515 */
2516 public void setTextViewTextSize(int viewId, int units, float size) {
2517 addAction(new TextViewSizeAction(viewId, units, size));
2518 }
2519
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002520 /**
Jim Millere667a7a2012-08-09 19:22:32 -07002521 * Equivalent to calling
Daniel Sandler820ba322012-03-23 16:36:00 -05002522 * {@link TextView#setCompoundDrawablesWithIntrinsicBounds(int, int, int, int)}.
2523 *
2524 * @param viewId The id of the view whose text should change
2525 * @param left The id of a drawable to place to the left of the text, or 0
2526 * @param top The id of a drawable to place above the text, or 0
2527 * @param right The id of a drawable to place to the right of the text, or 0
Jim Millere667a7a2012-08-09 19:22:32 -07002528 * @param bottom The id of a drawable to place below the text, or 0
Daniel Sandler820ba322012-03-23 16:36:00 -05002529 */
2530 public void setTextViewCompoundDrawables(int viewId, int left, int top, int right, int bottom) {
2531 addAction(new TextViewDrawableAction(viewId, false, left, top, right, bottom));
2532 }
2533
2534 /**
Jim Millere667a7a2012-08-09 19:22:32 -07002535 * Equivalent to calling {@link
Daniel Sandler820ba322012-03-23 16:36:00 -05002536 * TextView#setCompoundDrawablesRelativeWithIntrinsicBounds(int, int, int, int)}.
2537 *
2538 * @param viewId The id of the view whose text should change
Jim Millere667a7a2012-08-09 19:22:32 -07002539 * @param start The id of a drawable to place before the text (relative to the
Daniel Sandler820ba322012-03-23 16:36:00 -05002540 * layout direction), or 0
2541 * @param top The id of a drawable to place above the text, or 0
2542 * @param end The id of a drawable to place after the text, or 0
Fabrice Di Meglio66388dc2012-05-03 18:51:57 -07002543 * @param bottom The id of a drawable to place below the text, or 0
Daniel Sandler820ba322012-03-23 16:36:00 -05002544 */
2545 public void setTextViewCompoundDrawablesRelative(int viewId, int start, int top, int end, int bottom) {
2546 addAction(new TextViewDrawableAction(viewId, true, start, top, end, bottom));
2547 }
2548
2549 /**
Dan Sandler912282e2015-07-28 22:49:30 -04002550 * Equivalent to calling {@link
2551 * TextView#setCompoundDrawablesWithIntrinsicBounds(Drawable, Drawable, Drawable, Drawable)}
2552 * using the drawables yielded by {@link Icon#loadDrawable(Context)}.
2553 *
2554 * @param viewId The id of the view whose text should change
2555 * @param left an Icon to place to the left of the text, or 0
2556 * @param top an Icon to place above the text, or 0
2557 * @param right an Icon to place to the right of the text, or 0
2558 * @param bottom an Icon to place below the text, or 0
2559 *
2560 * @hide
2561 */
2562 public void setTextViewCompoundDrawables(int viewId, Icon left, Icon top, Icon right, Icon bottom) {
2563 addAction(new TextViewDrawableAction(viewId, false, left, top, right, bottom));
2564 }
2565
2566 /**
2567 * Equivalent to calling {@link
2568 * TextView#setCompoundDrawablesRelativeWithIntrinsicBounds(Drawable, Drawable, Drawable, Drawable)}
2569 * using the drawables yielded by {@link Icon#loadDrawable(Context)}.
2570 *
2571 * @param viewId The id of the view whose text should change
2572 * @param start an Icon to place before the text (relative to the
2573 * layout direction), or 0
2574 * @param top an Icon to place above the text, or 0
2575 * @param end an Icon to place after the text, or 0
2576 * @param bottom an Icon to place below the text, or 0
2577 *
2578 * @hide
2579 */
2580 public void setTextViewCompoundDrawablesRelative(int viewId, Icon start, Icon top, Icon end, Icon bottom) {
2581 addAction(new TextViewDrawableAction(viewId, true, start, top, end, bottom));
2582 }
2583
2584 /**
Kirill Grouchnikov40a39782016-07-01 17:24:32 -04002585 * Equivalent to calling {@link ImageView#setImageResource(int)}
Jim Millere667a7a2012-08-09 19:22:32 -07002586 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002587 * @param viewId The id of the view whose drawable should change
2588 * @param srcId The new resource id for the drawable
2589 */
Jim Millere667a7a2012-08-09 19:22:32 -07002590 public void setImageViewResource(int viewId, int srcId) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002591 setInt(viewId, "setImageResource", srcId);
2592 }
2593
2594 /**
Kirill Grouchnikov40a39782016-07-01 17:24:32 -04002595 * Equivalent to calling {@link ImageView#setImageURI(Uri)}
Jim Millere667a7a2012-08-09 19:22:32 -07002596 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002597 * @param viewId The id of the view whose drawable should change
2598 * @param uri The Uri for the image
2599 */
2600 public void setImageViewUri(int viewId, Uri uri) {
2601 setUri(viewId, "setImageURI", uri);
2602 }
2603
2604 /**
Kirill Grouchnikov40a39782016-07-01 17:24:32 -04002605 * Equivalent to calling {@link ImageView#setImageBitmap(Bitmap)}
Jim Millere667a7a2012-08-09 19:22:32 -07002606 *
Scott Main93dc6422012-02-24 12:04:06 -08002607 * @param viewId The id of the view whose bitmap should change
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002608 * @param bitmap The new Bitmap for the drawable
2609 */
2610 public void setImageViewBitmap(int viewId, Bitmap bitmap) {
2611 setBitmap(viewId, "setImageBitmap", bitmap);
2612 }
2613
2614 /**
Kirill Grouchnikov40a39782016-07-01 17:24:32 -04002615 * Equivalent to calling {@link ImageView#setImageIcon(Icon)}
Dan Sandlera22a3802015-05-13 00:12:47 -04002616 *
2617 * @param viewId The id of the view whose bitmap should change
2618 * @param icon The new Icon for the ImageView
2619 */
2620 public void setImageViewIcon(int viewId, Icon icon) {
2621 setIcon(viewId, "setImageIcon", icon);
2622 }
2623
2624 /**
Kirill Grouchnikov40a39782016-07-01 17:24:32 -04002625 * Equivalent to calling {@link AdapterView#setEmptyView(View)}
Adam Cohen1480fdd2010-08-25 17:24:53 -07002626 *
2627 * @param viewId The id of the view on which to set the empty view
2628 * @param emptyViewId The view id of the empty view
2629 */
2630 public void setEmptyView(int viewId, int emptyViewId) {
2631 addAction(new SetEmptyView(viewId, emptyViewId));
2632 }
2633
2634 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002635 * Equivalent to calling {@link Chronometer#setBase Chronometer.setBase},
2636 * {@link Chronometer#setFormat Chronometer.setFormat},
2637 * and {@link Chronometer#start Chronometer.start()} or
2638 * {@link Chronometer#stop Chronometer.stop()}.
Jim Millere667a7a2012-08-09 19:22:32 -07002639 *
Scott Main93dc6422012-02-24 12:04:06 -08002640 * @param viewId The id of the {@link Chronometer} to change
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002641 * @param base The time at which the timer would have read 0:00. This
2642 * time should be based off of
2643 * {@link android.os.SystemClock#elapsedRealtime SystemClock.elapsedRealtime()}.
2644 * @param format The Chronometer format string, or null to
2645 * simply display the timer value.
2646 * @param started True if you want the clock to be started, false if not.
Selim Cinek81c23aa2016-02-25 16:23:13 -08002647 *
Selim Cinekc3b752e2016-04-20 16:13:59 -07002648 * @see #setChronometerCountDown(int, boolean)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002649 */
2650 public void setChronometer(int viewId, long base, String format, boolean started) {
2651 setLong(viewId, "setBase", base);
2652 setString(viewId, "setFormat", format);
2653 setBoolean(viewId, "setStarted", started);
2654 }
Jim Millere667a7a2012-08-09 19:22:32 -07002655
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002656 /**
Selim Cinek81c23aa2016-02-25 16:23:13 -08002657 * Equivalent to calling {@link Chronometer#setCountDown(boolean) Chronometer.setCountDown} on
2658 * the chronometer with the given viewId.
2659 *
2660 * @param viewId The id of the {@link Chronometer} to change
2661 * @param isCountDown True if you want the chronometer to count down to base instead of
2662 * counting up.
2663 */
Selim Cinekc3b752e2016-04-20 16:13:59 -07002664 public void setChronometerCountDown(int viewId, boolean isCountDown) {
Selim Cinek81c23aa2016-02-25 16:23:13 -08002665 setBoolean(viewId, "setCountDown", isCountDown);
2666 }
2667
2668 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002669 * Equivalent to calling {@link ProgressBar#setMax ProgressBar.setMax},
2670 * {@link ProgressBar#setProgress ProgressBar.setProgress}, and
2671 * {@link ProgressBar#setIndeterminate ProgressBar.setIndeterminate}
2672 *
2673 * If indeterminate is true, then the values for max and progress are ignored.
Jim Millere667a7a2012-08-09 19:22:32 -07002674 *
Scott Main93dc6422012-02-24 12:04:06 -08002675 * @param viewId The id of the {@link ProgressBar} to change
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002676 * @param max The 100% value for the progress bar
2677 * @param progress The current value of the progress bar.
Jim Millere667a7a2012-08-09 19:22:32 -07002678 * @param indeterminate True if the progress bar is indeterminate,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002679 * false if not.
2680 */
Jim Millere667a7a2012-08-09 19:22:32 -07002681 public void setProgressBar(int viewId, int max, int progress,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002682 boolean indeterminate) {
2683 setBoolean(viewId, "setIndeterminate", indeterminate);
2684 if (!indeterminate) {
2685 setInt(viewId, "setMax", max);
2686 setInt(viewId, "setProgress", progress);
2687 }
2688 }
Jim Millere667a7a2012-08-09 19:22:32 -07002689
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002690 /**
2691 * Equivalent to calling
2692 * {@link android.view.View#setOnClickListener(android.view.View.OnClickListener)}
Sunny Goyald7d0c692017-10-12 12:05:14 -07002693 * to launch the provided {@link PendingIntent}. The source bounds
2694 * ({@link Intent#getSourceBounds()}) of the intent will be set to the bounds of the clicked
2695 * view in screen space.
2696 * Note that any activity options associated with the pendingIntent may get overridden
2697 * before starting the intent.
Jim Millere667a7a2012-08-09 19:22:32 -07002698 *
Adam Cohen35ae9ca2010-09-20 15:20:41 -07002699 * When setting the on-click action of items within collections (eg. {@link ListView},
2700 * {@link StackView} etc.), this method will not work. Instead, use {@link
Kirill Grouchnikovc5b30102016-06-28 15:34:13 -04002701 * RemoteViews#setPendingIntentTemplate(int, PendingIntent)} in conjunction with
2702 * {@link RemoteViews#setOnClickFillInIntent(int, Intent)}.
Adam Cohen35ae9ca2010-09-20 15:20:41 -07002703 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002704 * @param viewId The id of the view that will trigger the {@link PendingIntent} when clicked
2705 * @param pendingIntent The {@link PendingIntent} to send when user clicks
2706 */
2707 public void setOnClickPendingIntent(int viewId, PendingIntent pendingIntent) {
2708 addAction(new SetOnClickPendingIntent(viewId, pendingIntent));
2709 }
2710
2711 /**
Adam Cohen35ae9ca2010-09-20 15:20:41 -07002712 * When using collections (eg. {@link ListView}, {@link StackView} etc.) in widgets, it is very
2713 * costly to set PendingIntents on the individual items, and is hence not permitted. Instead
2714 * this method should be used to set a single PendingIntent template on the collection, and
2715 * individual items can differentiate their on-click behavior using
2716 * {@link RemoteViews#setOnClickFillInIntent(int, Intent)}.
Adam Cohenca6fd842010-09-03 18:10:35 -07002717 *
2718 * @param viewId The id of the collection who's children will use this PendingIntent template
2719 * when clicked
2720 * @param pendingIntentTemplate The {@link PendingIntent} to be combined with extras specified
2721 * by a child of viewId and executed when that child is clicked
2722 */
2723 public void setPendingIntentTemplate(int viewId, PendingIntent pendingIntentTemplate) {
2724 addAction(new SetPendingIntentTemplate(viewId, pendingIntentTemplate));
2725 }
2726
2727 /**
Adam Cohen35ae9ca2010-09-20 15:20:41 -07002728 * When using collections (eg. {@link ListView}, {@link StackView} etc.) in widgets, it is very
2729 * costly to set PendingIntents on the individual items, and is hence not permitted. Instead
2730 * a single PendingIntent template can be set on the collection, see {@link
2731 * RemoteViews#setPendingIntentTemplate(int, PendingIntent)}, and the individual on-click
2732 * action of a given item can be distinguished by setting a fillInIntent on that item. The
2733 * fillInIntent is then combined with the PendingIntent template in order to determine the final
2734 * intent which will be executed when the item is clicked. This works as follows: any fields
2735 * which are left blank in the PendingIntent template, but are provided by the fillInIntent
Kirill Grouchnikovc5b30102016-06-28 15:34:13 -04002736 * will be overwritten, and the resulting PendingIntent will be used. The rest
Adam Cohen35ae9ca2010-09-20 15:20:41 -07002737 * of the PendingIntent template will then be filled in with the associated fields that are
2738 * set in fillInIntent. See {@link Intent#fillIn(Intent, int)} for more details.
2739 *
2740 * @param viewId The id of the view on which to set the fillInIntent
2741 * @param fillInIntent The intent which will be combined with the parent's PendingIntent
2742 * in order to determine the on-click behavior of the view specified by viewId
2743 */
2744 public void setOnClickFillInIntent(int viewId, Intent fillInIntent) {
2745 addAction(new SetOnClickFillInIntent(viewId, fillInIntent));
2746 }
2747
2748 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002749 * @hide
Sunny Goyal5b153922017-09-21 21:00:36 -07002750 * Equivalent to calling
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002751 * {@link Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)},
Sunny Goyal5b153922017-09-21 21:00:36 -07002752 * on the {@link Drawable} of a given view.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002753 * <p>
Jim Millere667a7a2012-08-09 19:22:32 -07002754 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002755 * @param viewId The id of the view that contains the target
2756 * {@link Drawable}
2757 * @param targetBackground If true, apply these parameters to the
2758 * {@link Drawable} returned by
2759 * {@link android.view.View#getBackground()}. Otherwise, assume
2760 * the target view is an {@link ImageView} and apply them to
2761 * {@link ImageView#getDrawable()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002762 * @param colorFilter Specify a color for a
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02002763 * {@link android.graphics.ColorFilter} for this drawable. This will be ignored if
2764 * {@code mode} is {@code null}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002765 * @param mode Specify a PorterDuff mode for this drawable, or null to leave
2766 * unchanged.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002767 */
Sunny Goyal5b153922017-09-21 21:00:36 -07002768 public void setDrawableTint(int viewId, boolean targetBackground,
2769 int colorFilter, @NonNull PorterDuff.Mode mode) {
2770 addAction(new SetDrawableTint(viewId, targetBackground, colorFilter, mode));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002771 }
2772
2773 /**
Jorim Jaggief72a192014-08-26 21:57:46 +02002774 * @hide
2775 * Equivalent to calling {@link android.widget.ProgressBar#setProgressTintList}.
2776 *
2777 * @param viewId The id of the view whose tint should change
2778 * @param tint the tint to apply, may be {@code null} to clear tint
2779 */
2780 public void setProgressTintList(int viewId, ColorStateList tint) {
2781 addAction(new ReflectionAction(viewId, "setProgressTintList",
2782 ReflectionAction.COLOR_STATE_LIST, tint));
2783 }
2784
2785 /**
2786 * @hide
2787 * Equivalent to calling {@link android.widget.ProgressBar#setProgressBackgroundTintList}.
2788 *
2789 * @param viewId The id of the view whose tint should change
2790 * @param tint the tint to apply, may be {@code null} to clear tint
2791 */
2792 public void setProgressBackgroundTintList(int viewId, ColorStateList tint) {
2793 addAction(new ReflectionAction(viewId, "setProgressBackgroundTintList",
2794 ReflectionAction.COLOR_STATE_LIST, tint));
2795 }
2796
2797 /**
2798 * @hide
2799 * Equivalent to calling {@link android.widget.ProgressBar#setIndeterminateTintList}.
2800 *
2801 * @param viewId The id of the view whose tint should change
2802 * @param tint the tint to apply, may be {@code null} to clear tint
2803 */
2804 public void setProgressIndeterminateTintList(int viewId, ColorStateList tint) {
2805 addAction(new ReflectionAction(viewId, "setIndeterminateTintList",
2806 ReflectionAction.COLOR_STATE_LIST, tint));
2807 }
2808
2809 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002810 * Equivalent to calling {@link android.widget.TextView#setTextColor(int)}.
Jim Millere667a7a2012-08-09 19:22:32 -07002811 *
Scott Main93dc6422012-02-24 12:04:06 -08002812 * @param viewId The id of the view whose text color should change
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002813 * @param color Sets the text color for all the states (normal, selected,
2814 * focused) to be this color.
2815 */
Tor Norbye80756e32015-03-02 09:39:27 -08002816 public void setTextColor(int viewId, @ColorInt int color) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002817 setInt(viewId, "setTextColor", color);
2818 }
2819
Joe Onorato592d0652009-03-24 22:25:52 -07002820 /**
Selim Cinek981962e2016-07-20 20:41:58 -07002821 * @hide
2822 * Equivalent to calling {@link android.widget.TextView#setTextColor(ColorStateList)}.
2823 *
2824 * @param viewId The id of the view whose text color should change
2825 * @param colors the text colors to set
2826 */
2827 public void setTextColor(int viewId, @ColorInt ColorStateList colors) {
2828 addAction(new ReflectionAction(viewId, "setTextColor", ReflectionAction.COLOR_STATE_LIST,
2829 colors));
2830 }
2831
2832 /**
Adam Cohen3b4ca102012-12-14 12:00:41 -08002833 * Equivalent to calling {@link android.widget.AbsListView#setRemoteViewsAdapter(Intent)}.
Winson Chung499cb9f2010-07-16 11:18:17 -07002834 *
Winson Chung037300b2011-03-29 15:40:16 -07002835 * @param appWidgetId The id of the app widget which contains the specified view. (This
2836 * parameter is ignored in this deprecated method)
Adam Cohen50f3d1b2012-12-11 18:36:07 -08002837 * @param viewId The id of the {@link AdapterView}
Winson Chung037300b2011-03-29 15:40:16 -07002838 * @param intent The intent of the service which will be
2839 * providing data to the RemoteViewsAdapter
2840 * @deprecated This method has been deprecated. See
2841 * {@link android.widget.RemoteViews#setRemoteAdapter(int, Intent)}
2842 */
2843 @Deprecated
2844 public void setRemoteAdapter(int appWidgetId, int viewId, Intent intent) {
2845 setRemoteAdapter(viewId, intent);
2846 }
2847
2848 /**
2849 * Equivalent to calling {@link android.widget.AbsListView#setRemoteViewsAdapter(Intent)}.
2850 * Can only be used for App Widgets.
2851 *
Adam Cohen50f3d1b2012-12-11 18:36:07 -08002852 * @param viewId The id of the {@link AdapterView}
Winson Chung81f39eb2011-01-11 18:05:01 -08002853 * @param intent The intent of the service which will be
2854 * providing data to the RemoteViewsAdapter
2855 */
Winson Chung037300b2011-03-29 15:40:16 -07002856 public void setRemoteAdapter(int viewId, Intent intent) {
2857 addAction(new SetRemoteViewsAdapterIntent(viewId, intent));
Winson Chung499cb9f2010-07-16 11:18:17 -07002858 }
2859
2860 /**
Adam Cohen50f3d1b2012-12-11 18:36:07 -08002861 * Creates a simple Adapter for the viewId specified. The viewId must point to an AdapterView,
2862 * ie. {@link ListView}, {@link GridView}, {@link StackView} or {@link AdapterViewAnimator}.
2863 * This is a simpler but less flexible approach to populating collection widgets. Its use is
2864 * encouraged for most scenarios, as long as the total memory within the list of RemoteViews
2865 * is relatively small (ie. doesn't contain large or numerous Bitmaps, see {@link
2866 * RemoteViews#setImageViewBitmap}). In the case of numerous images, the use of API is still
2867 * possible by setting image URIs instead of Bitmaps, see {@link RemoteViews#setImageViewUri}.
2868 *
2869 * This API is supported in the compatibility library for previous API levels, see
2870 * RemoteViewsCompat.
2871 *
2872 * @param viewId The id of the {@link AdapterView}
2873 * @param list The list of RemoteViews which will populate the view specified by viewId.
Adam Cohenb00d9f02013-01-10 14:12:52 -08002874 * @param viewTypeCount The maximum number of unique layout id's used to construct the list of
2875 * RemoteViews. This count cannot change during the life-cycle of a given widget, so this
2876 * parameter should account for the maximum possible number of types that may appear in the
2877 * See {@link Adapter#getViewTypeCount()}.
Adam Cohen33f3aab2013-04-17 13:48:17 -07002878 *
2879 * @hide
Adam Cohen50f3d1b2012-12-11 18:36:07 -08002880 */
Adam Cohenb00d9f02013-01-10 14:12:52 -08002881 public void setRemoteAdapter(int viewId, ArrayList<RemoteViews> list, int viewTypeCount) {
2882 addAction(new SetRemoteViewsAdapterList(viewId, list, viewTypeCount));
Adam Cohen50f3d1b2012-12-11 18:36:07 -08002883 }
2884
2885 /**
Kirill Grouchnikov40a39782016-07-01 17:24:32 -04002886 * Equivalent to calling {@link ListView#smoothScrollToPosition(int)}.
Winson Chung499cb9f2010-07-16 11:18:17 -07002887 *
Scott Main93dc6422012-02-24 12:04:06 -08002888 * @param viewId The id of the view to change
Winson Chung499cb9f2010-07-16 11:18:17 -07002889 * @param position Scroll to this adapter position
2890 */
2891 public void setScrollPosition(int viewId, int position) {
2892 setInt(viewId, "smoothScrollToPosition", position);
2893 }
2894
2895 /**
Kirill Grouchnikov40a39782016-07-01 17:24:32 -04002896 * Equivalent to calling {@link ListView#smoothScrollByOffset(int)}.
Winson Chung499cb9f2010-07-16 11:18:17 -07002897 *
Scott Main93dc6422012-02-24 12:04:06 -08002898 * @param viewId The id of the view to change
Winson Chung95362592010-07-19 16:05:50 -07002899 * @param offset Scroll by this adapter position offset
Winson Chung499cb9f2010-07-16 11:18:17 -07002900 */
2901 public void setRelativeScrollPosition(int viewId, int offset) {
2902 setInt(viewId, "smoothScrollByOffset", offset);
2903 }
2904
2905 /**
Daniel Sandlerd5353b42012-06-21 09:28:07 -04002906 * Equivalent to calling {@link android.view.View#setPadding(int, int, int, int)}.
Daniel Sandler99d1f742012-05-21 16:14:14 -04002907 *
2908 * @param viewId The id of the view to change
2909 * @param left the left padding in pixels
2910 * @param top the top padding in pixels
2911 * @param right the right padding in pixels
2912 * @param bottom the bottom padding in pixels
2913 */
2914 public void setViewPadding(int viewId, int left, int top, int right, int bottom) {
2915 addAction(new ViewPaddingAction(viewId, left, top, right, bottom));
2916 }
2917
2918 /**
Selim Cinek65b2e7c2015-10-26 14:11:31 -07002919 * @hide
2920 * Equivalent to calling {@link android.view.ViewGroup.MarginLayoutParams#setMarginEnd(int)}.
2921 * Only works if the {@link View#getLayoutParams()} supports margins.
2922 * Hidden for now since we don't want to support this for all different layout margins yet.
2923 *
2924 * @param viewId The id of the view to change
Adrian Roos2d5dbba2016-06-08 17:11:53 -07002925 * @param endMarginDimen a dimen resource to read the margin from or 0 to clear the margin.
Selim Cinek65b2e7c2015-10-26 14:11:31 -07002926 */
Adrian Roos2d5dbba2016-06-08 17:11:53 -07002927 public void setViewLayoutMarginEndDimen(int viewId, @DimenRes int endMarginDimen) {
2928 addAction(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_MARGIN_END_DIMEN,
2929 endMarginDimen));
Adrian Roos9b123cf2016-02-04 14:55:57 -08002930 }
2931
2932 /**
Adrian Roosc1a80b02016-04-05 14:54:55 -07002933 * Equivalent to setting {@link android.view.ViewGroup.MarginLayoutParams#bottomMargin}.
2934 *
Adrian Roos2d5dbba2016-06-08 17:11:53 -07002935 * @param bottomMarginDimen a dimen resource to read the margin from or 0 to clear the margin.
Adrian Roosc1a80b02016-04-05 14:54:55 -07002936 * @hide
2937 */
Adrian Roos2d5dbba2016-06-08 17:11:53 -07002938 public void setViewLayoutMarginBottomDimen(int viewId, @DimenRes int bottomMarginDimen) {
2939 addAction(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_MARGIN_BOTTOM_DIMEN,
2940 bottomMarginDimen));
Adrian Roosc1a80b02016-04-05 14:54:55 -07002941 }
2942
2943 /**
Adrian Roos9b123cf2016-02-04 14:55:57 -08002944 * Equivalent to setting {@link android.view.ViewGroup.LayoutParams#width}.
Adrian Roos2d5dbba2016-06-08 17:11:53 -07002945 *
2946 * @param layoutWidth one of 0, MATCH_PARENT or WRAP_CONTENT. Other sizes are not allowed
2947 * because they behave poorly when the density changes.
Adrian Roos9b123cf2016-02-04 14:55:57 -08002948 * @hide
2949 */
2950 public void setViewLayoutWidth(int viewId, int layoutWidth) {
Adrian Roos2d5dbba2016-06-08 17:11:53 -07002951 if (layoutWidth != 0 && layoutWidth != ViewGroup.LayoutParams.MATCH_PARENT
2952 && layoutWidth != ViewGroup.LayoutParams.WRAP_CONTENT) {
2953 throw new IllegalArgumentException("Only supports 0, WRAP_CONTENT and MATCH_PARENT");
2954 }
Adrian Roos9b123cf2016-02-04 14:55:57 -08002955 mActions.add(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_WIDTH, layoutWidth));
Selim Cinek65b2e7c2015-10-26 14:11:31 -07002956 }
2957
2958 /**
Joe Onorato592d0652009-03-24 22:25:52 -07002959 * Call a method taking one boolean on a view in the layout for this RemoteViews.
2960 *
Scott Main93dc6422012-02-24 12:04:06 -08002961 * @param viewId The id of the view on which to call the method.
Joe Onorato592d0652009-03-24 22:25:52 -07002962 * @param methodName The name of the method to call.
2963 * @param value The value to pass to the method.
2964 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002965 public void setBoolean(int viewId, String methodName, boolean value) {
2966 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.BOOLEAN, value));
2967 }
2968
Joe Onorato592d0652009-03-24 22:25:52 -07002969 /**
2970 * Call a method taking one byte on a view in the layout for this RemoteViews.
2971 *
Scott Main93dc6422012-02-24 12:04:06 -08002972 * @param viewId The id of the view on which to call the method.
Joe Onorato592d0652009-03-24 22:25:52 -07002973 * @param methodName The name of the method to call.
2974 * @param value The value to pass to the method.
2975 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002976 public void setByte(int viewId, String methodName, byte value) {
2977 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.BYTE, value));
2978 }
2979
Joe Onorato592d0652009-03-24 22:25:52 -07002980 /**
2981 * Call a method taking one short on a view in the layout for this RemoteViews.
2982 *
Scott Main93dc6422012-02-24 12:04:06 -08002983 * @param viewId The id of the view on which to call the method.
Joe Onorato592d0652009-03-24 22:25:52 -07002984 * @param methodName The name of the method to call.
2985 * @param value The value to pass to the method.
2986 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002987 public void setShort(int viewId, String methodName, short value) {
2988 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.SHORT, value));
2989 }
2990
Joe Onorato592d0652009-03-24 22:25:52 -07002991 /**
2992 * Call a method taking one int on a view in the layout for this RemoteViews.
2993 *
Scott Main93dc6422012-02-24 12:04:06 -08002994 * @param viewId The id of the view on which to call the method.
Joe Onorato592d0652009-03-24 22:25:52 -07002995 * @param methodName The name of the method to call.
2996 * @param value The value to pass to the method.
2997 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002998 public void setInt(int viewId, String methodName, int value) {
2999 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.INT, value));
3000 }
3001
Joe Onorato592d0652009-03-24 22:25:52 -07003002 /**
3003 * Call a method taking one long on a view in the layout for this RemoteViews.
3004 *
Scott Main93dc6422012-02-24 12:04:06 -08003005 * @param viewId The id of the view on which to call the method.
Joe Onorato592d0652009-03-24 22:25:52 -07003006 * @param methodName The name of the method to call.
3007 * @param value The value to pass to the method.
3008 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003009 public void setLong(int viewId, String methodName, long value) {
3010 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.LONG, value));
3011 }
3012
Joe Onorato592d0652009-03-24 22:25:52 -07003013 /**
3014 * Call a method taking one float on a view in the layout for this RemoteViews.
3015 *
Scott Main93dc6422012-02-24 12:04:06 -08003016 * @param viewId The id of the view on which to call the method.
Joe Onorato592d0652009-03-24 22:25:52 -07003017 * @param methodName The name of the method to call.
3018 * @param value The value to pass to the method.
3019 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003020 public void setFloat(int viewId, String methodName, float value) {
3021 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.FLOAT, value));
3022 }
3023
Joe Onorato592d0652009-03-24 22:25:52 -07003024 /**
3025 * Call a method taking one double on a view in the layout for this RemoteViews.
3026 *
Scott Main93dc6422012-02-24 12:04:06 -08003027 * @param viewId The id of the view on which to call the method.
Joe Onorato592d0652009-03-24 22:25:52 -07003028 * @param methodName The name of the method to call.
3029 * @param value The value to pass to the method.
3030 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003031 public void setDouble(int viewId, String methodName, double value) {
3032 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.DOUBLE, value));
3033 }
3034
Joe Onorato592d0652009-03-24 22:25:52 -07003035 /**
3036 * Call a method taking one char on a view in the layout for this RemoteViews.
3037 *
Scott Main93dc6422012-02-24 12:04:06 -08003038 * @param viewId The id of the view on which to call the method.
Joe Onorato592d0652009-03-24 22:25:52 -07003039 * @param methodName The name of the method to call.
3040 * @param value The value to pass to the method.
3041 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003042 public void setChar(int viewId, String methodName, char value) {
3043 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.CHAR, value));
3044 }
3045
Joe Onorato592d0652009-03-24 22:25:52 -07003046 /**
3047 * Call a method taking one String on a view in the layout for this RemoteViews.
3048 *
Scott Main93dc6422012-02-24 12:04:06 -08003049 * @param viewId The id of the view on which to call the method.
Joe Onorato592d0652009-03-24 22:25:52 -07003050 * @param methodName The name of the method to call.
3051 * @param value The value to pass to the method.
3052 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003053 public void setString(int viewId, String methodName, String value) {
3054 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.STRING, value));
3055 }
3056
Joe Onorato592d0652009-03-24 22:25:52 -07003057 /**
3058 * Call a method taking one CharSequence on a view in the layout for this RemoteViews.
3059 *
Scott Main93dc6422012-02-24 12:04:06 -08003060 * @param viewId The id of the view on which to call the method.
Joe Onorato592d0652009-03-24 22:25:52 -07003061 * @param methodName The name of the method to call.
3062 * @param value The value to pass to the method.
3063 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003064 public void setCharSequence(int viewId, String methodName, CharSequence value) {
3065 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.CHAR_SEQUENCE, value));
3066 }
3067
Joe Onorato592d0652009-03-24 22:25:52 -07003068 /**
3069 * Call a method taking one Uri on a view in the layout for this RemoteViews.
3070 *
Scott Main93dc6422012-02-24 12:04:06 -08003071 * @param viewId The id of the view on which to call the method.
Joe Onorato592d0652009-03-24 22:25:52 -07003072 * @param methodName The name of the method to call.
3073 * @param value The value to pass to the method.
3074 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003075 public void setUri(int viewId, String methodName, Uri value) {
Jeff Sharkeya14acd22013-04-02 18:27:45 -07003076 if (value != null) {
3077 // Resolve any filesystem path before sending remotely
3078 value = value.getCanonicalUri();
3079 if (StrictMode.vmFileUriExposureEnabled()) {
3080 value.checkFileUriExposed("RemoteViews.setUri()");
3081 }
3082 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003083 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.URI, value));
3084 }
3085
Joe Onorato592d0652009-03-24 22:25:52 -07003086 /**
3087 * Call a method taking one Bitmap on a view in the layout for this RemoteViews.
3088 * @more
3089 * <p class="note">The bitmap will be flattened into the parcel if this object is
3090 * sent across processes, so it may end up using a lot of memory, and may be fairly slow.</p>
3091 *
Scott Main93dc6422012-02-24 12:04:06 -08003092 * @param viewId The id of the view on which to call the method.
Joe Onorato592d0652009-03-24 22:25:52 -07003093 * @param methodName The name of the method to call.
3094 * @param value The value to pass to the method.
3095 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003096 public void setBitmap(int viewId, String methodName, Bitmap value) {
Adam Cohen5d200642012-04-24 10:43:31 -07003097 addAction(new BitmapReflectionAction(viewId, methodName, value));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003098 }
3099
3100 /**
Bjorn Bringertd755b062010-01-06 17:15:37 +00003101 * Call a method taking one Bundle on a view in the layout for this RemoteViews.
3102 *
Scott Main93dc6422012-02-24 12:04:06 -08003103 * @param viewId The id of the view on which to call the method.
Bjorn Bringertd755b062010-01-06 17:15:37 +00003104 * @param methodName The name of the method to call.
3105 * @param value The value to pass to the method.
3106 */
3107 public void setBundle(int viewId, String methodName, Bundle value) {
3108 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.BUNDLE, value));
3109 }
3110
3111 /**
Scott Main93dc6422012-02-24 12:04:06 -08003112 * Call a method taking one Intent on a view in the layout for this RemoteViews.
Winson Chung499cb9f2010-07-16 11:18:17 -07003113 *
Scott Main93dc6422012-02-24 12:04:06 -08003114 * @param viewId The id of the view on which to call the method.
3115 * @param methodName The name of the method to call.
3116 * @param value The {@link android.content.Intent} to pass the method.
Winson Chung499cb9f2010-07-16 11:18:17 -07003117 */
3118 public void setIntent(int viewId, String methodName, Intent value) {
3119 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.INTENT, value));
3120 }
3121
3122 /**
Dan Sandlera22a3802015-05-13 00:12:47 -04003123 * Call a method taking one Icon on a view in the layout for this RemoteViews.
3124 *
3125 * @param viewId The id of the view on which to call the method.
3126 * @param methodName The name of the method to call.
3127 * @param value The {@link android.graphics.drawable.Icon} to pass the method.
3128 */
3129 public void setIcon(int viewId, String methodName, Icon value) {
3130 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.ICON, value));
3131 }
3132
3133 /**
Svetoslav Ganov33aef982012-09-13 12:49:03 -07003134 * Equivalent to calling View.setContentDescription(CharSequence).
Svetoslav Ganove261e282011-10-18 17:47:04 -07003135 *
Svetoslav Ganov33aef982012-09-13 12:49:03 -07003136 * @param viewId The id of the view whose content description should change.
3137 * @param contentDescription The new content description for the view.
Svetoslav Ganove261e282011-10-18 17:47:04 -07003138 */
3139 public void setContentDescription(int viewId, CharSequence contentDescription) {
3140 setCharSequence(viewId, "setContentDescription", contentDescription);
3141 }
3142
Svetoslav Ganov33aef982012-09-13 12:49:03 -07003143 /**
Svetoslav6c702902014-10-09 18:40:56 -07003144 * Equivalent to calling {@link android.view.View#setAccessibilityTraversalBefore(int)}.
3145 *
3146 * @param viewId The id of the view whose before view in accessibility traversal to set.
3147 * @param nextId The id of the next in the accessibility traversal.
3148 **/
3149 public void setAccessibilityTraversalBefore(int viewId, int nextId) {
3150 setInt(viewId, "setAccessibilityTraversalBefore", nextId);
3151 }
3152
3153 /**
3154 * Equivalent to calling {@link android.view.View#setAccessibilityTraversalAfter(int)}.
3155 *
3156 * @param viewId The id of the view whose after view in accessibility traversal to set.
3157 * @param nextId The id of the next in the accessibility traversal.
3158 **/
3159 public void setAccessibilityTraversalAfter(int viewId, int nextId) {
3160 setInt(viewId, "setAccessibilityTraversalAfter", nextId);
3161 }
3162
3163 /**
Kirill Grouchnikov40a39782016-07-01 17:24:32 -04003164 * Equivalent to calling {@link View#setLabelFor(int)}.
Svetoslav Ganov33aef982012-09-13 12:49:03 -07003165 *
3166 * @param viewId The id of the view whose property to set.
3167 * @param labeledId The id of a view for which this view serves as a label.
3168 */
3169 public void setLabelFor(int viewId, int labeledId) {
3170 setInt(viewId, "setLabelFor", labeledId);
3171 }
3172
Adam Cohen5d200642012-04-24 10:43:31 -07003173 private RemoteViews getRemoteViewsToApply(Context context) {
3174 if (hasLandscapeAndPortraitLayouts()) {
3175 int orientation = context.getResources().getConfiguration().orientation;
3176 if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
3177 return mLandscape;
3178 } else {
3179 return mPortrait;
3180 }
3181 }
3182 return this;
3183 }
3184
Svetoslav Ganove261e282011-10-18 17:47:04 -07003185 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003186 * Inflates the view hierarchy represented by this object and applies
3187 * all of the actions.
Jim Millere667a7a2012-08-09 19:22:32 -07003188 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003189 * <p><strong>Caller beware: this may throw</strong>
Jim Millere667a7a2012-08-09 19:22:32 -07003190 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003191 * @param context Default context to use
3192 * @param parent Parent that the resulting view hierarchy will be attached to. This method
3193 * does <strong>not</strong> attach the hierarchy. The caller should do so when appropriate.
3194 * @return The inflated view hierarchy
3195 */
3196 public View apply(Context context, ViewGroup parent) {
Jim Millere667a7a2012-08-09 19:22:32 -07003197 return apply(context, parent, null);
Dianne Hackborn1927ae82012-06-22 15:21:36 -07003198 }
3199
Dianne Hackborna1940212012-06-28 16:07:22 -07003200 /** @hide */
3201 public View apply(Context context, ViewGroup parent, OnClickHandler handler) {
Adam Cohen5d200642012-04-24 10:43:31 -07003202 RemoteViews rvToApply = getRemoteViewsToApply(context);
3203
Sunny Goyaldd292f42015-12-02 14:29:27 -08003204 View result = inflateView(context, rvToApply, parent);
3205 loadTransitionOverride(context, handler);
3206
3207 rvToApply.performApply(result, parent, handler);
3208
3209 return result;
3210 }
3211
3212 private View inflateView(Context context, RemoteViews rv, ViewGroup parent) {
Kenny Guy77320062014-08-27 21:37:15 +01003213 // RemoteViews may be built by an application installed in another
3214 // user. So build a context that loads resources from that user but
3215 // still returns the current users userId so settings like data / time formats
3216 // are loaded without requiring cross user persmissions.
3217 final Context contextForResources = getContextForResources(context);
Adrian Roos83fad0a2017-04-24 16:58:26 -07003218 Context inflationContext = new RemoteViewsContextWrapper(context, contextForResources);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003219
Romain Guya5475592009-07-01 17:20:08 -07003220 LayoutInflater inflater = (LayoutInflater)
Kenny Guy77320062014-08-27 21:37:15 +01003221 context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003222
Kenny Guy77320062014-08-27 21:37:15 +01003223 // Clone inflater so we load resources from correct context and
3224 // we don't add a filter to the static version returned by getSystemService.
3225 inflater = inflater.cloneInContext(inflationContext);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003226 inflater.setFilter(this);
Sunny Goyal49e66952016-05-17 11:57:53 -07003227 View v = inflater.inflate(rv.getLayoutId(), parent, false);
3228 v.setTagInternal(R.id.widget_frame, rv.getLayoutId());
3229 return v;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003230 }
Adam Cohen5d200642012-04-24 10:43:31 -07003231
Gus Prevas1ed322b2015-09-17 17:34:46 -04003232 private static void loadTransitionOverride(Context context,
3233 RemoteViews.OnClickHandler handler) {
3234 if (handler != null && context.getResources().getBoolean(
3235 com.android.internal.R.bool.config_overrideRemoteViewsActivityTransition)) {
3236 TypedArray windowStyle = context.getTheme().obtainStyledAttributes(
3237 com.android.internal.R.styleable.Window);
3238 int windowAnimations = windowStyle.getResourceId(
3239 com.android.internal.R.styleable.Window_windowAnimationStyle, 0);
3240 TypedArray windowAnimationStyle = context.obtainStyledAttributes(
3241 windowAnimations, com.android.internal.R.styleable.WindowAnimation);
3242 handler.setEnterAnimationId(windowAnimationStyle.getResourceId(
3243 com.android.internal.R.styleable.
3244 WindowAnimation_activityOpenRemoteViewsEnterAnimation, 0));
3245 windowStyle.recycle();
3246 windowAnimationStyle.recycle();
3247 }
3248 }
3249
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003250 /**
Sunny Goyaldd292f42015-12-02 14:29:27 -08003251 * Implement this interface to receive a callback when
3252 * {@link #applyAsync} or {@link #reapplyAsync} is finished.
3253 * @hide
3254 */
3255 public interface OnViewAppliedListener {
3256 void onViewApplied(View v);
3257
3258 void onError(Exception e);
3259 }
3260
3261 /**
3262 * Applies the views asynchronously, moving as much of the task on the background
3263 * thread as possible.
3264 *
Elliot Waite54de7742017-01-11 15:30:35 -08003265 * @see #apply(Context, ViewGroup)
Sunny Goyaldd292f42015-12-02 14:29:27 -08003266 * @param context Default context to use
3267 * @param parent Parent that the resulting view hierarchy will be attached to. This method
3268 * does <strong>not</strong> attach the hierarchy. The caller should do so when appropriate.
3269 * @param listener the callback to run when all actions have been applied. May be null.
3270 * @param executor The executor to use. If null {@link AsyncTask#THREAD_POOL_EXECUTOR} is used.
3271 * @return CancellationSignal
3272 * @hide
3273 */
3274 public CancellationSignal applyAsync(
3275 Context context, ViewGroup parent, Executor executor, OnViewAppliedListener listener) {
3276 return applyAsync(context, parent, executor, listener, null);
3277 }
3278
3279 private CancellationSignal startTaskOnExecutor(AsyncApplyTask task, Executor executor) {
3280 CancellationSignal cancelSignal = new CancellationSignal();
3281 cancelSignal.setOnCancelListener(task);
3282
3283 task.executeOnExecutor(executor == null ? AsyncTask.THREAD_POOL_EXECUTOR : executor);
3284 return cancelSignal;
3285 }
3286
3287 /** @hide */
3288 public CancellationSignal applyAsync(Context context, ViewGroup parent,
3289 Executor executor, OnViewAppliedListener listener, OnClickHandler handler) {
3290 return startTaskOnExecutor(getAsyncApplyTask(context, parent, listener, handler), executor);
3291 }
3292
3293 private AsyncApplyTask getAsyncApplyTask(Context context, ViewGroup parent,
3294 OnViewAppliedListener listener, OnClickHandler handler) {
3295 return new AsyncApplyTask(getRemoteViewsToApply(context), parent, context, listener,
3296 handler, null);
3297 }
3298
3299 private class AsyncApplyTask extends AsyncTask<Void, Void, ViewTree>
3300 implements CancellationSignal.OnCancelListener {
3301 final RemoteViews mRV;
3302 final ViewGroup mParent;
3303 final Context mContext;
3304 final OnViewAppliedListener mListener;
3305 final OnClickHandler mHandler;
3306
3307 private View mResult;
3308 private ViewTree mTree;
3309 private Action[] mActions;
3310 private Exception mError;
3311
3312 private AsyncApplyTask(
3313 RemoteViews rv, ViewGroup parent, Context context, OnViewAppliedListener listener,
3314 OnClickHandler handler, View result) {
3315 mRV = rv;
3316 mParent = parent;
3317 mContext = context;
3318 mListener = listener;
3319 mHandler = handler;
3320
3321 mResult = result;
3322 loadTransitionOverride(context, handler);
3323 }
3324
3325 @Override
3326 protected ViewTree doInBackground(Void... params) {
3327 try {
3328 if (mResult == null) {
3329 mResult = inflateView(mContext, mRV, mParent);
3330 }
3331
3332 mTree = new ViewTree(mResult);
3333 if (mRV.mActions != null) {
3334 int count = mRV.mActions.size();
3335 mActions = new Action[count];
3336 for (int i = 0; i < count && !isCancelled(); i++) {
Sunny Goyal7b0e2c72016-11-03 14:48:05 -07003337 // TODO: check if isCancelled in nested views.
Sunny Goyaldd292f42015-12-02 14:29:27 -08003338 mActions[i] = mRV.mActions.get(i).initActionAsync(mTree, mParent, mHandler);
3339 }
3340 } else {
3341 mActions = null;
3342 }
3343 return mTree;
3344 } catch (Exception e) {
3345 mError = e;
3346 return null;
3347 }
3348 }
3349
3350 @Override
3351 protected void onPostExecute(ViewTree viewTree) {
3352 if (mError == null) {
3353 try {
3354 if (mActions != null) {
3355 OnClickHandler handler = mHandler == null
3356 ? DEFAULT_ON_CLICK_HANDLER : mHandler;
3357 for (Action a : mActions) {
3358 a.apply(viewTree.mRoot, mParent, handler);
3359 }
3360 }
3361 } catch (Exception e) {
3362 mError = e;
3363 }
3364 }
3365
3366 if (mListener != null) {
3367 if (mError != null) {
3368 mListener.onError(mError);
3369 } else {
3370 mListener.onViewApplied(viewTree.mRoot);
3371 }
3372 } else if (mError != null) {
3373 if (mError instanceof ActionException) {
3374 throw (ActionException) mError;
3375 } else {
3376 throw new ActionException(mError);
3377 }
3378 }
3379 }
3380
3381 @Override
3382 public void onCancel() {
3383 cancel(true);
3384 }
3385 }
3386
3387 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003388 * Applies all of the actions to the provided view.
3389 *
3390 * <p><strong>Caller beware: this may throw</strong>
Jim Millere667a7a2012-08-09 19:22:32 -07003391 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003392 * @param v The view to apply the actions to. This should be the result of
3393 * the {@link #apply(Context,ViewGroup)} call.
3394 */
3395 public void reapply(Context context, View v) {
Jim Millere667a7a2012-08-09 19:22:32 -07003396 reapply(context, v, null);
Dianne Hackborna1940212012-06-28 16:07:22 -07003397 }
3398
3399 /** @hide */
3400 public void reapply(Context context, View v, OnClickHandler handler) {
Adam Cohen5d200642012-04-24 10:43:31 -07003401 RemoteViews rvToApply = getRemoteViewsToApply(context);
3402
3403 // In the case that a view has this RemoteViews applied in one orientation, is persisted
3404 // across orientation change, and has the RemoteViews re-applied in the new orientation,
3405 // we throw an exception, since the layouts may be completely unrelated.
3406 if (hasLandscapeAndPortraitLayouts()) {
Sunny Goyal49e66952016-05-17 11:57:53 -07003407 if ((Integer) v.getTag(R.id.widget_frame) != rvToApply.getLayoutId()) {
Adam Cohen5d200642012-04-24 10:43:31 -07003408 throw new RuntimeException("Attempting to re-apply RemoteViews to a view that" +
3409 " that does not share the same root layout id.");
3410 }
3411 }
3412
Dianne Hackborna1940212012-06-28 16:07:22 -07003413 rvToApply.performApply(v, (ViewGroup) v.getParent(), handler);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003414 }
3415
Sunny Goyaldd292f42015-12-02 14:29:27 -08003416 /**
3417 * Applies all the actions to the provided view, moving as much of the task on the background
3418 * thread as possible.
3419 *
Elliot Waite54de7742017-01-11 15:30:35 -08003420 * @see #reapply(Context, View)
Sunny Goyaldd292f42015-12-02 14:29:27 -08003421 * @param context Default context to use
3422 * @param v The view to apply the actions to. This should be the result of
3423 * the {@link #apply(Context,ViewGroup)} call.
3424 * @param listener the callback to run when all actions have been applied. May be null.
3425 * @param executor The executor to use. If null {@link AsyncTask#THREAD_POOL_EXECUTOR} is used
3426 * @return CancellationSignal
3427 * @hide
3428 */
3429 public CancellationSignal reapplyAsync(
3430 Context context, View v, Executor executor, OnViewAppliedListener listener) {
3431 return reapplyAsync(context, v, executor, listener, null);
3432 }
3433
3434 /** @hide */
3435 public CancellationSignal reapplyAsync(Context context, View v, Executor executor,
3436 OnViewAppliedListener listener, OnClickHandler handler) {
3437 RemoteViews rvToApply = getRemoteViewsToApply(context);
3438
3439 // In the case that a view has this RemoteViews applied in one orientation, is persisted
3440 // across orientation change, and has the RemoteViews re-applied in the new orientation,
3441 // we throw an exception, since the layouts may be completely unrelated.
3442 if (hasLandscapeAndPortraitLayouts()) {
Sunny Goyal49e66952016-05-17 11:57:53 -07003443 if ((Integer) v.getTag(R.id.widget_frame) != rvToApply.getLayoutId()) {
Sunny Goyaldd292f42015-12-02 14:29:27 -08003444 throw new RuntimeException("Attempting to re-apply RemoteViews to a view that" +
3445 " that does not share the same root layout id.");
3446 }
3447 }
3448
3449 return startTaskOnExecutor(new AsyncApplyTask(rvToApply, (ViewGroup) v.getParent(),
3450 context, listener, handler, v), executor);
3451 }
3452
Dianne Hackborna1940212012-06-28 16:07:22 -07003453 private void performApply(View v, ViewGroup parent, OnClickHandler handler) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003454 if (mActions != null) {
Jim Millere667a7a2012-08-09 19:22:32 -07003455 handler = handler == null ? DEFAULT_ON_CLICK_HANDLER : handler;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003456 final int count = mActions.size();
3457 for (int i = 0; i < count; i++) {
3458 Action a = mActions.get(i);
Dianne Hackborna1940212012-06-28 16:07:22 -07003459 a.apply(v, parent, handler);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003460 }
3461 }
3462 }
3463
Sunny Goyal5c022632016-02-17 16:30:41 -08003464 /**
3465 * Returns true if the RemoteViews contains potentially costly operations and should be
3466 * applied asynchronously.
3467 *
3468 * @hide
3469 */
3470 public boolean prefersAsyncApply() {
3471 if (mActions != null) {
3472 final int count = mActions.size();
3473 for (int i = 0; i < count; i++) {
3474 if (mActions.get(i).prefersAsyncApply()) {
3475 return true;
3476 }
3477 }
3478 }
3479 return false;
3480 }
3481
Kenny Guy77320062014-08-27 21:37:15 +01003482 private Context getContextForResources(Context context) {
Svetoslav976e8bd2014-07-16 15:12:03 -07003483 if (mApplication != null) {
3484 if (context.getUserId() == UserHandle.getUserId(mApplication.uid)
3485 && context.getPackageName().equals(mApplication.packageName)) {
3486 return context;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003487 }
Svetoslav976e8bd2014-07-16 15:12:03 -07003488 try {
3489 return context.createApplicationContext(mApplication,
3490 Context.CONTEXT_RESTRICTED);
3491 } catch (NameNotFoundException e) {
Svet Ganov0da85b62014-08-06 14:11:37 -07003492 Log.e(LOG_TAG, "Package name " + mApplication.packageName + " not found");
Svetoslav976e8bd2014-07-16 15:12:03 -07003493 }
3494 }
3495
3496 return context;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003497 }
3498
Christoph Studer4600f9b2014-07-22 22:44:43 +02003499 /**
3500 * Returns the number of actions in this RemoteViews. Can be used as a sequence number.
3501 *
3502 * @hide
3503 */
3504 public int getSequenceNumber() {
3505 return (mActions == null) ? 0 : mActions.size();
3506 }
3507
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003508 /* (non-Javadoc)
3509 * Used to restrict the views which can be inflated
Jim Millere667a7a2012-08-09 19:22:32 -07003510 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003511 * @see android.view.LayoutInflater.Filter#onLoadClass(java.lang.Class)
3512 */
Gilles Debunnee6ac8b92010-06-17 10:55:04 -07003513 public boolean onLoadClass(Class clazz) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003514 return clazz.isAnnotationPresent(RemoteView.class);
3515 }
Adam Cohen5d200642012-04-24 10:43:31 -07003516
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003517 public int describeContents() {
3518 return 0;
3519 }
3520
3521 public void writeToParcel(Parcel dest, int flags) {
Adam Cohen5d200642012-04-24 10:43:31 -07003522 if (!hasLandscapeAndPortraitLayouts()) {
3523 dest.writeInt(MODE_NORMAL);
3524 // We only write the bitmap cache if we are the root RemoteViews, as this cache
3525 // is shared by all children.
3526 if (mIsRoot) {
3527 mBitmapCache.writeBitmapsToParcel(dest, flags);
3528 }
Sunny Goyal5d8bcdf2016-10-27 16:11:29 -07003529 if (!mIsRoot && (flags & PARCELABLE_ELIDE_DUPLICATES) != 0) {
3530 dest.writeInt(0);
3531 } else {
3532 dest.writeInt(1);
3533 mApplication.writeToParcel(dest, flags);
3534 }
Adam Cohen5d200642012-04-24 10:43:31 -07003535 dest.writeInt(mLayoutId);
3536 dest.writeInt(mIsWidgetCollectionChild ? 1 : 0);
Sunny Goyal5b7689f2017-09-21 11:08:34 -07003537 writeActionsToParcel(dest);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003538 } else {
Adam Cohen5d200642012-04-24 10:43:31 -07003539 dest.writeInt(MODE_HAS_LANDSCAPE_AND_PORTRAIT);
3540 // We only write the bitmap cache if we are the root RemoteViews, as this cache
3541 // is shared by all children.
3542 if (mIsRoot) {
3543 mBitmapCache.writeBitmapsToParcel(dest, flags);
3544 }
3545 mLandscape.writeToParcel(dest, flags);
Sunny Goyal5d8bcdf2016-10-27 16:11:29 -07003546 // Both RemoteViews already share the same package and user
3547 mPortrait.writeToParcel(dest, flags | PARCELABLE_ELIDE_DUPLICATES);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003548 }
Selim Cinekfc8073c2017-08-16 17:50:20 -07003549 dest.writeInt(mReapplyDisallowed ? 1 : 0);
Svet Ganov0da85b62014-08-06 14:11:37 -07003550 }
Svetoslav976e8bd2014-07-16 15:12:03 -07003551
Sunny Goyal5b7689f2017-09-21 11:08:34 -07003552 private void writeActionsToParcel(Parcel parcel) {
3553 int count;
3554 if (mActions != null) {
3555 count = mActions.size();
3556 } else {
3557 count = 0;
3558 }
3559 parcel.writeInt(count);
3560 for (int i = 0; i < count; i++) {
3561 Action a = mActions.get(i);
Sunny Goyal5b153922017-09-21 21:00:36 -07003562 parcel.writeInt(a.getActionTag());
Sunny Goyal5b7689f2017-09-21 11:08:34 -07003563 a.writeToParcel(parcel, a.hasSameAppInfo(mApplication)
3564 ? PARCELABLE_ELIDE_DUPLICATES : 0);
3565 }
3566 }
3567
Svet Ganov0da85b62014-08-06 14:11:37 -07003568 private static ApplicationInfo getApplicationInfo(String packageName, int userId) {
Svetoslavb6242442014-09-19 13:21:55 -07003569 if (packageName == null) {
3570 return null;
3571 }
3572
Svet Ganov0da85b62014-08-06 14:11:37 -07003573 // Get the application for the passed in package and user.
3574 Application application = ActivityThread.currentApplication();
3575 if (application == null) {
3576 throw new IllegalStateException("Cannot create remote views out of an aplication.");
3577 }
3578
3579 ApplicationInfo applicationInfo = application.getApplicationInfo();
3580 if (UserHandle.getUserId(applicationInfo.uid) != userId
3581 || !applicationInfo.packageName.equals(packageName)) {
3582 try {
Svetoslav14494a82014-08-18 10:43:27 -07003583 Context context = application.getBaseContext().createPackageContextAsUser(
Svet Ganov0da85b62014-08-06 14:11:37 -07003584 packageName, 0, new UserHandle(userId));
3585 applicationInfo = context.getApplicationInfo();
3586 } catch (NameNotFoundException nnfe) {
3587 throw new IllegalArgumentException("No such package " + packageName);
3588 }
3589 }
3590
3591 return applicationInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003592 }
3593
3594 /**
Sunny Goyaldd60f4d2017-10-18 15:22:42 -07003595 * Returns true if the {@link #mApplication} is same as the provided info.
3596 *
3597 * @hide
3598 */
3599 public boolean hasSameAppInfo(ApplicationInfo info) {
3600 return mApplication.packageName.equals(info.packageName) && mApplication.uid == info.uid;
3601 }
3602
3603 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003604 * Parcelable.Creator that instantiates RemoteViews objects
3605 */
3606 public static final Parcelable.Creator<RemoteViews> CREATOR = new Parcelable.Creator<RemoteViews>() {
3607 public RemoteViews createFromParcel(Parcel parcel) {
3608 return new RemoteViews(parcel);
3609 }
3610
3611 public RemoteViews[] newArray(int size) {
3612 return new RemoteViews[size];
3613 }
3614 };
Sunny Goyaldd292f42015-12-02 14:29:27 -08003615
3616 /**
3617 * A representation of the view hierarchy. Only views which have a valid ID are added
3618 * and can be searched.
3619 */
3620 private static class ViewTree {
Anthony Chen8f5f3582017-04-11 11:18:37 -07003621 private static final int INSERT_AT_END_INDEX = -1;
Sunny Goyal7b0e2c72016-11-03 14:48:05 -07003622 private View mRoot;
Sunny Goyaldd292f42015-12-02 14:29:27 -08003623 private ArrayList<ViewTree> mChildren;
3624
3625 private ViewTree(View root) {
3626 mRoot = root;
3627 }
3628
3629 public void createTree() {
3630 if (mChildren != null) {
3631 return;
3632 }
3633
3634 mChildren = new ArrayList<>();
Sunny Goyal7b0e2c72016-11-03 14:48:05 -07003635 if (mRoot instanceof ViewGroup) {
Sunny Goyaldd292f42015-12-02 14:29:27 -08003636 ViewGroup vg = (ViewGroup) mRoot;
3637 int count = vg.getChildCount();
3638 for (int i = 0; i < count; i++) {
3639 addViewChild(vg.getChildAt(i));
3640 }
3641 }
3642 }
3643
3644 public ViewTree findViewTreeById(int id) {
3645 if (mRoot.getId() == id) {
3646 return this;
3647 }
3648 if (mChildren == null) {
3649 return null;
3650 }
3651 for (ViewTree tree : mChildren) {
3652 ViewTree result = tree.findViewTreeById(id);
3653 if (result != null) {
3654 return result;
3655 }
3656 }
3657 return null;
3658 }
3659
Sunny Goyal7b0e2c72016-11-03 14:48:05 -07003660 public void replaceView(View v) {
3661 mRoot = v;
3662 mChildren = null;
3663 createTree();
3664 }
3665
Alan Viverette04fd4702017-04-13 16:37:06 -04003666 public <T extends View> T findViewById(int id) {
Sunny Goyaldd292f42015-12-02 14:29:27 -08003667 if (mChildren == null) {
3668 return mRoot.findViewById(id);
3669 }
3670 ViewTree tree = findViewTreeById(id);
Alan Viverette04fd4702017-04-13 16:37:06 -04003671 return tree == null ? null : (T) tree.mRoot;
Sunny Goyaldd292f42015-12-02 14:29:27 -08003672 }
3673
3674 public void addChild(ViewTree child) {
Anthony Chen8f5f3582017-04-11 11:18:37 -07003675 addChild(child, INSERT_AT_END_INDEX);
3676 }
3677
3678 /**
3679 * Adds the given {@link ViewTree} as a child at the given index.
3680 *
3681 * @param index The position at which to add the child or -1 to add last.
3682 */
3683 public void addChild(ViewTree child, int index) {
Sunny Goyaldd292f42015-12-02 14:29:27 -08003684 if (mChildren == null) {
3685 mChildren = new ArrayList<>();
3686 }
3687 child.createTree();
Anthony Chen8f5f3582017-04-11 11:18:37 -07003688
3689 if (index == INSERT_AT_END_INDEX) {
3690 mChildren.add(child);
3691 return;
3692 }
3693
3694 mChildren.add(index, child);
Sunny Goyaldd292f42015-12-02 14:29:27 -08003695 }
3696
3697 private void addViewChild(View v) {
Sunny Goyal7b0e2c72016-11-03 14:48:05 -07003698 // ViewTree only contains Views which can be found using findViewById.
3699 // If isRootNamespace is true, this view is skipped.
3700 // @see ViewGroup#findViewTraversal(int)
3701 if (v.isRootNamespace()) {
3702 return;
3703 }
Sunny Goyaldd292f42015-12-02 14:29:27 -08003704 final ViewTree target;
3705
3706 // If the view has a valid id, i.e., if can be found using findViewById, add it to the
3707 // tree, otherwise skip this view and add its children instead.
3708 if (v.getId() != 0) {
3709 ViewTree tree = new ViewTree(v);
3710 mChildren.add(tree);
3711 target = tree;
3712 } else {
3713 target = this;
3714 }
3715
Sunny Goyal7b0e2c72016-11-03 14:48:05 -07003716 if (v instanceof ViewGroup) {
Sunny Goyaldd292f42015-12-02 14:29:27 -08003717 if (target.mChildren == null) {
3718 target.mChildren = new ArrayList<>();
3719 ViewGroup vg = (ViewGroup) v;
3720 int count = vg.getChildCount();
3721 for (int i = 0; i < count; i++) {
3722 target.addViewChild(vg.getChildAt(i));
3723 }
3724 }
3725 }
3726 }
3727 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003728}