blob: 1cccfaeb7dc37aef5bd32f6d84f95dae861df1f9 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.widget;
18
Tor Norbye80756e32015-03-02 09:39:27 -080019import android.annotation.ColorInt;
Winson Chungdc6f79b2012-04-17 17:27:31 -070020import android.app.ActivityOptions;
Svetoslav976e8bd2014-07-16 15:12:03 -070021import android.app.ActivityThread;
Svet Ganov0da85b62014-08-06 14:11:37 -070022import android.app.Application;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080023import android.app.PendingIntent;
Adrian Roosfe84e1f2015-11-04 15:55:39 -080024import android.app.RemoteInput;
Adam Cohen1480fdd2010-08-25 17:24:53 -070025import android.appwidget.AppWidgetHostView;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080026import android.content.Context;
Kenny Guy77320062014-08-27 21:37:15 +010027import android.content.ContextWrapper;
Dianne Hackbornfa82f222009-09-17 15:14:12 -070028import android.content.Intent;
29import android.content.IntentSender;
Adam Cohenffc46a52012-04-30 19:54:39 -070030import android.content.pm.ApplicationInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080031import android.content.pm.PackageManager.NameNotFoundException;
Jorim Jaggief72a192014-08-26 21:57:46 +020032import android.content.res.ColorStateList;
Adam Cohen5d200642012-04-24 10:43:31 -070033import android.content.res.Configuration;
Kenny Guy77320062014-08-27 21:37:15 +010034import android.content.res.Resources;
Gus Prevas1ed322b2015-09-17 17:34:46 -040035import android.content.res.TypedArray;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080036import android.graphics.Bitmap;
37import android.graphics.PorterDuff;
Joe Onorato75970652009-12-02 23:04:55 -080038import android.graphics.Rect;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080039import android.graphics.drawable.Drawable;
Dan Sandlera22a3802015-05-13 00:12:47 -040040import android.graphics.drawable.Icon;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080041import android.net.Uri;
Sunny Goyaldd292f42015-12-02 14:29:27 -080042import android.os.AsyncTask;
Adam Cohenffc46a52012-04-30 19:54:39 -070043import android.os.Build;
Bjorn Bringertd755b062010-01-06 17:15:37 +000044import android.os.Bundle;
Sunny Goyaldd292f42015-12-02 14:29:27 -080045import android.os.CancellationSignal;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080046import android.os.Parcel;
47import android.os.Parcelable;
Jeff Sharkeya14acd22013-04-02 18:27:45 -070048import android.os.StrictMode;
Jeff Sharkey6d515712012-09-20 16:06:08 -070049import android.os.UserHandle;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080050import android.text.TextUtils;
Romain Guye4d4e202013-07-22 13:02:02 -070051import android.util.ArrayMap;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080052import android.util.Log;
53import android.view.LayoutInflater;
Winson Chungdc6f79b2012-04-17 17:27:31 -070054import android.view.LayoutInflater.Filter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080055import android.view.RemotableViewMethod;
56import android.view.View;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080057import android.view.View.OnClickListener;
Winson Chungdc6f79b2012-04-17 17:27:31 -070058import android.view.ViewGroup;
Adam Cohena32edd42010-10-26 10:35:01 -070059import android.widget.AdapterView.OnItemClickListener;
Romain Guy484f4d62013-07-22 16:39:16 -070060import libcore.util.Objects;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080061
Gus Prevas1ed322b2015-09-17 17:34:46 -040062import com.android.internal.R;
63
Winson Chungdc6f79b2012-04-17 17:27:31 -070064import java.lang.annotation.ElementType;
65import java.lang.annotation.Retention;
66import java.lang.annotation.RetentionPolicy;
67import java.lang.annotation.Target;
68import java.lang.reflect.Method;
69import java.util.ArrayList;
Adam Cohenfbe44b72012-09-19 20:36:23 -070070import java.util.HashMap;
Sunny Goyaldd292f42015-12-02 14:29:27 -080071import java.util.concurrent.Executor;
Winson Chungdc6f79b2012-04-17 17:27:31 -070072
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080073/**
74 * A class that describes a view hierarchy that can be displayed in
75 * another process. The hierarchy is inflated from a layout resource
76 * file, and this class provides some basic operations for modifying
77 * the content of the inflated hierarchy.
78 */
79public class RemoteViews implements Parcelable, Filter {
Jim Millere667a7a2012-08-09 19:22:32 -070080
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080081 private static final String LOG_TAG = "RemoteViews";
Jim Millere667a7a2012-08-09 19:22:32 -070082
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080083 /**
Winson Chung81f39eb2011-01-11 18:05:01 -080084 * The intent extra that contains the appWidgetId.
85 * @hide
86 */
87 static final String EXTRA_REMOTEADAPTER_APPWIDGET_ID = "remoteAdapterAppWidgetId";
88
89 /**
Svetoslav976e8bd2014-07-16 15:12:03 -070090 * Application that hosts the remote views.
91 *
92 * @hide
Jeff Sharkey6d515712012-09-20 16:06:08 -070093 */
Svetoslav976e8bd2014-07-16 15:12:03 -070094 private ApplicationInfo mApplication;
Jeff Sharkey6d515712012-09-20 16:06:08 -070095
96 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080097 * The resource ID of the layout file. (Added to the parcel)
98 */
Gilles Debunne30301932010-06-16 18:32:00 -070099 private final int mLayoutId;
Romain Guya5475592009-07-01 17:20:08 -0700100
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800101 /**
102 * An array of actions to perform on the view tree once it has been
103 * inflated
104 */
105 private ArrayList<Action> mActions;
Jim Millere667a7a2012-08-09 19:22:32 -0700106
Winson Chung3ec9a452010-09-23 16:40:28 -0700107 /**
108 * A class to keep track of memory usage by this RemoteViews
109 */
110 private MemoryUsageCounter mMemoryUsageCounter;
111
Adam Cohen5d200642012-04-24 10:43:31 -0700112 /**
113 * Maps bitmaps to unique indicies to avoid Bitmap duplication.
114 */
115 private BitmapCache mBitmapCache;
116
117 /**
118 * Indicates whether or not this RemoteViews object is contained as a child of any other
119 * RemoteViews.
120 */
121 private boolean mIsRoot = true;
122
123 /**
124 * Constants to whether or not this RemoteViews is composed of a landscape and portrait
125 * RemoteViews.
126 */
127 private static final int MODE_NORMAL = 0;
128 private static final int MODE_HAS_LANDSCAPE_AND_PORTRAIT = 1;
129
130 /**
131 * Used in conjunction with the special constructor
132 * {@link #RemoteViews(RemoteViews, RemoteViews)} to keep track of the landscape and portrait
133 * RemoteViews.
134 */
135 private RemoteViews mLandscape = null;
136 private RemoteViews mPortrait = null;
137
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800138 /**
Adam Cohenca6fd842010-09-03 18:10:35 -0700139 * This flag indicates whether this RemoteViews object is being created from a
140 * RemoteViewsService for use as a child of a widget collection. This flag is used
141 * to determine whether or not certain features are available, in particular,
142 * setting on click extras and setting on click pending intents. The former is enabled,
143 * and the latter disabled when this flag is true.
144 */
Dianne Hackborn1927ae82012-06-22 15:21:36 -0700145 private boolean mIsWidgetCollectionChild = false;
146
147 private static final OnClickHandler DEFAULT_ON_CLICK_HANDLER = new OnClickHandler();
Adam Cohenca6fd842010-09-03 18:10:35 -0700148
Romain Guy484f4d62013-07-22 16:39:16 -0700149 private static final Object[] sMethodsLock = new Object[0];
150 private static final ArrayMap<Class<? extends View>, ArrayMap<MutablePair<String, Class<?>>, Method>> sMethods =
151 new ArrayMap<Class<? extends View>, ArrayMap<MutablePair<String, Class<?>>, Method>>();
Sunny Goyaldd292f42015-12-02 14:29:27 -0800152 private static final ArrayMap<Method, Method> sAsyncMethods = new ArrayMap<>();
153
Romain Guye4d4e202013-07-22 13:02:02 -0700154 private static final ThreadLocal<Object[]> sInvokeArgsTls = new ThreadLocal<Object[]>() {
155 @Override
156 protected Object[] initialValue() {
157 return new Object[1];
158 }
159 };
160
Adam Cohenca6fd842010-09-03 18:10:35 -0700161 /**
Adrian Roosfe84e1f2015-11-04 15:55:39 -0800162 * @hide
163 */
164 public void setRemoteInputs(int viewId, RemoteInput[] remoteInputs) {
165 mActions.add(new SetRemoteInputsAction(viewId, remoteInputs));
166 }
167
168 /**
Romain Guy484f4d62013-07-22 16:39:16 -0700169 * Handle with care!
170 */
171 static class MutablePair<F, S> {
172 F first;
173 S second;
174
175 MutablePair(F first, S second) {
176 this.first = first;
177 this.second = second;
178 }
179
180 @Override
181 public boolean equals(Object o) {
182 if (!(o instanceof MutablePair)) {
183 return false;
184 }
185 MutablePair<?, ?> p = (MutablePair<?, ?>) o;
186 return Objects.equal(p.first, first) && Objects.equal(p.second, second);
187 }
188
189 @Override
190 public int hashCode() {
191 return (first == null ? 0 : first.hashCode()) ^ (second == null ? 0 : second.hashCode());
192 }
193 }
194
195 /**
196 * This pair is used to perform lookups in sMethods without causing allocations.
197 */
198 private final MutablePair<String, Class<?>> mPair =
199 new MutablePair<String, Class<?>>(null, null);
200
201 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800202 * This annotation indicates that a subclass of View is alllowed to be used
Romain Guya5475592009-07-01 17:20:08 -0700203 * with the {@link RemoteViews} mechanism.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800204 */
205 @Target({ ElementType.TYPE })
206 @Retention(RetentionPolicy.RUNTIME)
207 public @interface RemoteView {
208 }
209
210 /**
211 * Exception to send when something goes wrong executing an action
212 *
213 */
214 public static class ActionException extends RuntimeException {
215 public ActionException(Exception ex) {
216 super(ex);
217 }
218 public ActionException(String message) {
219 super(message);
220 }
221 }
Adam Cohena32edd42010-10-26 10:35:01 -0700222
Dianne Hackborn1927ae82012-06-22 15:21:36 -0700223 /** @hide */
224 public static class OnClickHandler {
Gus Prevas1ed322b2015-09-17 17:34:46 -0400225
226 private int mEnterAnimationId;
227
Dianne Hackborn1927ae82012-06-22 15:21:36 -0700228 public boolean onClickHandler(View view, PendingIntent pendingIntent,
Winson Chungae615982010-11-01 12:14:49 -0700229 Intent fillInIntent) {
230 try {
231 // TODO: Unregister this handler if PendingIntent.FLAG_ONE_SHOT?
Winson Chungdc6f79b2012-04-17 17:27:31 -0700232 Context context = view.getContext();
Gus Prevas1ed322b2015-09-17 17:34:46 -0400233 ActivityOptions opts;
234 if (mEnterAnimationId != 0) {
235 opts = ActivityOptions.makeCustomAnimation(context, mEnterAnimationId, 0);
236 } else {
237 opts = ActivityOptions.makeScaleUpAnimation(view,
238 0, 0,
239 view.getMeasuredWidth(), view.getMeasuredHeight());
240 }
Winson Chungae615982010-11-01 12:14:49 -0700241 context.startIntentSender(
242 pendingIntent.getIntentSender(), fillInIntent,
243 Intent.FLAG_ACTIVITY_NEW_TASK,
Winson Chungdc6f79b2012-04-17 17:27:31 -0700244 Intent.FLAG_ACTIVITY_NEW_TASK, 0, opts.toBundle());
Winson Chungae615982010-11-01 12:14:49 -0700245 } catch (IntentSender.SendIntentException e) {
246 android.util.Log.e(LOG_TAG, "Cannot send pending intent: ", e);
247 return false;
248 } catch (Exception e) {
249 android.util.Log.e(LOG_TAG, "Cannot send pending intent due to " +
250 "unknown exception: ", e);
251 return false;
252 }
253 return true;
254 }
Gus Prevas1ed322b2015-09-17 17:34:46 -0400255
256 public void setEnterAnimationId(int enterAnimationId) {
257 mEnterAnimationId = enterAnimationId;
258 }
Dianne Hackborn1927ae82012-06-22 15:21:36 -0700259 }
260
261 /**
262 * Base class for all actions that can be performed on an
263 * inflated view.
264 *
265 * SUBCLASSES MUST BE IMMUTABLE SO CLONE WORKS!!!!!
266 */
267 private abstract static class Action implements Parcelable {
Dianne Hackborna1940212012-06-28 16:07:22 -0700268 public abstract void apply(View root, ViewGroup rootParent,
269 OnClickHandler handler) throws ActionException;
Dianne Hackborn1927ae82012-06-22 15:21:36 -0700270
Adam Cohenfbe44b72012-09-19 20:36:23 -0700271 public static final int MERGE_REPLACE = 0;
272 public static final int MERGE_APPEND = 1;
273 public static final int MERGE_IGNORE = 2;
274
Dianne Hackborn1927ae82012-06-22 15:21:36 -0700275 public int describeContents() {
276 return 0;
277 }
278
279 /**
280 * Overridden by each class to report on it's own memory usage
281 */
282 public void updateMemoryUsageEstimate(MemoryUsageCounter counter) {
Romain Guye4d4e202013-07-22 13:02:02 -0700283 // We currently only calculate Bitmap memory usage, so by default,
284 // don't do anything here
Dianne Hackborn1927ae82012-06-22 15:21:36 -0700285 }
Adam Cohen5d200642012-04-24 10:43:31 -0700286
287 public void setBitmapCache(BitmapCache bitmapCache) {
288 // Do nothing
289 }
Adam Cohenfbe44b72012-09-19 20:36:23 -0700290
291 public int mergeBehavior() {
292 return MERGE_REPLACE;
293 }
294
295 public abstract String getActionName();
296
297 public String getUniqueKey() {
298 return (getActionName() + viewId);
299 }
300
Sunny Goyaldd292f42015-12-02 14:29:27 -0800301 /**
302 * This is called on the background thread. It should perform any non-ui computations
303 * and return the final action which will run on the UI thread.
304 * Override this if some of the tasks can be performed async.
305 */
306 public Action initActionAsync(ViewTree root, ViewGroup rootParent, OnClickHandler handler) {
307 return this;
308 }
309
Adam Cohenfbe44b72012-09-19 20:36:23 -0700310 int viewId;
311 }
312
Adam Cohenbd1e0072012-09-21 16:51:50 -0700313 /**
Sunny Goyaldd292f42015-12-02 14:29:27 -0800314 * Action class used during async inflation of RemoteViews. Subclasses are not parcelable.
315 */
316 private static abstract class RuntimeAction extends Action {
317 @Override
318 public final String getActionName() {
319 return "RuntimeAction";
320 }
321
322 @Override
323 public final void writeToParcel(Parcel dest, int flags) {
324 throw new UnsupportedOperationException();
325 }
326 }
327
328 // Constant used during async execution. It is not parcelable.
329 private static final Action ACTION_NOOP = new RuntimeAction() {
330 @Override
331 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) { }
332 };
333
334 /**
Adam Cohenbd1e0072012-09-21 16:51:50 -0700335 * Merges the passed RemoteViews actions with this RemoteViews actions according to
336 * action-specific merge rules.
Svetoslav976e8bd2014-07-16 15:12:03 -0700337 *
Adam Cohenbd1e0072012-09-21 16:51:50 -0700338 * @param newRv
Svetoslav976e8bd2014-07-16 15:12:03 -0700339 *
Adam Cohenbd1e0072012-09-21 16:51:50 -0700340 * @hide
341 */
Adam Cohenfbe44b72012-09-19 20:36:23 -0700342 public void mergeRemoteViews(RemoteViews newRv) {
Adam Cohen3ff2d862012-09-26 14:07:57 -0700343 if (newRv == null) return;
Adam Cohenfbe44b72012-09-19 20:36:23 -0700344 // We first copy the new RemoteViews, as the process of merging modifies the way the actions
345 // reference the bitmap cache. We don't want to modify the object as it may need to
346 // be merged and applied multiple times.
Adam Cohen3ff2d862012-09-26 14:07:57 -0700347 RemoteViews copy = newRv.clone();
Adam Cohenfbe44b72012-09-19 20:36:23 -0700348
349 HashMap<String, Action> map = new HashMap<String, Action>();
350 if (mActions == null) {
351 mActions = new ArrayList<Action>();
352 }
353
354 int count = mActions.size();
355 for (int i = 0; i < count; i++) {
356 Action a = mActions.get(i);
357 map.put(a.getUniqueKey(), a);
358 }
359
360 ArrayList<Action> newActions = copy.mActions;
361 if (newActions == null) return;
362 count = newActions.size();
363 for (int i = 0; i < count; i++) {
364 Action a = newActions.get(i);
365 String key = newActions.get(i).getUniqueKey();
Adam Cohen3ff2d862012-09-26 14:07:57 -0700366 int mergeBehavior = newActions.get(i).mergeBehavior();
Adam Cohenfbe44b72012-09-19 20:36:23 -0700367 if (map.containsKey(key) && mergeBehavior == Action.MERGE_REPLACE) {
368 mActions.remove(map.get(key));
369 map.remove(key);
370 }
371
372 // If the merge behavior is ignore, we don't bother keeping the extra action
373 if (mergeBehavior == Action.MERGE_REPLACE || mergeBehavior == Action.MERGE_APPEND) {
374 mActions.add(a);
375 }
376 }
377
378 // Because pruning can remove the need for bitmaps, we reconstruct the bitmap cache
379 mBitmapCache = new BitmapCache();
380 setBitmapCache(mBitmapCache);
Romain Guya5475592009-07-01 17:20:08 -0700381 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800382
Adam Cohen1480fdd2010-08-25 17:24:53 -0700383 private class SetEmptyView extends Action {
384 int viewId;
385 int emptyViewId;
386
387 public final static int TAG = 6;
388
389 SetEmptyView(int viewId, int emptyViewId) {
390 this.viewId = viewId;
391 this.emptyViewId = emptyViewId;
392 }
393
394 SetEmptyView(Parcel in) {
395 this.viewId = in.readInt();
396 this.emptyViewId = in.readInt();
397 }
398
399 public void writeToParcel(Parcel out, int flags) {
400 out.writeInt(TAG);
401 out.writeInt(this.viewId);
402 out.writeInt(this.emptyViewId);
403 }
404
405 @Override
Dianne Hackborna1940212012-06-28 16:07:22 -0700406 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
Adam Cohen1480fdd2010-08-25 17:24:53 -0700407 final View view = root.findViewById(viewId);
408 if (!(view instanceof AdapterView<?>)) return;
409
410 AdapterView<?> adapterView = (AdapterView<?>) view;
411
412 final View emptyView = root.findViewById(emptyViewId);
413 if (emptyView == null) return;
414
415 adapterView.setEmptyView(emptyView);
416 }
Adam Cohenfbe44b72012-09-19 20:36:23 -0700417
418 public String getActionName() {
419 return "SetEmptyView";
420 }
Adam Cohen1480fdd2010-08-25 17:24:53 -0700421 }
422
Adam Cohen35ae9ca2010-09-20 15:20:41 -0700423 private class SetOnClickFillInIntent extends Action {
424 public SetOnClickFillInIntent(int id, Intent fillInIntent) {
425 this.viewId = id;
426 this.fillInIntent = fillInIntent;
427 }
428
429 public SetOnClickFillInIntent(Parcel parcel) {
430 viewId = parcel.readInt();
431 fillInIntent = Intent.CREATOR.createFromParcel(parcel);
432 }
433
434 public void writeToParcel(Parcel dest, int flags) {
435 dest.writeInt(TAG);
436 dest.writeInt(viewId);
437 fillInIntent.writeToParcel(dest, 0 /* no flags */);
438 }
439
440 @Override
Dianne Hackborna1940212012-06-28 16:07:22 -0700441 public void apply(View root, ViewGroup rootParent, final OnClickHandler handler) {
Adam Cohen35ae9ca2010-09-20 15:20:41 -0700442 final View target = root.findViewById(viewId);
Joe Onorato2b69ce42010-10-31 11:35:41 -0700443 if (target == null) return;
Adam Cohen35ae9ca2010-09-20 15:20:41 -0700444
445 if (!mIsWidgetCollectionChild) {
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800446 Log.e(LOG_TAG, "The method setOnClickFillInIntent is available " +
Adam Cohen35ae9ca2010-09-20 15:20:41 -0700447 "only from RemoteViewsFactory (ie. on collection items).");
448 return;
449 }
Adam Cohena32edd42010-10-26 10:35:01 -0700450 if (target == root) {
451 target.setTagInternal(com.android.internal.R.id.fillInIntent, fillInIntent);
Romain Guye4d4e202013-07-22 13:02:02 -0700452 } else if (fillInIntent != null) {
Adam Cohen35ae9ca2010-09-20 15:20:41 -0700453 OnClickListener listener = new OnClickListener() {
454 public void onClick(View v) {
455 // Insure that this view is a child of an AdapterView
456 View parent = (View) v.getParent();
Sunny Goyalb880d162016-02-24 14:38:59 -0800457 // Break the for loop on the first encounter of:
458 // 1) an AdapterView,
459 // 2) an AppWidgetHostView that is not a RemoteViewsFrameLayout, or
460 // 3) a null parent.
461 // 2) and 3) are unexpected and catch the case where a child is not
462 // correctly parented in an AdapterView.
Adam Cohen85a08f12012-11-06 11:24:23 -0800463 while (parent != null && !(parent instanceof AdapterView<?>)
Sunny Goyalb880d162016-02-24 14:38:59 -0800464 && !((parent instanceof AppWidgetHostView) &&
465 !(parent instanceof RemoteViewsAdapter.RemoteViewsFrameLayout))) {
Adam Cohen35ae9ca2010-09-20 15:20:41 -0700466 parent = (View) parent.getParent();
467 }
468
Sunny Goyalb880d162016-02-24 14:38:59 -0800469 if (!(parent instanceof AdapterView<?>)) {
Adam Cohen35ae9ca2010-09-20 15:20:41 -0700470 // Somehow they've managed to get this far without having
471 // and AdapterView as a parent.
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800472 Log.e(LOG_TAG, "Collection item doesn't have AdapterView parent");
Adam Cohen35ae9ca2010-09-20 15:20:41 -0700473 return;
474 }
475
476 // Insure that a template pending intent has been set on an ancestor
477 if (!(parent.getTag() instanceof PendingIntent)) {
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800478 Log.e(LOG_TAG, "Attempting setOnClickFillInIntent without" +
Adam Cohena32edd42010-10-26 10:35:01 -0700479 " calling setPendingIntentTemplate on parent.");
Adam Cohen35ae9ca2010-09-20 15:20:41 -0700480 return;
481 }
482
483 PendingIntent pendingIntent = (PendingIntent) parent.getTag();
484
Romain Guye4d4e202013-07-22 13:02:02 -0700485 final Rect rect = getSourceBounds(v);
Adam Cohen35ae9ca2010-09-20 15:20:41 -0700486
487 fillInIntent.setSourceBounds(rect);
Dianne Hackborna1940212012-06-28 16:07:22 -0700488 handler.onClickHandler(v, pendingIntent, fillInIntent);
Adam Cohen35ae9ca2010-09-20 15:20:41 -0700489 }
490
491 };
492 target.setOnClickListener(listener);
493 }
494 }
495
Adam Cohenfbe44b72012-09-19 20:36:23 -0700496 public String getActionName() {
497 return "SetOnClickFillInIntent";
498 }
499
Adam Cohen35ae9ca2010-09-20 15:20:41 -0700500 Intent fillInIntent;
501
502 public final static int TAG = 9;
503 }
504
Adam Cohenca6fd842010-09-03 18:10:35 -0700505 private class SetPendingIntentTemplate extends Action {
506 public SetPendingIntentTemplate(int id, PendingIntent pendingIntentTemplate) {
507 this.viewId = id;
508 this.pendingIntentTemplate = pendingIntentTemplate;
509 }
510
511 public SetPendingIntentTemplate(Parcel parcel) {
512 viewId = parcel.readInt();
513 pendingIntentTemplate = PendingIntent.readPendingIntentOrNullFromParcel(parcel);
514 }
515
516 public void writeToParcel(Parcel dest, int flags) {
517 dest.writeInt(TAG);
518 dest.writeInt(viewId);
519 pendingIntentTemplate.writeToParcel(dest, 0 /* no flags */);
520 }
521
522 @Override
Dianne Hackborna1940212012-06-28 16:07:22 -0700523 public void apply(View root, ViewGroup rootParent, final OnClickHandler handler) {
Adam Cohenca6fd842010-09-03 18:10:35 -0700524 final View target = root.findViewById(viewId);
Joe Onorato2b69ce42010-10-31 11:35:41 -0700525 if (target == null) return;
Adam Cohenca6fd842010-09-03 18:10:35 -0700526
527 // If the view isn't an AdapterView, setting a PendingIntent template doesn't make sense
528 if (target instanceof AdapterView<?>) {
Adam Cohena32edd42010-10-26 10:35:01 -0700529 AdapterView<?> av = (AdapterView<?>) target;
Adam Cohenca6fd842010-09-03 18:10:35 -0700530 // The PendingIntent template is stored in the view's tag.
Adam Cohena32edd42010-10-26 10:35:01 -0700531 OnItemClickListener listener = new OnItemClickListener() {
532 public void onItemClick(AdapterView<?> parent, View view,
533 int position, long id) {
534 // The view should be a frame layout
535 if (view instanceof ViewGroup) {
536 ViewGroup vg = (ViewGroup) view;
537
538 // AdapterViews contain their children in a frame
539 // so we need to go one layer deeper here.
540 if (parent instanceof AdapterViewAnimator) {
541 vg = (ViewGroup) vg.getChildAt(0);
542 }
543 if (vg == null) return;
544
545 Intent fillInIntent = null;
546 int childCount = vg.getChildCount();
547 for (int i = 0; i < childCount; i++) {
548 Object tag = vg.getChildAt(i).getTag(com.android.internal.R.id.fillInIntent);
549 if (tag instanceof Intent) {
550 fillInIntent = (Intent) tag;
551 break;
552 }
553 }
554 if (fillInIntent == null) return;
555
Romain Guye4d4e202013-07-22 13:02:02 -0700556 final Rect rect = getSourceBounds(view);
Adam Cohena32edd42010-10-26 10:35:01 -0700557
558 final Intent intent = new Intent();
559 intent.setSourceBounds(rect);
Dianne Hackborna1940212012-06-28 16:07:22 -0700560 handler.onClickHandler(view, pendingIntentTemplate, fillInIntent);
Adam Cohena32edd42010-10-26 10:35:01 -0700561 }
562 }
563 };
564 av.setOnItemClickListener(listener);
565 av.setTag(pendingIntentTemplate);
Adam Cohenca6fd842010-09-03 18:10:35 -0700566 } else {
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800567 Log.e(LOG_TAG, "Cannot setPendingIntentTemplate on a view which is not" +
Adam Cohen35ae9ca2010-09-20 15:20:41 -0700568 "an AdapterView (id: " + viewId + ")");
Adam Cohenca6fd842010-09-03 18:10:35 -0700569 return;
570 }
571 }
572
Adam Cohenfbe44b72012-09-19 20:36:23 -0700573 public String getActionName() {
574 return "SetPendingIntentTemplate";
575 }
576
Adam Cohenca6fd842010-09-03 18:10:35 -0700577 PendingIntent pendingIntentTemplate;
578
579 public final static int TAG = 8;
580 }
581
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800582 private class SetRemoteViewsAdapterList extends Action {
Adam Cohenb00d9f02013-01-10 14:12:52 -0800583 public SetRemoteViewsAdapterList(int id, ArrayList<RemoteViews> list, int viewTypeCount) {
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800584 this.viewId = id;
585 this.list = list;
Adam Cohenb00d9f02013-01-10 14:12:52 -0800586 this.viewTypeCount = viewTypeCount;
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800587 }
588
589 public SetRemoteViewsAdapterList(Parcel parcel) {
590 viewId = parcel.readInt();
Adam Cohenb00d9f02013-01-10 14:12:52 -0800591 viewTypeCount = parcel.readInt();
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800592 int count = parcel.readInt();
593 list = new ArrayList<RemoteViews>();
594
595 for (int i = 0; i < count; i++) {
596 RemoteViews rv = RemoteViews.CREATOR.createFromParcel(parcel);
597 list.add(rv);
598 }
599 }
600
601 public void writeToParcel(Parcel dest, int flags) {
602 dest.writeInt(TAG);
603 dest.writeInt(viewId);
Adam Cohenb00d9f02013-01-10 14:12:52 -0800604 dest.writeInt(viewTypeCount);
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800605
606 if (list == null || list.size() == 0) {
607 dest.writeInt(0);
608 } else {
609 int count = list.size();
610 dest.writeInt(count);
611 for (int i = 0; i < count; i++) {
612 RemoteViews rv = list.get(i);
613 rv.writeToParcel(dest, flags);
614 }
615 }
616 }
617
618 @Override
619 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
620 final View target = root.findViewById(viewId);
621 if (target == null) return;
622
623 // Ensure that we are applying to an AppWidget root
624 if (!(rootParent instanceof AppWidgetHostView)) {
625 Log.e(LOG_TAG, "SetRemoteViewsAdapterIntent action can only be used for " +
626 "AppWidgets (root id: " + viewId + ")");
627 return;
628 }
629 // Ensure that we are calling setRemoteAdapter on an AdapterView that supports it
630 if (!(target instanceof AbsListView) && !(target instanceof AdapterViewAnimator)) {
631 Log.e(LOG_TAG, "Cannot setRemoteViewsAdapter on a view which is not " +
632 "an AbsListView or AdapterViewAnimator (id: " + viewId + ")");
633 return;
634 }
635
636 if (target instanceof AbsListView) {
637 AbsListView v = (AbsListView) target;
638 Adapter a = v.getAdapter();
Adam Cohenb00d9f02013-01-10 14:12:52 -0800639 if (a instanceof RemoteViewsListAdapter && viewTypeCount <= a.getViewTypeCount()) {
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800640 ((RemoteViewsListAdapter) a).setViewsList(list);
641 } else {
Adam Cohenb00d9f02013-01-10 14:12:52 -0800642 v.setAdapter(new RemoteViewsListAdapter(v.getContext(), list, viewTypeCount));
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800643 }
644 } else if (target instanceof AdapterViewAnimator) {
645 AdapterViewAnimator v = (AdapterViewAnimator) target;
646 Adapter a = v.getAdapter();
Adam Cohenb00d9f02013-01-10 14:12:52 -0800647 if (a instanceof RemoteViewsListAdapter && viewTypeCount <= a.getViewTypeCount()) {
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800648 ((RemoteViewsListAdapter) a).setViewsList(list);
649 } else {
Adam Cohenb00d9f02013-01-10 14:12:52 -0800650 v.setAdapter(new RemoteViewsListAdapter(v.getContext(), list, viewTypeCount));
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800651 }
652 }
653 }
654
655 public String getActionName() {
656 return "SetRemoteViewsAdapterList";
657 }
658
Adam Cohenb00d9f02013-01-10 14:12:52 -0800659 int viewTypeCount;
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800660 ArrayList<RemoteViews> list;
661 public final static int TAG = 15;
662 }
663
Winson Chung037300b2011-03-29 15:40:16 -0700664 private class SetRemoteViewsAdapterIntent extends Action {
665 public SetRemoteViewsAdapterIntent(int id, Intent intent) {
666 this.viewId = id;
667 this.intent = intent;
668 }
669
670 public SetRemoteViewsAdapterIntent(Parcel parcel) {
671 viewId = parcel.readInt();
672 intent = Intent.CREATOR.createFromParcel(parcel);
673 }
674
675 public void writeToParcel(Parcel dest, int flags) {
676 dest.writeInt(TAG);
677 dest.writeInt(viewId);
678 intent.writeToParcel(dest, flags);
679 }
680
681 @Override
Dianne Hackborna1940212012-06-28 16:07:22 -0700682 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
Winson Chung037300b2011-03-29 15:40:16 -0700683 final View target = root.findViewById(viewId);
684 if (target == null) return;
685
686 // Ensure that we are applying to an AppWidget root
687 if (!(rootParent instanceof AppWidgetHostView)) {
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800688 Log.e(LOG_TAG, "SetRemoteViewsAdapterIntent action can only be used for " +
Winson Chung037300b2011-03-29 15:40:16 -0700689 "AppWidgets (root id: " + viewId + ")");
690 return;
691 }
692 // Ensure that we are calling setRemoteAdapter on an AdapterView that supports it
693 if (!(target instanceof AbsListView) && !(target instanceof AdapterViewAnimator)) {
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800694 Log.e(LOG_TAG, "Cannot setRemoteViewsAdapter on a view which is not " +
Winson Chung037300b2011-03-29 15:40:16 -0700695 "an AbsListView or AdapterViewAnimator (id: " + viewId + ")");
696 return;
697 }
698
699 // Embed the AppWidget Id for use in RemoteViewsAdapter when connecting to the intent
700 // RemoteViewsService
701 AppWidgetHostView host = (AppWidgetHostView) rootParent;
702 intent.putExtra(EXTRA_REMOTEADAPTER_APPWIDGET_ID, host.getAppWidgetId());
703 if (target instanceof AbsListView) {
704 AbsListView v = (AbsListView) target;
705 v.setRemoteViewsAdapter(intent);
Adam Cohena6a4cbc2012-09-26 17:36:40 -0700706 v.setRemoteViewsOnClickHandler(handler);
Winson Chung037300b2011-03-29 15:40:16 -0700707 } else if (target instanceof AdapterViewAnimator) {
708 AdapterViewAnimator v = (AdapterViewAnimator) target;
709 v.setRemoteViewsAdapter(intent);
Adam Cohena6a4cbc2012-09-26 17:36:40 -0700710 v.setRemoteViewsOnClickHandler(handler);
Winson Chung037300b2011-03-29 15:40:16 -0700711 }
712 }
713
Adam Cohenfbe44b72012-09-19 20:36:23 -0700714 public String getActionName() {
715 return "SetRemoteViewsAdapterIntent";
716 }
717
Winson Chung037300b2011-03-29 15:40:16 -0700718 Intent intent;
719
720 public final static int TAG = 10;
721 }
722
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800723 /**
724 * Equivalent to calling
725 * {@link android.view.View#setOnClickListener(android.view.View.OnClickListener)}
726 * to launch the provided {@link PendingIntent}.
727 */
728 private class SetOnClickPendingIntent extends Action {
729 public SetOnClickPendingIntent(int id, PendingIntent pendingIntent) {
730 this.viewId = id;
731 this.pendingIntent = pendingIntent;
732 }
Adam Cohenca6fd842010-09-03 18:10:35 -0700733
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800734 public SetOnClickPendingIntent(Parcel parcel) {
735 viewId = parcel.readInt();
Adam Cohenc6151f22012-02-02 21:02:31 -0800736
737 // We check a flag to determine if the parcel contains a PendingIntent.
738 if (parcel.readInt() != 0) {
739 pendingIntent = PendingIntent.readPendingIntentOrNullFromParcel(parcel);
740 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800741 }
Adam Cohenca6fd842010-09-03 18:10:35 -0700742
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800743 public void writeToParcel(Parcel dest, int flags) {
744 dest.writeInt(TAG);
745 dest.writeInt(viewId);
Adam Cohenc6151f22012-02-02 21:02:31 -0800746
747 // We use a flag to indicate whether the parcel contains a valid object.
748 dest.writeInt(pendingIntent != null ? 1 : 0);
749 if (pendingIntent != null) {
750 pendingIntent.writeToParcel(dest, 0 /* no flags */);
751 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800752 }
Adam Cohenca6fd842010-09-03 18:10:35 -0700753
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800754 @Override
Dianne Hackborna1940212012-06-28 16:07:22 -0700755 public void apply(View root, ViewGroup rootParent, final OnClickHandler handler) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800756 final View target = root.findViewById(viewId);
Joe Onorato2b69ce42010-10-31 11:35:41 -0700757 if (target == null) return;
Adam Cohenca6fd842010-09-03 18:10:35 -0700758
759 // If the view is an AdapterView, setting a PendingIntent on click doesn't make much
760 // sense, do they mean to set a PendingIntent template for the AdapterView's children?
761 if (mIsWidgetCollectionChild) {
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800762 Log.w(LOG_TAG, "Cannot setOnClickPendingIntent for collection item " +
Adam Cohenc6151f22012-02-02 21:02:31 -0800763 "(id: " + viewId + ")");
Adam Cohenffc46a52012-04-30 19:54:39 -0700764 ApplicationInfo appInfo = root.getContext().getApplicationInfo();
765
766 // We let this slide for HC and ICS so as to not break compatibility. It should have
767 // been disabled from the outset, but was left open by accident.
768 if (appInfo != null &&
769 appInfo.targetSdkVersion >= Build.VERSION_CODES.JELLY_BEAN) {
770 return;
771 }
Adam Cohenca6fd842010-09-03 18:10:35 -0700772 }
773
Romain Guye4d4e202013-07-22 13:02:02 -0700774 // If the pendingIntent is null, we clear the onClickListener
775 OnClickListener listener = null;
776 if (pendingIntent != null) {
777 listener = new OnClickListener() {
778 public void onClick(View v) {
779 // Find target view location in screen coordinates and
780 // fill into PendingIntent before sending.
781 final Rect rect = getSourceBounds(v);
Jim Millere667a7a2012-08-09 19:22:32 -0700782
Romain Guye4d4e202013-07-22 13:02:02 -0700783 final Intent intent = new Intent();
784 intent.setSourceBounds(rect);
785 handler.onClickHandler(v, pendingIntent, intent);
786 }
787 };
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800788 }
Romain Guye4d4e202013-07-22 13:02:02 -0700789 target.setOnClickListener(listener);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800790 }
Adam Cohenc6151f22012-02-02 21:02:31 -0800791
Adam Cohenfbe44b72012-09-19 20:36:23 -0700792 public String getActionName() {
793 return "SetOnClickPendingIntent";
794 }
795
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800796 PendingIntent pendingIntent;
797
798 public final static int TAG = 1;
799 }
800
Romain Guye4d4e202013-07-22 13:02:02 -0700801 private static Rect getSourceBounds(View v) {
802 final float appScale = v.getContext().getResources()
803 .getCompatibilityInfo().applicationScale;
804 final int[] pos = new int[2];
805 v.getLocationOnScreen(pos);
806
807 final Rect rect = new Rect();
808 rect.left = (int) (pos[0] * appScale + 0.5f);
809 rect.top = (int) (pos[1] * appScale + 0.5f);
810 rect.right = (int) ((pos[0] + v.getWidth()) * appScale + 0.5f);
811 rect.bottom = (int) ((pos[1] + v.getHeight()) * appScale + 0.5f);
812 return rect;
813 }
814
Romain Guy484f4d62013-07-22 16:39:16 -0700815 private Method getMethod(View view, String methodName, Class<?> paramType) {
Romain Guye4d4e202013-07-22 13:02:02 -0700816 Method method;
817 Class<? extends View> klass = view.getClass();
818
819 synchronized (sMethodsLock) {
Romain Guy484f4d62013-07-22 16:39:16 -0700820 ArrayMap<MutablePair<String, Class<?>>, Method> methods = sMethods.get(klass);
Romain Guye4d4e202013-07-22 13:02:02 -0700821 if (methods == null) {
Romain Guy484f4d62013-07-22 16:39:16 -0700822 methods = new ArrayMap<MutablePair<String, Class<?>>, Method>();
Romain Guye4d4e202013-07-22 13:02:02 -0700823 sMethods.put(klass, methods);
824 }
825
Romain Guy484f4d62013-07-22 16:39:16 -0700826 mPair.first = methodName;
827 mPair.second = paramType;
828
829 method = methods.get(mPair);
Romain Guye4d4e202013-07-22 13:02:02 -0700830 if (method == null) {
831 try {
Romain Guy9870e5c2013-07-23 13:09:51 -0700832 if (paramType == null) {
833 method = klass.getMethod(methodName);
834 } else {
835 method = klass.getMethod(methodName, paramType);
836 }
Romain Guye4d4e202013-07-22 13:02:02 -0700837 } catch (NoSuchMethodException ex) {
838 throw new ActionException("view: " + klass.getName() + " doesn't have method: "
839 + methodName + getParameters(paramType));
840 }
841
842 if (!method.isAnnotationPresent(RemotableViewMethod.class)) {
843 throw new ActionException("view: " + klass.getName()
844 + " can't use method with RemoteViews: "
845 + methodName + getParameters(paramType));
846 }
847
Romain Guy484f4d62013-07-22 16:39:16 -0700848 methods.put(new MutablePair<String, Class<?>>(methodName, paramType), method);
Romain Guye4d4e202013-07-22 13:02:02 -0700849 }
850 }
851
852 return method;
853 }
854
Sunny Goyaldd292f42015-12-02 14:29:27 -0800855 /**
856 * @return the async implementation of the provided method.
857 */
858 private Method getAsyncMethod(Method method) {
859 synchronized (sAsyncMethods) {
860 int valueIndex = sAsyncMethods.indexOfKey(method);
861 if (valueIndex >= 0) {
862 return sAsyncMethods.valueAt(valueIndex);
863 }
864
865 RemotableViewMethod annotation = method.getAnnotation(RemotableViewMethod.class);
866 Method asyncMethod = null;
867 if (!annotation.asyncImpl().isEmpty()) {
868 try {
869 asyncMethod = method.getDeclaringClass()
870 .getMethod(annotation.asyncImpl(), method.getParameterTypes());
871 if (!asyncMethod.getReturnType().equals(Runnable.class)) {
872 throw new ActionException("Async implementation for " + method.getName() +
873 " does not return a Runnable");
874 }
875 } catch (NoSuchMethodException ex) {
876 throw new ActionException("Async implementation declared but not defined for " +
877 method.getName());
878 }
879 }
880 sAsyncMethods.put(method, asyncMethod);
881 return asyncMethod;
882 }
883 }
884
Romain Guye4d4e202013-07-22 13:02:02 -0700885 private static String getParameters(Class<?> paramType) {
886 if (paramType == null) return "()";
887 return "(" + paramType + ")";
888 }
889
890 private static Object[] wrapArg(Object value) {
891 Object[] args = sInvokeArgsTls.get();
892 args[0] = value;
893 return args;
894 }
895
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800896 /**
897 * Equivalent to calling a combination of {@link Drawable#setAlpha(int)},
898 * {@link Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)},
899 * and/or {@link Drawable#setLevel(int)} on the {@link Drawable} of a given view.
900 * <p>
901 * These operations will be performed on the {@link Drawable} returned by the
902 * target {@link View#getBackground()} by default. If targetBackground is false,
903 * we assume the target is an {@link ImageView} and try applying the operations
904 * to {@link ImageView#getDrawable()}.
905 * <p>
906 * You can omit specific calls by marking their values with null or -1.
907 */
908 private class SetDrawableParameters extends Action {
909 public SetDrawableParameters(int id, boolean targetBackground, int alpha,
910 int colorFilter, PorterDuff.Mode mode, int level) {
911 this.viewId = id;
912 this.targetBackground = targetBackground;
913 this.alpha = alpha;
914 this.colorFilter = colorFilter;
915 this.filterMode = mode;
916 this.level = level;
917 }
Jim Millere667a7a2012-08-09 19:22:32 -0700918
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800919 public SetDrawableParameters(Parcel parcel) {
920 viewId = parcel.readInt();
921 targetBackground = parcel.readInt() != 0;
922 alpha = parcel.readInt();
923 colorFilter = parcel.readInt();
924 boolean hasMode = parcel.readInt() != 0;
925 if (hasMode) {
926 filterMode = PorterDuff.Mode.valueOf(parcel.readString());
927 } else {
928 filterMode = null;
929 }
930 level = parcel.readInt();
931 }
Jim Millere667a7a2012-08-09 19:22:32 -0700932
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800933 public void writeToParcel(Parcel dest, int flags) {
934 dest.writeInt(TAG);
935 dest.writeInt(viewId);
936 dest.writeInt(targetBackground ? 1 : 0);
937 dest.writeInt(alpha);
938 dest.writeInt(colorFilter);
939 if (filterMode != null) {
940 dest.writeInt(1);
941 dest.writeString(filterMode.toString());
942 } else {
943 dest.writeInt(0);
944 }
945 dest.writeInt(level);
946 }
Jim Millere667a7a2012-08-09 19:22:32 -0700947
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800948 @Override
Dianne Hackborna1940212012-06-28 16:07:22 -0700949 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800950 final View target = root.findViewById(viewId);
Joe Onorato2b69ce42010-10-31 11:35:41 -0700951 if (target == null) return;
Jim Millere667a7a2012-08-09 19:22:32 -0700952
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800953 // Pick the correct drawable to modify for this view
954 Drawable targetDrawable = null;
955 if (targetBackground) {
956 targetDrawable = target.getBackground();
957 } else if (target instanceof ImageView) {
958 ImageView imageView = (ImageView) target;
959 targetDrawable = imageView.getDrawable();
960 }
Jim Millere667a7a2012-08-09 19:22:32 -0700961
Romain Guya5475592009-07-01 17:20:08 -0700962 if (targetDrawable != null) {
963 // Perform modifications only if values are set correctly
964 if (alpha != -1) {
Jorim Jaggi03a7c4c2015-06-01 16:25:39 -0700965 targetDrawable.mutate().setAlpha(alpha);
Romain Guya5475592009-07-01 17:20:08 -0700966 }
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +0200967 if (filterMode != null) {
Jorim Jaggi03a7c4c2015-06-01 16:25:39 -0700968 targetDrawable.mutate().setColorFilter(colorFilter, filterMode);
Romain Guya5475592009-07-01 17:20:08 -0700969 }
970 if (level != -1) {
Jorim Jaggi03a7c4c2015-06-01 16:25:39 -0700971 targetDrawable.mutate().setLevel(level);
Romain Guya5475592009-07-01 17:20:08 -0700972 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800973 }
974 }
Winson Chung3ec9a452010-09-23 16:40:28 -0700975
Adam Cohenfbe44b72012-09-19 20:36:23 -0700976 public String getActionName() {
977 return "SetDrawableParameters";
978 }
979
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800980 boolean targetBackground;
981 int alpha;
982 int colorFilter;
983 PorterDuff.Mode filterMode;
984 int level;
985
986 public final static int TAG = 3;
987 }
Jim Millere667a7a2012-08-09 19:22:32 -0700988
Romain Guy484f4d62013-07-22 16:39:16 -0700989 private final class ReflectionActionWithoutParams extends Action {
990 final String methodName;
Adam Cohen2dd21972010-08-15 18:20:04 -0700991
992 public final static int TAG = 5;
993
994 ReflectionActionWithoutParams(int viewId, String methodName) {
995 this.viewId = viewId;
996 this.methodName = methodName;
997 }
998
999 ReflectionActionWithoutParams(Parcel in) {
1000 this.viewId = in.readInt();
1001 this.methodName = in.readString();
1002 }
1003
1004 public void writeToParcel(Parcel out, int flags) {
1005 out.writeInt(TAG);
1006 out.writeInt(this.viewId);
1007 out.writeString(this.methodName);
1008 }
1009
1010 @Override
Dianne Hackborna1940212012-06-28 16:07:22 -07001011 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
Adam Cohen2dd21972010-08-15 18:20:04 -07001012 final View view = root.findViewById(viewId);
Joe Onorato2b69ce42010-10-31 11:35:41 -07001013 if (view == null) return;
Adam Cohen2dd21972010-08-15 18:20:04 -07001014
Adam Cohen2dd21972010-08-15 18:20:04 -07001015 try {
Romain Guye4d4e202013-07-22 13:02:02 -07001016 getMethod(view, this.methodName, null).invoke(view);
1017 } catch (ActionException e) {
1018 throw e;
Adam Cohen2dd21972010-08-15 18:20:04 -07001019 } catch (Exception ex) {
1020 throw new ActionException(ex);
1021 }
1022 }
Adam Cohenfbe44b72012-09-19 20:36:23 -07001023
1024 public int mergeBehavior() {
1025 // we don't need to build up showNext or showPrevious calls
1026 if (methodName.equals("showNext") || methodName.equals("showPrevious")) {
1027 return MERGE_IGNORE;
1028 } else {
1029 return MERGE_REPLACE;
1030 }
1031 }
1032
1033 public String getActionName() {
1034 return "ReflectionActionWithoutParams";
1035 }
Adam Cohen2dd21972010-08-15 18:20:04 -07001036 }
1037
Adam Cohen5d200642012-04-24 10:43:31 -07001038 private static class BitmapCache {
1039 ArrayList<Bitmap> mBitmaps;
1040
1041 public BitmapCache() {
1042 mBitmaps = new ArrayList<Bitmap>();
1043 }
1044
1045 public BitmapCache(Parcel source) {
1046 int count = source.readInt();
1047 mBitmaps = new ArrayList<Bitmap>();
1048 for (int i = 0; i < count; i++) {
1049 Bitmap b = Bitmap.CREATOR.createFromParcel(source);
1050 mBitmaps.add(b);
1051 }
1052 }
1053
1054 public int getBitmapId(Bitmap b) {
1055 if (b == null) {
1056 return -1;
1057 } else {
1058 if (mBitmaps.contains(b)) {
1059 return mBitmaps.indexOf(b);
1060 } else {
1061 mBitmaps.add(b);
1062 return (mBitmaps.size() - 1);
1063 }
1064 }
1065 }
1066
1067 public Bitmap getBitmapForId(int id) {
1068 if (id == -1 || id >= mBitmaps.size()) {
1069 return null;
1070 } else {
1071 return mBitmaps.get(id);
1072 }
1073 }
1074
1075 public void writeBitmapsToParcel(Parcel dest, int flags) {
1076 int count = mBitmaps.size();
1077 dest.writeInt(count);
1078 for (int i = 0; i < count; i++) {
1079 mBitmaps.get(i).writeToParcel(dest, flags);
1080 }
1081 }
1082
1083 public void assimilate(BitmapCache bitmapCache) {
1084 ArrayList<Bitmap> bitmapsToBeAdded = bitmapCache.mBitmaps;
1085 int count = bitmapsToBeAdded.size();
1086 for (int i = 0; i < count; i++) {
1087 Bitmap b = bitmapsToBeAdded.get(i);
1088 if (!mBitmaps.contains(b)) {
1089 mBitmaps.add(b);
1090 }
1091 }
1092 }
1093
1094 public void addBitmapMemory(MemoryUsageCounter memoryCounter) {
1095 for (int i = 0; i < mBitmaps.size(); i++) {
1096 memoryCounter.addBitmapMemory(mBitmaps.get(i));
1097 }
1098 }
1099 }
1100
1101 private class BitmapReflectionAction extends Action {
1102 int bitmapId;
Adam Cohen5d200642012-04-24 10:43:31 -07001103 Bitmap bitmap;
1104 String methodName;
1105
1106 BitmapReflectionAction(int viewId, String methodName, Bitmap bitmap) {
1107 this.bitmap = bitmap;
1108 this.viewId = viewId;
1109 this.methodName = methodName;
1110 bitmapId = mBitmapCache.getBitmapId(bitmap);
1111 }
1112
1113 BitmapReflectionAction(Parcel in) {
1114 viewId = in.readInt();
1115 methodName = in.readString();
1116 bitmapId = in.readInt();
1117 bitmap = mBitmapCache.getBitmapForId(bitmapId);
1118 }
1119
1120 @Override
1121 public void writeToParcel(Parcel dest, int flags) {
1122 dest.writeInt(TAG);
1123 dest.writeInt(viewId);
1124 dest.writeString(methodName);
1125 dest.writeInt(bitmapId);
1126 }
1127
1128 @Override
Dianne Hackborna1940212012-06-28 16:07:22 -07001129 public void apply(View root, ViewGroup rootParent,
1130 OnClickHandler handler) throws ActionException {
Adam Cohen5d200642012-04-24 10:43:31 -07001131 ReflectionAction ra = new ReflectionAction(viewId, methodName, ReflectionAction.BITMAP,
1132 bitmap);
Dianne Hackborna1940212012-06-28 16:07:22 -07001133 ra.apply(root, rootParent, handler);
Adam Cohen5d200642012-04-24 10:43:31 -07001134 }
1135
1136 @Override
1137 public void setBitmapCache(BitmapCache bitmapCache) {
1138 bitmapId = bitmapCache.getBitmapId(bitmap);
1139 }
1140
Adam Cohenfbe44b72012-09-19 20:36:23 -07001141 public String getActionName() {
1142 return "BitmapReflectionAction";
1143 }
1144
Adam Cohen5d200642012-04-24 10:43:31 -07001145 public final static int TAG = 12;
1146 }
1147
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001148 /**
1149 * Base class for the reflection actions.
1150 */
Romain Guy484f4d62013-07-22 16:39:16 -07001151 private final class ReflectionAction extends Action {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001152 static final int TAG = 2;
1153
1154 static final int BOOLEAN = 1;
1155 static final int BYTE = 2;
1156 static final int SHORT = 3;
1157 static final int INT = 4;
1158 static final int LONG = 5;
1159 static final int FLOAT = 6;
1160 static final int DOUBLE = 7;
1161 static final int CHAR = 8;
1162 static final int STRING = 9;
1163 static final int CHAR_SEQUENCE = 10;
1164 static final int URI = 11;
Adam Cohenfbe44b72012-09-19 20:36:23 -07001165 // BITMAP actions are never stored in the list of actions. They are only used locally
1166 // to implement BitmapReflectionAction, which eliminates duplicates using BitmapCache.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001167 static final int BITMAP = 12;
Bjorn Bringertd755b062010-01-06 17:15:37 +00001168 static final int BUNDLE = 13;
Winson Chung499cb9f2010-07-16 11:18:17 -07001169 static final int INTENT = 14;
Jorim Jaggief72a192014-08-26 21:57:46 +02001170 static final int COLOR_STATE_LIST = 15;
Dan Sandlera22a3802015-05-13 00:12:47 -04001171 static final int ICON = 16;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001172
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001173 String methodName;
1174 int type;
1175 Object value;
1176
1177 ReflectionAction(int viewId, String methodName, int type, Object value) {
1178 this.viewId = viewId;
1179 this.methodName = methodName;
1180 this.type = type;
1181 this.value = value;
1182 }
1183
1184 ReflectionAction(Parcel in) {
1185 this.viewId = in.readInt();
1186 this.methodName = in.readString();
1187 this.type = in.readInt();
Romain Guya5475592009-07-01 17:20:08 -07001188 //noinspection ConstantIfStatement
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001189 if (false) {
Adam Cohen50f3d1b2012-12-11 18:36:07 -08001190 Log.d(LOG_TAG, "read viewId=0x" + Integer.toHexString(this.viewId)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001191 + " methodName=" + this.methodName + " type=" + this.type);
1192 }
Adam Cohenc6151f22012-02-02 21:02:31 -08001193
1194 // For some values that may have been null, we first check a flag to see if they were
1195 // written to the parcel.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001196 switch (this.type) {
1197 case BOOLEAN:
1198 this.value = in.readInt() != 0;
1199 break;
1200 case BYTE:
1201 this.value = in.readByte();
1202 break;
1203 case SHORT:
1204 this.value = (short)in.readInt();
1205 break;
1206 case INT:
1207 this.value = in.readInt();
1208 break;
1209 case LONG:
1210 this.value = in.readLong();
1211 break;
1212 case FLOAT:
1213 this.value = in.readFloat();
1214 break;
1215 case DOUBLE:
1216 this.value = in.readDouble();
1217 break;
1218 case CHAR:
1219 this.value = (char)in.readInt();
1220 break;
1221 case STRING:
1222 this.value = in.readString();
1223 break;
1224 case CHAR_SEQUENCE:
1225 this.value = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
1226 break;
1227 case URI:
Adam Cohenc6151f22012-02-02 21:02:31 -08001228 if (in.readInt() != 0) {
1229 this.value = Uri.CREATOR.createFromParcel(in);
1230 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001231 break;
1232 case BITMAP:
Adam Cohenc6151f22012-02-02 21:02:31 -08001233 if (in.readInt() != 0) {
1234 this.value = Bitmap.CREATOR.createFromParcel(in);
1235 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001236 break;
Bjorn Bringertd755b062010-01-06 17:15:37 +00001237 case BUNDLE:
1238 this.value = in.readBundle();
1239 break;
Winson Chung499cb9f2010-07-16 11:18:17 -07001240 case INTENT:
Adam Cohenc6151f22012-02-02 21:02:31 -08001241 if (in.readInt() != 0) {
1242 this.value = Intent.CREATOR.createFromParcel(in);
1243 }
Winson Chung499cb9f2010-07-16 11:18:17 -07001244 break;
Jorim Jaggief72a192014-08-26 21:57:46 +02001245 case COLOR_STATE_LIST:
1246 if (in.readInt() != 0) {
1247 this.value = ColorStateList.CREATOR.createFromParcel(in);
1248 }
1249 break;
Dan Sandlera22a3802015-05-13 00:12:47 -04001250 case ICON:
1251 if (in.readInt() != 0) {
1252 this.value = Icon.CREATOR.createFromParcel(in);
1253 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001254 default:
1255 break;
1256 }
1257 }
1258
1259 public void writeToParcel(Parcel out, int flags) {
1260 out.writeInt(TAG);
1261 out.writeInt(this.viewId);
1262 out.writeString(this.methodName);
1263 out.writeInt(this.type);
Romain Guya5475592009-07-01 17:20:08 -07001264 //noinspection ConstantIfStatement
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001265 if (false) {
Adam Cohen50f3d1b2012-12-11 18:36:07 -08001266 Log.d(LOG_TAG, "write viewId=0x" + Integer.toHexString(this.viewId)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001267 + " methodName=" + this.methodName + " type=" + this.type);
1268 }
Adam Cohenc6151f22012-02-02 21:02:31 -08001269
1270 // For some values which are null, we record an integer flag to indicate whether
1271 // we have written a valid value to the parcel.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001272 switch (this.type) {
1273 case BOOLEAN:
Romain Guya5475592009-07-01 17:20:08 -07001274 out.writeInt((Boolean) this.value ? 1 : 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001275 break;
1276 case BYTE:
Romain Guya5475592009-07-01 17:20:08 -07001277 out.writeByte((Byte) this.value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001278 break;
1279 case SHORT:
Romain Guya5475592009-07-01 17:20:08 -07001280 out.writeInt((Short) this.value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001281 break;
1282 case INT:
Romain Guya5475592009-07-01 17:20:08 -07001283 out.writeInt((Integer) this.value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001284 break;
1285 case LONG:
Romain Guya5475592009-07-01 17:20:08 -07001286 out.writeLong((Long) this.value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001287 break;
1288 case FLOAT:
Romain Guya5475592009-07-01 17:20:08 -07001289 out.writeFloat((Float) this.value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001290 break;
1291 case DOUBLE:
Romain Guya5475592009-07-01 17:20:08 -07001292 out.writeDouble((Double) this.value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001293 break;
1294 case CHAR:
1295 out.writeInt((int)((Character)this.value).charValue());
1296 break;
1297 case STRING:
1298 out.writeString((String)this.value);
1299 break;
1300 case CHAR_SEQUENCE:
Jim Millere667a7a2012-08-09 19:22:32 -07001301 TextUtils.writeToParcel((CharSequence)this.value, out, flags);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001302 break;
1303 case URI:
Adam Cohenc6151f22012-02-02 21:02:31 -08001304 out.writeInt(this.value != null ? 1 : 0);
1305 if (this.value != null) {
1306 ((Uri)this.value).writeToParcel(out, flags);
1307 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001308 break;
1309 case BITMAP:
Adam Cohenc6151f22012-02-02 21:02:31 -08001310 out.writeInt(this.value != null ? 1 : 0);
1311 if (this.value != null) {
1312 ((Bitmap)this.value).writeToParcel(out, flags);
1313 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001314 break;
Bjorn Bringertd755b062010-01-06 17:15:37 +00001315 case BUNDLE:
1316 out.writeBundle((Bundle) this.value);
1317 break;
Winson Chung499cb9f2010-07-16 11:18:17 -07001318 case INTENT:
Adam Cohenc6151f22012-02-02 21:02:31 -08001319 out.writeInt(this.value != null ? 1 : 0);
1320 if (this.value != null) {
1321 ((Intent)this.value).writeToParcel(out, flags);
1322 }
Winson Chung499cb9f2010-07-16 11:18:17 -07001323 break;
Jorim Jaggief72a192014-08-26 21:57:46 +02001324 case COLOR_STATE_LIST:
1325 out.writeInt(this.value != null ? 1 : 0);
1326 if (this.value != null) {
1327 ((ColorStateList)this.value).writeToParcel(out, flags);
1328 }
Dan Sandlera22a3802015-05-13 00:12:47 -04001329 break;
1330 case ICON:
1331 out.writeInt(this.value != null ? 1 : 0);
1332 if (this.value != null) {
1333 ((Icon)this.value).writeToParcel(out, flags);
1334 }
1335 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001336 default:
1337 break;
1338 }
1339 }
1340
Romain Guye4d4e202013-07-22 13:02:02 -07001341 private Class<?> getParameterType() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001342 switch (this.type) {
1343 case BOOLEAN:
1344 return boolean.class;
1345 case BYTE:
1346 return byte.class;
1347 case SHORT:
1348 return short.class;
1349 case INT:
1350 return int.class;
1351 case LONG:
1352 return long.class;
1353 case FLOAT:
1354 return float.class;
1355 case DOUBLE:
1356 return double.class;
1357 case CHAR:
1358 return char.class;
1359 case STRING:
1360 return String.class;
1361 case CHAR_SEQUENCE:
1362 return CharSequence.class;
1363 case URI:
1364 return Uri.class;
1365 case BITMAP:
1366 return Bitmap.class;
Bjorn Bringertd755b062010-01-06 17:15:37 +00001367 case BUNDLE:
1368 return Bundle.class;
Winson Chung499cb9f2010-07-16 11:18:17 -07001369 case INTENT:
1370 return Intent.class;
Jorim Jaggief72a192014-08-26 21:57:46 +02001371 case COLOR_STATE_LIST:
1372 return ColorStateList.class;
Dan Sandlera22a3802015-05-13 00:12:47 -04001373 case ICON:
1374 return Icon.class;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001375 default:
1376 return null;
1377 }
1378 }
1379
1380 @Override
Dianne Hackborna1940212012-06-28 16:07:22 -07001381 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001382 final View view = root.findViewById(viewId);
Joe Onorato2b69ce42010-10-31 11:35:41 -07001383 if (view == null) return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001384
Romain Guye4d4e202013-07-22 13:02:02 -07001385 Class<?> param = getParameterType();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001386 if (param == null) {
1387 throw new ActionException("bad type: " + this.type);
1388 }
1389
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001390 try {
Romain Guye4d4e202013-07-22 13:02:02 -07001391 getMethod(view, this.methodName, param).invoke(view, wrapArg(this.value));
1392 } catch (ActionException e) {
1393 throw e;
1394 } catch (Exception ex) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001395 throw new ActionException(ex);
1396 }
1397 }
Winson Chung3ec9a452010-09-23 16:40:28 -07001398
Sunny Goyaldd292f42015-12-02 14:29:27 -08001399 @Override
1400 public Action initActionAsync(ViewTree root, ViewGroup rootParent, OnClickHandler handler) {
1401 final View view = root.findViewById(viewId);
1402 if (view == null) return ACTION_NOOP;
1403
1404 Class<?> param = getParameterType();
1405 if (param == null) {
1406 throw new ActionException("bad type: " + this.type);
1407 }
1408
1409 try {
1410 Method method = getMethod(view, this.methodName, param);
1411 Method asyncMethod = getAsyncMethod(method);
1412
1413 if (asyncMethod != null) {
1414 Runnable endAction = (Runnable) asyncMethod.invoke(view, wrapArg(this.value));
1415 if (endAction == null) {
1416 return ACTION_NOOP;
1417 } else {
1418 return new RunnableAction(endAction);
1419 }
1420 }
1421 } catch (ActionException e) {
1422 throw e;
1423 } catch (Exception ex) {
1424 throw new ActionException(ex);
1425 }
1426
1427 return this;
1428 }
1429
Adam Cohenfbe44b72012-09-19 20:36:23 -07001430 public int mergeBehavior() {
1431 // smoothScrollBy is cumulative, everything else overwites.
1432 if (methodName.equals("smoothScrollBy")) {
1433 return MERGE_APPEND;
1434 } else {
1435 return MERGE_REPLACE;
Winson Chung3ec9a452010-09-23 16:40:28 -07001436 }
1437 }
Adam Cohenfbe44b72012-09-19 20:36:23 -07001438
1439 public String getActionName() {
1440 // Each type of reflection action corresponds to a setter, so each should be seen as
1441 // unique from the standpoint of merging.
1442 return "ReflectionAction" + this.methodName + this.type;
1443 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001444 }
1445
Sunny Goyaldd292f42015-12-02 14:29:27 -08001446 /**
1447 * This is only used for async execution of actions and it not parcelable.
1448 */
1449 private static final class RunnableAction extends RuntimeAction {
1450 private final Runnable mRunnable;
1451
1452 RunnableAction(Runnable r) {
1453 mRunnable = r;
1454 }
1455
1456 @Override
1457 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
1458 mRunnable.run();
1459 }
1460 }
1461
Adam Cohen5d200642012-04-24 10:43:31 -07001462 private void configureRemoteViewsAsChild(RemoteViews rv) {
1463 mBitmapCache.assimilate(rv.mBitmapCache);
1464 rv.setBitmapCache(mBitmapCache);
1465 rv.setNotRoot();
1466 }
1467
1468 void setNotRoot() {
1469 mIsRoot = false;
1470 }
1471
Jeff Sharkey1162fd72009-11-04 17:58:08 -08001472 /**
1473 * Equivalent to calling {@link ViewGroup#addView(View)} after inflating the
1474 * given {@link RemoteViews}, or calling {@link ViewGroup#removeAllViews()}
1475 * when null. This allows users to build "nested" {@link RemoteViews}.
1476 */
1477 private class ViewGroupAction extends Action {
1478 public ViewGroupAction(int viewId, RemoteViews nestedViews) {
1479 this.viewId = viewId;
1480 this.nestedViews = nestedViews;
Adam Cohenc431c152012-04-26 18:42:17 -07001481 if (nestedViews != null) {
1482 configureRemoteViewsAsChild(nestedViews);
1483 }
Jeff Sharkey1162fd72009-11-04 17:58:08 -08001484 }
1485
Adam Cohen5d200642012-04-24 10:43:31 -07001486 public ViewGroupAction(Parcel parcel, BitmapCache bitmapCache) {
Jeff Sharkey1162fd72009-11-04 17:58:08 -08001487 viewId = parcel.readInt();
Adam Cohen5d200642012-04-24 10:43:31 -07001488 boolean nestedViewsNull = parcel.readInt() == 0;
1489 if (!nestedViewsNull) {
1490 nestedViews = new RemoteViews(parcel, bitmapCache);
1491 } else {
1492 nestedViews = null;
1493 }
Jeff Sharkey1162fd72009-11-04 17:58:08 -08001494 }
1495
1496 public void writeToParcel(Parcel dest, int flags) {
1497 dest.writeInt(TAG);
1498 dest.writeInt(viewId);
Adam Cohen5d200642012-04-24 10:43:31 -07001499 if (nestedViews != null) {
1500 dest.writeInt(1);
1501 nestedViews.writeToParcel(dest, flags);
1502 } else {
1503 // signifies null
1504 dest.writeInt(0);
1505 }
Jeff Sharkey1162fd72009-11-04 17:58:08 -08001506 }
1507
1508 @Override
Dianne Hackborna1940212012-06-28 16:07:22 -07001509 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
Jeff Sharkey1162fd72009-11-04 17:58:08 -08001510 final Context context = root.getContext();
1511 final ViewGroup target = (ViewGroup) root.findViewById(viewId);
Joe Onorato2b69ce42010-10-31 11:35:41 -07001512 if (target == null) return;
Jeff Sharkey1162fd72009-11-04 17:58:08 -08001513 if (nestedViews != null) {
1514 // Inflate nested views and add as children
Dianne Hackborna1940212012-06-28 16:07:22 -07001515 target.addView(nestedViews.apply(context, target, handler));
Joe Onorato2b69ce42010-10-31 11:35:41 -07001516 } else {
Jeff Sharkey1162fd72009-11-04 17:58:08 -08001517 // Clear all children when nested views omitted
1518 target.removeAllViews();
1519 }
1520 }
1521
Winson Chung3ec9a452010-09-23 16:40:28 -07001522 @Override
Sunny Goyaldd292f42015-12-02 14:29:27 -08001523 public Action initActionAsync(ViewTree root, ViewGroup rootParent, OnClickHandler handler) {
1524 // In the async implementation, update the view tree so that subsequent calls to
1525 // findViewById return the currect view.
1526 root.createTree();
1527 ViewTree target = root.findViewTreeById(viewId);
1528 if ((target == null) || !(target.mRoot instanceof ViewGroup)) {
1529 return ACTION_NOOP;
1530 }
1531 if (nestedViews == null) {
1532 // Clear all children when nested views omitted
1533 target.mChildren = null;
1534 return this;
1535 } else {
1536 // Inflate nested views and perform all the async tasks for the child remoteView.
1537 final Context context = root.mRoot.getContext();
1538 final AsyncApplyTask task = nestedViews.getAsyncApplyTask(
1539 context, (ViewGroup) target.mRoot, null, handler);
1540 final ViewTree tree = task.doInBackground();
1541
1542 // Update the global view tree, so that next call to findViewTreeById
1543 // goes through the subtree as well.
1544 target.addChild(tree);
1545
1546 return new RuntimeAction() {
1547
1548 @Override
1549 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) throws ActionException {
1550 // This view will exist as we have already made sure
1551 final ViewGroup target = (ViewGroup) root.findViewById(viewId);
1552 task.onPostExecute(tree);
1553 target.addView(task.mResult);
1554 }
1555 };
1556 }
1557 }
1558
1559 @Override
Winson Chung3ec9a452010-09-23 16:40:28 -07001560 public void updateMemoryUsageEstimate(MemoryUsageCounter counter) {
1561 if (nestedViews != null) {
Adam Cohen5d200642012-04-24 10:43:31 -07001562 counter.increment(nestedViews.estimateMemoryUsage());
1563 }
1564 }
1565
1566 @Override
1567 public void setBitmapCache(BitmapCache bitmapCache) {
1568 if (nestedViews != null) {
1569 nestedViews.setBitmapCache(bitmapCache);
Winson Chung3ec9a452010-09-23 16:40:28 -07001570 }
1571 }
1572
Adam Cohenfbe44b72012-09-19 20:36:23 -07001573 public String getActionName() {
Romain Guye4d4e202013-07-22 13:02:02 -07001574 return "ViewGroupAction" + (nestedViews == null ? "Remove" : "Add");
Adam Cohenfbe44b72012-09-19 20:36:23 -07001575 }
1576
1577 public int mergeBehavior() {
1578 return MERGE_APPEND;
1579 }
1580
Jeff Sharkey1162fd72009-11-04 17:58:08 -08001581 RemoteViews nestedViews;
1582
1583 public final static int TAG = 4;
1584 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001585
1586 /**
Daniel Sandler820ba322012-03-23 16:36:00 -05001587 * Helper action to set compound drawables on a TextView. Supports relative
1588 * (s/t/e/b) or cardinal (l/t/r/b) arrangement.
1589 */
1590 private class TextViewDrawableAction extends Action {
1591 public TextViewDrawableAction(int viewId, boolean isRelative, int d1, int d2, int d3, int d4) {
1592 this.viewId = viewId;
1593 this.isRelative = isRelative;
Dan Sandler912282e2015-07-28 22:49:30 -04001594 this.useIcons = false;
Daniel Sandler820ba322012-03-23 16:36:00 -05001595 this.d1 = d1;
1596 this.d2 = d2;
1597 this.d3 = d3;
1598 this.d4 = d4;
1599 }
1600
Dan Sandler912282e2015-07-28 22:49:30 -04001601 public TextViewDrawableAction(int viewId, boolean isRelative,
1602 Icon i1, Icon i2, Icon i3, Icon i4) {
1603 this.viewId = viewId;
1604 this.isRelative = isRelative;
1605 this.useIcons = true;
1606 this.i1 = i1;
1607 this.i2 = i2;
1608 this.i3 = i3;
1609 this.i4 = i4;
1610 }
1611
Daniel Sandler820ba322012-03-23 16:36:00 -05001612 public TextViewDrawableAction(Parcel parcel) {
1613 viewId = parcel.readInt();
1614 isRelative = (parcel.readInt() != 0);
Dan Sandler912282e2015-07-28 22:49:30 -04001615 useIcons = (parcel.readInt() != 0);
1616 if (useIcons) {
1617 if (parcel.readInt() != 0) {
1618 i1 = Icon.CREATOR.createFromParcel(parcel);
1619 }
1620 if (parcel.readInt() != 0) {
1621 i2 = Icon.CREATOR.createFromParcel(parcel);
1622 }
1623 if (parcel.readInt() != 0) {
1624 i3 = Icon.CREATOR.createFromParcel(parcel);
1625 }
1626 if (parcel.readInt() != 0) {
1627 i4 = Icon.CREATOR.createFromParcel(parcel);
1628 }
1629 } else {
1630 d1 = parcel.readInt();
1631 d2 = parcel.readInt();
1632 d3 = parcel.readInt();
1633 d4 = parcel.readInt();
1634 }
Daniel Sandler820ba322012-03-23 16:36:00 -05001635 }
1636
1637 public void writeToParcel(Parcel dest, int flags) {
1638 dest.writeInt(TAG);
1639 dest.writeInt(viewId);
1640 dest.writeInt(isRelative ? 1 : 0);
Dan Sandler912282e2015-07-28 22:49:30 -04001641 dest.writeInt(useIcons ? 1 : 0);
1642 if (useIcons) {
1643 if (i1 != null) {
1644 dest.writeInt(1);
1645 i1.writeToParcel(dest, 0);
1646 } else {
1647 dest.writeInt(0);
1648 }
1649 if (i2 != null) {
1650 dest.writeInt(1);
1651 i2.writeToParcel(dest, 0);
1652 } else {
1653 dest.writeInt(0);
1654 }
1655 if (i3 != null) {
1656 dest.writeInt(1);
1657 i3.writeToParcel(dest, 0);
1658 } else {
1659 dest.writeInt(0);
1660 }
1661 if (i4 != null) {
1662 dest.writeInt(1);
1663 i4.writeToParcel(dest, 0);
1664 } else {
1665 dest.writeInt(0);
1666 }
1667 } else {
1668 dest.writeInt(d1);
1669 dest.writeInt(d2);
1670 dest.writeInt(d3);
1671 dest.writeInt(d4);
1672 }
Daniel Sandler820ba322012-03-23 16:36:00 -05001673 }
1674
1675 @Override
Dianne Hackborna1940212012-06-28 16:07:22 -07001676 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
Daniel Sandler820ba322012-03-23 16:36:00 -05001677 final TextView target = (TextView) root.findViewById(viewId);
1678 if (target == null) return;
Sunny Goyaldd292f42015-12-02 14:29:27 -08001679 if (drawablesLoaded) {
1680 if (isRelative) {
1681 target.setCompoundDrawablesRelativeWithIntrinsicBounds(id1, id2, id3, id4);
1682 } else {
1683 target.setCompoundDrawablesWithIntrinsicBounds(id1, id2, id3, id4);
1684 }
1685 } else if (useIcons) {
Dan Sandler912282e2015-07-28 22:49:30 -04001686 final Context ctx = target.getContext();
1687 final Drawable id1 = i1 == null ? null : i1.loadDrawable(ctx);
1688 final Drawable id2 = i2 == null ? null : i2.loadDrawable(ctx);
1689 final Drawable id3 = i3 == null ? null : i3.loadDrawable(ctx);
1690 final Drawable id4 = i4 == null ? null : i4.loadDrawable(ctx);
1691 if (isRelative) {
1692 target.setCompoundDrawablesRelativeWithIntrinsicBounds(id1, id2, id3, id4);
1693 } else {
1694 target.setCompoundDrawablesWithIntrinsicBounds(id1, id2, id3, id4);
1695 }
Daniel Sandler820ba322012-03-23 16:36:00 -05001696 } else {
Dan Sandler912282e2015-07-28 22:49:30 -04001697 if (isRelative) {
1698 target.setCompoundDrawablesRelativeWithIntrinsicBounds(d1, d2, d3, d4);
1699 } else {
1700 target.setCompoundDrawablesWithIntrinsicBounds(d1, d2, d3, d4);
1701 }
Daniel Sandler820ba322012-03-23 16:36:00 -05001702 }
1703 }
1704
Sunny Goyaldd292f42015-12-02 14:29:27 -08001705 @Override
1706 public Action initActionAsync(ViewTree root, ViewGroup rootParent, OnClickHandler handler) {
1707 final TextView target = (TextView) root.findViewById(viewId);
1708 if (target == null) return ACTION_NOOP;
1709
1710 TextViewDrawableAction copy = useIcons ?
1711 new TextViewDrawableAction(viewId, isRelative, i1, i2, i3, i4) :
1712 new TextViewDrawableAction(viewId, isRelative, d1, d2, d3, d4);
1713
1714 // Load the drawables on the background thread.
1715 copy.drawablesLoaded = true;
1716 final Context ctx = target.getContext();
1717
1718 if (useIcons) {
1719 copy.id1 = i1 == null ? null : i1.loadDrawable(ctx);
1720 copy.id2 = i2 == null ? null : i2.loadDrawable(ctx);
1721 copy.id3 = i3 == null ? null : i3.loadDrawable(ctx);
1722 copy.id4 = i4 == null ? null : i4.loadDrawable(ctx);
1723 } else {
1724 copy.id1 = d1 == 0 ? null : ctx.getDrawable(d1);
1725 copy.id2 = d2 == 0 ? null : ctx.getDrawable(d2);
1726 copy.id3 = d3 == 0 ? null : ctx.getDrawable(d3);
1727 copy.id4 = d4 == 0 ? null : ctx.getDrawable(d4);
1728 }
1729 return copy;
1730 }
1731
Adam Cohenfbe44b72012-09-19 20:36:23 -07001732 public String getActionName() {
1733 return "TextViewDrawableAction";
1734 }
1735
Daniel Sandler820ba322012-03-23 16:36:00 -05001736 boolean isRelative = false;
Dan Sandler912282e2015-07-28 22:49:30 -04001737 boolean useIcons = false;
Daniel Sandler820ba322012-03-23 16:36:00 -05001738 int d1, d2, d3, d4;
Dan Sandler912282e2015-07-28 22:49:30 -04001739 Icon i1, i2, i3, i4;
Daniel Sandler820ba322012-03-23 16:36:00 -05001740
Sunny Goyaldd292f42015-12-02 14:29:27 -08001741 boolean drawablesLoaded = false;
1742 Drawable id1, id2, id3, id4;
1743
Daniel Sandler820ba322012-03-23 16:36:00 -05001744 public final static int TAG = 11;
1745 }
1746
1747 /**
Daniel Sandler99d1f742012-05-21 16:14:14 -04001748 * Helper action to set text size on a TextView in any supported units.
Daniel Sandler7264f712012-05-21 14:48:23 -04001749 */
1750 private class TextViewSizeAction extends Action {
1751 public TextViewSizeAction(int viewId, int units, float size) {
1752 this.viewId = viewId;
1753 this.units = units;
1754 this.size = size;
1755 }
1756
1757 public TextViewSizeAction(Parcel parcel) {
1758 viewId = parcel.readInt();
1759 units = parcel.readInt();
1760 size = parcel.readFloat();
1761 }
1762
1763 public void writeToParcel(Parcel dest, int flags) {
1764 dest.writeInt(TAG);
1765 dest.writeInt(viewId);
1766 dest.writeInt(units);
1767 dest.writeFloat(size);
1768 }
1769
1770 @Override
Dianne Hackborna1940212012-06-28 16:07:22 -07001771 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
Daniel Sandler7264f712012-05-21 14:48:23 -04001772 final TextView target = (TextView) root.findViewById(viewId);
1773 if (target == null) return;
1774 target.setTextSize(units, size);
1775 }
1776
Adam Cohenfbe44b72012-09-19 20:36:23 -07001777 public String getActionName() {
1778 return "TextViewSizeAction";
1779 }
1780
Daniel Sandler7264f712012-05-21 14:48:23 -04001781 int units;
1782 float size;
1783
1784 public final static int TAG = 13;
1785 }
1786
1787 /**
Daniel Sandler99d1f742012-05-21 16:14:14 -04001788 * Helper action to set padding on a View.
1789 */
1790 private class ViewPaddingAction extends Action {
1791 public ViewPaddingAction(int viewId, int left, int top, int right, int bottom) {
1792 this.viewId = viewId;
1793 this.left = left;
1794 this.top = top;
1795 this.right = right;
1796 this.bottom = bottom;
1797 }
1798
1799 public ViewPaddingAction(Parcel parcel) {
1800 viewId = parcel.readInt();
1801 left = parcel.readInt();
1802 top = parcel.readInt();
1803 right = parcel.readInt();
1804 bottom = parcel.readInt();
1805 }
1806
1807 public void writeToParcel(Parcel dest, int flags) {
1808 dest.writeInt(TAG);
1809 dest.writeInt(viewId);
1810 dest.writeInt(left);
1811 dest.writeInt(top);
1812 dest.writeInt(right);
1813 dest.writeInt(bottom);
1814 }
1815
1816 @Override
Dianne Hackborna1940212012-06-28 16:07:22 -07001817 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
Daniel Sandler99d1f742012-05-21 16:14:14 -04001818 final View target = root.findViewById(viewId);
1819 if (target == null) return;
1820 target.setPadding(left, top, right, bottom);
1821 }
1822
Adam Cohenfbe44b72012-09-19 20:36:23 -07001823 public String getActionName() {
1824 return "ViewPaddingAction";
1825 }
1826
Daniel Sandler99d1f742012-05-21 16:14:14 -04001827 int left, top, right, bottom;
1828
1829 public final static int TAG = 14;
1830 }
1831
1832 /**
Adrian Roos9b123cf2016-02-04 14:55:57 -08001833 * Helper action to set layout params on a View.
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001834 */
Adrian Roos9b123cf2016-02-04 14:55:57 -08001835 private class LayoutParamAction extends Action {
1836
1837 /** Set marginEnd */
1838 public static final int LAYOUT_MARGIN_END = 1;
1839 /** Set width */
1840 public static final int LAYOUT_WIDTH = 2;
1841
1842 /**
1843 * @param viewId ID of the view alter
1844 * @param property which layout parameter to alter
1845 * @param value new value of the layout parameter
1846 */
1847 public LayoutParamAction(int viewId, int property, int value) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001848 this.viewId = viewId;
Adrian Roos9b123cf2016-02-04 14:55:57 -08001849 this.property = property;
1850 this.value = value;
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001851 }
1852
Adrian Roos9b123cf2016-02-04 14:55:57 -08001853 public LayoutParamAction(Parcel parcel) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001854 viewId = parcel.readInt();
Adrian Roos9b123cf2016-02-04 14:55:57 -08001855 property = parcel.readInt();
1856 value = parcel.readInt();
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001857 }
1858
1859 public void writeToParcel(Parcel dest, int flags) {
1860 dest.writeInt(TAG);
1861 dest.writeInt(viewId);
Adrian Roos9b123cf2016-02-04 14:55:57 -08001862 dest.writeInt(property);
1863 dest.writeInt(value);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001864 }
1865
1866 @Override
1867 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
1868 final View target = root.findViewById(viewId);
1869 if (target == null) {
1870 return;
1871 }
1872 ViewGroup.LayoutParams layoutParams = target.getLayoutParams();
Adrian Roos9b123cf2016-02-04 14:55:57 -08001873 if (layoutParams == null) {
1874 return;
1875 }
1876 switch (property) {
1877 case LAYOUT_MARGIN_END:
1878 if (layoutParams instanceof ViewGroup.MarginLayoutParams) {
1879 ((ViewGroup.MarginLayoutParams) layoutParams).setMarginEnd(value);
1880 target.setLayoutParams(layoutParams);
1881 }
1882 break;
1883 case LAYOUT_WIDTH:
1884 layoutParams.width = value;
1885 target.setLayoutParams(layoutParams);
1886 break;
1887 default:
1888 throw new IllegalArgumentException("Unknown property " + property);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001889 }
1890 }
1891
1892 public String getActionName() {
Adrian Roos9b123cf2016-02-04 14:55:57 -08001893 return "LayoutParamAction" + property + ".";
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001894 }
1895
Adrian Roos9b123cf2016-02-04 14:55:57 -08001896 int property;
1897 int value;
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001898
1899 public final static int TAG = 19;
1900 }
1901
1902 /**
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01001903 * Helper action to set a color filter on a compound drawable on a TextView. Supports relative
1904 * (s/t/e/b) or cardinal (l/t/r/b) arrangement.
1905 */
1906 private class TextViewDrawableColorFilterAction extends Action {
1907 public TextViewDrawableColorFilterAction(int viewId, boolean isRelative, int index,
1908 int color, PorterDuff.Mode mode) {
1909 this.viewId = viewId;
1910 this.isRelative = isRelative;
1911 this.index = index;
1912 this.color = color;
1913 this.mode = mode;
1914 }
1915
1916 public TextViewDrawableColorFilterAction(Parcel parcel) {
1917 viewId = parcel.readInt();
1918 isRelative = (parcel.readInt() != 0);
1919 index = parcel.readInt();
1920 color = parcel.readInt();
1921 mode = readPorterDuffMode(parcel);
1922 }
1923
1924 private PorterDuff.Mode readPorterDuffMode(Parcel parcel) {
1925 int mode = parcel.readInt();
1926 if (mode >= 0 && mode < PorterDuff.Mode.values().length) {
1927 return PorterDuff.Mode.values()[mode];
1928 } else {
1929 return PorterDuff.Mode.CLEAR;
1930 }
1931 }
1932
1933 public void writeToParcel(Parcel dest, int flags) {
1934 dest.writeInt(TAG);
1935 dest.writeInt(viewId);
1936 dest.writeInt(isRelative ? 1 : 0);
1937 dest.writeInt(index);
1938 dest.writeInt(color);
1939 dest.writeInt(mode.ordinal());
1940 }
1941
1942 @Override
1943 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
1944 final TextView target = (TextView) root.findViewById(viewId);
1945 if (target == null) return;
1946 Drawable[] drawables = isRelative
1947 ? target.getCompoundDrawablesRelative()
1948 : target.getCompoundDrawables();
1949 if (index < 0 || index >= 4) {
1950 throw new IllegalStateException("index must be in range [0, 3].");
1951 }
1952 Drawable d = drawables[index];
1953 if (d != null) {
1954 d.mutate();
1955 d.setColorFilter(color, mode);
1956 }
1957 }
1958
1959 public String getActionName() {
1960 return "TextViewDrawableColorFilterAction";
1961 }
1962
1963 final boolean isRelative;
1964 final int index;
1965 final int color;
1966 final PorterDuff.Mode mode;
1967
1968 public final static int TAG = 17;
1969 }
1970
1971 /**
Adrian Roosfe84e1f2015-11-04 15:55:39 -08001972 * Helper action to add a view tag with RemoteInputs.
1973 */
1974 private class SetRemoteInputsAction extends Action {
1975
1976 public SetRemoteInputsAction(int viewId, RemoteInput[] remoteInputs) {
1977 this.viewId = viewId;
1978 this.remoteInputs = remoteInputs;
1979 }
1980
1981 public SetRemoteInputsAction(Parcel parcel) {
1982 viewId = parcel.readInt();
Adrian Roos5dd685f2016-02-24 12:05:51 -08001983 remoteInputs = parcel.createTypedArray(RemoteInput.CREATOR);
Adrian Roosfe84e1f2015-11-04 15:55:39 -08001984 }
1985
1986 public void writeToParcel(Parcel dest, int flags) {
1987 dest.writeInt(TAG);
1988 dest.writeInt(viewId);
Adrian Roos5dd685f2016-02-24 12:05:51 -08001989 dest.writeTypedArray(remoteInputs, flags);
Adrian Roosfe84e1f2015-11-04 15:55:39 -08001990 }
1991
1992 @Override
1993 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
1994 final TextView target = (TextView) root.findViewById(viewId);
1995 if (target == null) return;
1996
1997 target.setTagInternal(R.id.remote_input_tag, remoteInputs);
1998 }
1999
2000 public String getActionName() {
2001 return "SetRemoteInputsAction";
2002 }
2003
2004 final Parcelable[] remoteInputs;
2005 public final static int TAG = 18;
2006 }
2007
2008 /**
Winson Chung3ec9a452010-09-23 16:40:28 -07002009 * Simple class used to keep track of memory usage in a RemoteViews.
2010 *
2011 */
2012 private class MemoryUsageCounter {
2013 public void clear() {
Adam Cohen5d200642012-04-24 10:43:31 -07002014 mMemoryUsage = 0;
Winson Chung3ec9a452010-09-23 16:40:28 -07002015 }
2016
Adam Cohen5d200642012-04-24 10:43:31 -07002017 public void increment(int numBytes) {
2018 mMemoryUsage += numBytes;
Winson Chung3ec9a452010-09-23 16:40:28 -07002019 }
2020
Adam Cohen5d200642012-04-24 10:43:31 -07002021 public int getMemoryUsage() {
2022 return mMemoryUsage;
Winson Chung3ec9a452010-09-23 16:40:28 -07002023 }
2024
Romain Guye4d4e202013-07-22 13:02:02 -07002025 @SuppressWarnings("deprecation")
Adam Cohen5d200642012-04-24 10:43:31 -07002026 public void addBitmapMemory(Bitmap b) {
2027 final Bitmap.Config c = b.getConfig();
2028 // If we don't know, be pessimistic and assume 4
2029 int bpp = 4;
2030 if (c != null) {
2031 switch (c) {
Svetoslav976e8bd2014-07-16 15:12:03 -07002032 case ALPHA_8:
2033 bpp = 1;
2034 break;
2035 case RGB_565:
2036 case ARGB_4444:
2037 bpp = 2;
2038 break;
2039 case ARGB_8888:
2040 bpp = 4;
2041 break;
Adam Cohen5d200642012-04-24 10:43:31 -07002042 }
2043 }
2044 increment(b.getWidth() * b.getHeight() * bpp);
2045 }
2046
2047 int mMemoryUsage;
Winson Chung3ec9a452010-09-23 16:40:28 -07002048 }
2049
2050 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002051 * Create a new RemoteViews object that will display the views contained
2052 * in the specified layout file.
Jim Millere667a7a2012-08-09 19:22:32 -07002053 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002054 * @param packageName Name of the package that contains the layout resource
2055 * @param layoutId The id of the layout resource
2056 */
2057 public RemoteViews(String packageName, int layoutId) {
Svet Ganov0da85b62014-08-06 14:11:37 -07002058 this(getApplicationInfo(packageName, UserHandle.myUserId()), layoutId);
2059 }
2060
2061 /**
2062 * Create a new RemoteViews object that will display the views contained
2063 * in the specified layout file.
2064 *
2065 * @param packageName Name of the package that contains the layout resource.
2066 * @param userId The user under which the package is running.
2067 * @param layoutId The id of the layout resource.
2068 *
2069 * @hide
2070 */
2071 public RemoteViews(String packageName, int userId, int layoutId) {
2072 this(getApplicationInfo(packageName, userId), layoutId);
2073 }
2074
2075 /**
2076 * Create a new RemoteViews object that will display the views contained
2077 * in the specified layout file.
2078 *
2079 * @param application The application whose content is shown by the views.
2080 * @param layoutId The id of the layout resource.
Kenny Guy77320062014-08-27 21:37:15 +01002081 *
2082 * @hide
Svet Ganov0da85b62014-08-06 14:11:37 -07002083 */
Kenny Guy77320062014-08-27 21:37:15 +01002084 protected RemoteViews(ApplicationInfo application, int layoutId) {
Svet Ganov0da85b62014-08-06 14:11:37 -07002085 mApplication = application;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002086 mLayoutId = layoutId;
Adam Cohen5d200642012-04-24 10:43:31 -07002087 mBitmapCache = new BitmapCache();
Winson Chung3ec9a452010-09-23 16:40:28 -07002088 // setup the memory usage statistics
2089 mMemoryUsageCounter = new MemoryUsageCounter();
2090 recalculateMemoryUsage();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002091 }
2092
Adam Cohen5d200642012-04-24 10:43:31 -07002093 private boolean hasLandscapeAndPortraitLayouts() {
2094 return (mLandscape != null) && (mPortrait != null);
2095 }
2096
Jeff Sharkey6d515712012-09-20 16:06:08 -07002097 /**
Adam Cohen5d200642012-04-24 10:43:31 -07002098 * Create a new RemoteViews object that will inflate as the specified
2099 * landspace or portrait RemoteViews, depending on the current configuration.
2100 *
2101 * @param landscape The RemoteViews to inflate in landscape configuration
2102 * @param portrait The RemoteViews to inflate in portrait configuration
2103 */
2104 public RemoteViews(RemoteViews landscape, RemoteViews portrait) {
2105 if (landscape == null || portrait == null) {
2106 throw new RuntimeException("Both RemoteViews must be non-null");
2107 }
Svet Ganov0da85b62014-08-06 14:11:37 -07002108 if (landscape.mApplication.uid != portrait.mApplication.uid
2109 || !landscape.mApplication.packageName.equals(portrait.mApplication.packageName)) {
2110 throw new RuntimeException("Both RemoteViews must share the same package and user");
Adam Cohen5d200642012-04-24 10:43:31 -07002111 }
Svet Ganov0da85b62014-08-06 14:11:37 -07002112 mApplication = portrait.mApplication;
Adam Cohen5d200642012-04-24 10:43:31 -07002113 mLayoutId = portrait.getLayoutId();
2114
2115 mLandscape = landscape;
2116 mPortrait = portrait;
2117
2118 // setup the memory usage statistics
2119 mMemoryUsageCounter = new MemoryUsageCounter();
2120
2121 mBitmapCache = new BitmapCache();
2122 configureRemoteViewsAsChild(landscape);
2123 configureRemoteViewsAsChild(portrait);
2124
2125 recalculateMemoryUsage();
2126 }
2127
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002128 /**
2129 * Reads a RemoteViews object from a parcel.
Jim Millere667a7a2012-08-09 19:22:32 -07002130 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002131 * @param parcel
2132 */
2133 public RemoteViews(Parcel parcel) {
Adam Cohen5d200642012-04-24 10:43:31 -07002134 this(parcel, null);
2135 }
Adam Cohenca6fd842010-09-03 18:10:35 -07002136
Adam Cohen5d200642012-04-24 10:43:31 -07002137 private RemoteViews(Parcel parcel, BitmapCache bitmapCache) {
2138 int mode = parcel.readInt();
2139
2140 // We only store a bitmap cache in the root of the RemoteViews.
2141 if (bitmapCache == null) {
2142 mBitmapCache = new BitmapCache(parcel);
2143 } else {
2144 setBitmapCache(bitmapCache);
2145 setNotRoot();
2146 }
2147
2148 if (mode == MODE_NORMAL) {
Svet Ganov0da85b62014-08-06 14:11:37 -07002149 mApplication = parcel.readParcelable(null);
Adam Cohen5d200642012-04-24 10:43:31 -07002150 mLayoutId = parcel.readInt();
Romain Guye4d4e202013-07-22 13:02:02 -07002151 mIsWidgetCollectionChild = parcel.readInt() == 1;
Adam Cohen5d200642012-04-24 10:43:31 -07002152
2153 int count = parcel.readInt();
2154 if (count > 0) {
2155 mActions = new ArrayList<Action>(count);
2156 for (int i=0; i<count; i++) {
2157 int tag = parcel.readInt();
2158 switch (tag) {
Svetoslav976e8bd2014-07-16 15:12:03 -07002159 case SetOnClickPendingIntent.TAG:
2160 mActions.add(new SetOnClickPendingIntent(parcel));
2161 break;
2162 case SetDrawableParameters.TAG:
2163 mActions.add(new SetDrawableParameters(parcel));
2164 break;
2165 case ReflectionAction.TAG:
2166 mActions.add(new ReflectionAction(parcel));
2167 break;
2168 case ViewGroupAction.TAG:
2169 mActions.add(new ViewGroupAction(parcel, mBitmapCache));
2170 break;
2171 case ReflectionActionWithoutParams.TAG:
2172 mActions.add(new ReflectionActionWithoutParams(parcel));
2173 break;
2174 case SetEmptyView.TAG:
2175 mActions.add(new SetEmptyView(parcel));
2176 break;
2177 case SetPendingIntentTemplate.TAG:
2178 mActions.add(new SetPendingIntentTemplate(parcel));
2179 break;
2180 case SetOnClickFillInIntent.TAG:
2181 mActions.add(new SetOnClickFillInIntent(parcel));
2182 break;
2183 case SetRemoteViewsAdapterIntent.TAG:
2184 mActions.add(new SetRemoteViewsAdapterIntent(parcel));
2185 break;
2186 case TextViewDrawableAction.TAG:
2187 mActions.add(new TextViewDrawableAction(parcel));
2188 break;
2189 case TextViewSizeAction.TAG:
2190 mActions.add(new TextViewSizeAction(parcel));
2191 break;
2192 case ViewPaddingAction.TAG:
2193 mActions.add(new ViewPaddingAction(parcel));
2194 break;
2195 case BitmapReflectionAction.TAG:
2196 mActions.add(new BitmapReflectionAction(parcel));
2197 break;
2198 case SetRemoteViewsAdapterList.TAG:
2199 mActions.add(new SetRemoteViewsAdapterList(parcel));
2200 break;
2201 case TextViewDrawableColorFilterAction.TAG:
2202 mActions.add(new TextViewDrawableColorFilterAction(parcel));
2203 break;
Adrian Roosfe84e1f2015-11-04 15:55:39 -08002204 case SetRemoteInputsAction.TAG:
2205 mActions.add(new SetRemoteInputsAction(parcel));
2206 break;
Adrian Roos9b123cf2016-02-04 14:55:57 -08002207 case LayoutParamAction.TAG:
2208 mActions.add(new LayoutParamAction(parcel));
Selim Cinek65b2e7c2015-10-26 14:11:31 -07002209 break;
Svetoslav976e8bd2014-07-16 15:12:03 -07002210 default:
2211 throw new ActionException("Tag " + tag + " not found");
Adam Cohen5d200642012-04-24 10:43:31 -07002212 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002213 }
2214 }
Adam Cohen5d200642012-04-24 10:43:31 -07002215 } else {
2216 // MODE_HAS_LANDSCAPE_AND_PORTRAIT
2217 mLandscape = new RemoteViews(parcel, mBitmapCache);
2218 mPortrait = new RemoteViews(parcel, mBitmapCache);
Svet Ganov0da85b62014-08-06 14:11:37 -07002219 mApplication = mPortrait.mApplication;
Adam Cohen5d200642012-04-24 10:43:31 -07002220 mLayoutId = mPortrait.getLayoutId();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002221 }
Winson Chung3ec9a452010-09-23 16:40:28 -07002222
2223 // setup the memory usage statistics
2224 mMemoryUsageCounter = new MemoryUsageCounter();
2225 recalculateMemoryUsage();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002226 }
2227
Winson Chung3ec9a452010-09-23 16:40:28 -07002228
Adam Cohen3ff2d862012-09-26 14:07:57 -07002229 public RemoteViews clone() {
2230 Parcel p = Parcel.obtain();
2231 writeToParcel(p, 0);
2232 p.setDataPosition(0);
Maunik Shahdb1a9a32014-06-19 14:18:39 +05302233 RemoteViews rv = new RemoteViews(p);
2234 p.recycle();
2235 return rv;
Joe Onorato18e69df2010-05-17 22:26:12 -07002236 }
2237
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002238 public String getPackage() {
Svetoslavb6242442014-09-19 13:21:55 -07002239 return (mApplication != null) ? mApplication.packageName : null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002240 }
2241
Adam Cohen5d200642012-04-24 10:43:31 -07002242 /**
2243 * Reutrns the layout id of the root layout associated with this RemoteViews. In the case
2244 * that the RemoteViews has both a landscape and portrait root, this will return the layout
2245 * id associated with the portrait layout.
2246 *
2247 * @return the layout id.
2248 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002249 public int getLayoutId() {
2250 return mLayoutId;
2251 }
2252
Winson Chung3ec9a452010-09-23 16:40:28 -07002253 /*
Adam Cohenca6fd842010-09-03 18:10:35 -07002254 * This flag indicates whether this RemoteViews object is being created from a
2255 * RemoteViewsService for use as a child of a widget collection. This flag is used
2256 * to determine whether or not certain features are available, in particular,
2257 * setting on click extras and setting on click pending intents. The former is enabled,
2258 * and the latter disabled when this flag is true.
2259 */
2260 void setIsWidgetCollectionChild(boolean isWidgetCollectionChild) {
2261 mIsWidgetCollectionChild = isWidgetCollectionChild;
2262 }
2263
2264 /**
Winson Chung3ec9a452010-09-23 16:40:28 -07002265 * Updates the memory usage statistics.
2266 */
2267 private void recalculateMemoryUsage() {
2268 mMemoryUsageCounter.clear();
2269
Adam Cohen5d200642012-04-24 10:43:31 -07002270 if (!hasLandscapeAndPortraitLayouts()) {
2271 // Accumulate the memory usage for each action
2272 if (mActions != null) {
2273 final int count = mActions.size();
2274 for (int i= 0; i < count; ++i) {
2275 mActions.get(i).updateMemoryUsageEstimate(mMemoryUsageCounter);
2276 }
Winson Chung3ec9a452010-09-23 16:40:28 -07002277 }
Adam Cohen5d200642012-04-24 10:43:31 -07002278 if (mIsRoot) {
2279 mBitmapCache.addBitmapMemory(mMemoryUsageCounter);
2280 }
2281 } else {
2282 mMemoryUsageCounter.increment(mLandscape.estimateMemoryUsage());
2283 mMemoryUsageCounter.increment(mPortrait.estimateMemoryUsage());
2284 mBitmapCache.addBitmapMemory(mMemoryUsageCounter);
2285 }
2286 }
2287
2288 /**
2289 * Recursively sets BitmapCache in the hierarchy and update the bitmap ids.
2290 */
2291 private void setBitmapCache(BitmapCache bitmapCache) {
2292 mBitmapCache = bitmapCache;
2293 if (!hasLandscapeAndPortraitLayouts()) {
2294 if (mActions != null) {
2295 final int count = mActions.size();
2296 for (int i= 0; i < count; ++i) {
2297 mActions.get(i).setBitmapCache(bitmapCache);
2298 }
2299 }
2300 } else {
2301 mLandscape.setBitmapCache(bitmapCache);
2302 mPortrait.setBitmapCache(bitmapCache);
Winson Chung3ec9a452010-09-23 16:40:28 -07002303 }
2304 }
2305
2306 /**
2307 * Returns an estimate of the bitmap heap memory usage for this RemoteViews.
2308 */
Adam Cohen311c79c2012-05-10 14:44:38 -07002309 /** @hide */
2310 public int estimateMemoryUsage() {
Adam Cohen5d200642012-04-24 10:43:31 -07002311 return mMemoryUsageCounter.getMemoryUsage();
Winson Chung3ec9a452010-09-23 16:40:28 -07002312 }
2313
2314 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002315 * Add an action to be executed on the remote side when apply is called.
Jim Millere667a7a2012-08-09 19:22:32 -07002316 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002317 * @param a The action to add
2318 */
2319 private void addAction(Action a) {
Adam Cohen5d200642012-04-24 10:43:31 -07002320 if (hasLandscapeAndPortraitLayouts()) {
2321 throw new RuntimeException("RemoteViews specifying separate landscape and portrait" +
2322 " layouts cannot be modified. Instead, fully configure the landscape and" +
2323 " portrait layouts individually before constructing the combined layout.");
2324 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002325 if (mActions == null) {
2326 mActions = new ArrayList<Action>();
2327 }
2328 mActions.add(a);
Winson Chung3ec9a452010-09-23 16:40:28 -07002329
2330 // update the memory usage stats
2331 a.updateMemoryUsageEstimate(mMemoryUsageCounter);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002332 }
Jeff Sharkey1162fd72009-11-04 17:58:08 -08002333
2334 /**
2335 * Equivalent to calling {@link ViewGroup#addView(View)} after inflating the
2336 * given {@link RemoteViews}. This allows users to build "nested"
2337 * {@link RemoteViews}. In cases where consumers of {@link RemoteViews} may
2338 * recycle layouts, use {@link #removeAllViews(int)} to clear any existing
2339 * children.
2340 *
2341 * @param viewId The id of the parent {@link ViewGroup} to add child into.
2342 * @param nestedView {@link RemoteViews} that describes the child.
2343 */
2344 public void addView(int viewId, RemoteViews nestedView) {
2345 addAction(new ViewGroupAction(viewId, nestedView));
2346 }
2347
2348 /**
2349 * Equivalent to calling {@link ViewGroup#removeAllViews()}.
2350 *
2351 * @param viewId The id of the parent {@link ViewGroup} to remove all
2352 * children from.
2353 */
2354 public void removeAllViews(int viewId) {
2355 addAction(new ViewGroupAction(viewId, null));
2356 }
2357
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002358 /**
Adam Cohen0b96a572011-02-10 15:56:16 -08002359 * Equivalent to calling {@link AdapterViewAnimator#showNext()}
Adam Cohen2dd21972010-08-15 18:20:04 -07002360 *
Adam Cohen0b96a572011-02-10 15:56:16 -08002361 * @param viewId The id of the view on which to call {@link AdapterViewAnimator#showNext()}
Adam Cohen2dd21972010-08-15 18:20:04 -07002362 */
2363 public void showNext(int viewId) {
2364 addAction(new ReflectionActionWithoutParams(viewId, "showNext"));
2365 }
2366
2367 /**
Adam Cohen0b96a572011-02-10 15:56:16 -08002368 * Equivalent to calling {@link AdapterViewAnimator#showPrevious()}
Adam Cohen2dd21972010-08-15 18:20:04 -07002369 *
Adam Cohen0b96a572011-02-10 15:56:16 -08002370 * @param viewId The id of the view on which to call {@link AdapterViewAnimator#showPrevious()}
Adam Cohen2dd21972010-08-15 18:20:04 -07002371 */
2372 public void showPrevious(int viewId) {
2373 addAction(new ReflectionActionWithoutParams(viewId, "showPrevious"));
2374 }
2375
2376 /**
Adam Cohen0b96a572011-02-10 15:56:16 -08002377 * Equivalent to calling {@link AdapterViewAnimator#setDisplayedChild(int)}
2378 *
2379 * @param viewId The id of the view on which to call
2380 * {@link AdapterViewAnimator#setDisplayedChild(int)}
2381 */
2382 public void setDisplayedChild(int viewId, int childIndex) {
2383 setInt(viewId, "setDisplayedChild", childIndex);
2384 }
2385
2386 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002387 * Equivalent to calling View.setVisibility
Jim Millere667a7a2012-08-09 19:22:32 -07002388 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002389 * @param viewId The id of the view whose visibility should change
2390 * @param visibility The new visibility for the view
2391 */
2392 public void setViewVisibility(int viewId, int visibility) {
2393 setInt(viewId, "setVisibility", visibility);
2394 }
Adam Cohenca6fd842010-09-03 18:10:35 -07002395
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002396 /**
2397 * Equivalent to calling TextView.setText
Jim Millere667a7a2012-08-09 19:22:32 -07002398 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002399 * @param viewId The id of the view whose text should change
2400 * @param text The new text for the view
2401 */
2402 public void setTextViewText(int viewId, CharSequence text) {
2403 setCharSequence(viewId, "setText", text);
2404 }
Daniel Sandler7264f712012-05-21 14:48:23 -04002405
2406 /**
Daniel Sandler7264f712012-05-21 14:48:23 -04002407 * Equivalent to calling {@link TextView#setTextSize(int, float)}
Jim Millere667a7a2012-08-09 19:22:32 -07002408 *
Daniel Sandler7264f712012-05-21 14:48:23 -04002409 * @param viewId The id of the view whose text size should change
2410 * @param units The units of size (e.g. COMPLEX_UNIT_SP)
2411 * @param size The size of the text
2412 */
2413 public void setTextViewTextSize(int viewId, int units, float size) {
2414 addAction(new TextViewSizeAction(viewId, units, size));
2415 }
2416
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002417 /**
Jim Millere667a7a2012-08-09 19:22:32 -07002418 * Equivalent to calling
Daniel Sandler820ba322012-03-23 16:36:00 -05002419 * {@link TextView#setCompoundDrawablesWithIntrinsicBounds(int, int, int, int)}.
2420 *
2421 * @param viewId The id of the view whose text should change
2422 * @param left The id of a drawable to place to the left of the text, or 0
2423 * @param top The id of a drawable to place above the text, or 0
2424 * @param right The id of a drawable to place to the right of the text, or 0
Jim Millere667a7a2012-08-09 19:22:32 -07002425 * @param bottom The id of a drawable to place below the text, or 0
Daniel Sandler820ba322012-03-23 16:36:00 -05002426 */
2427 public void setTextViewCompoundDrawables(int viewId, int left, int top, int right, int bottom) {
2428 addAction(new TextViewDrawableAction(viewId, false, left, top, right, bottom));
2429 }
2430
2431 /**
Jim Millere667a7a2012-08-09 19:22:32 -07002432 * Equivalent to calling {@link
Daniel Sandler820ba322012-03-23 16:36:00 -05002433 * TextView#setCompoundDrawablesRelativeWithIntrinsicBounds(int, int, int, int)}.
2434 *
2435 * @param viewId The id of the view whose text should change
Jim Millere667a7a2012-08-09 19:22:32 -07002436 * @param start The id of a drawable to place before the text (relative to the
Daniel Sandler820ba322012-03-23 16:36:00 -05002437 * layout direction), or 0
2438 * @param top The id of a drawable to place above the text, or 0
2439 * @param end The id of a drawable to place after the text, or 0
Fabrice Di Meglio66388dc2012-05-03 18:51:57 -07002440 * @param bottom The id of a drawable to place below the text, or 0
Daniel Sandler820ba322012-03-23 16:36:00 -05002441 */
2442 public void setTextViewCompoundDrawablesRelative(int viewId, int start, int top, int end, int bottom) {
2443 addAction(new TextViewDrawableAction(viewId, true, start, top, end, bottom));
2444 }
2445
2446 /**
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01002447 * Equivalent to applying a color filter on one of the drawables in
2448 * {@link android.widget.TextView#getCompoundDrawablesRelative()}.
2449 *
2450 * @param viewId The id of the view whose text should change.
2451 * @param index The index of the drawable in the array of
2452 * {@link android.widget.TextView#getCompoundDrawablesRelative()} to set the color
2453 * filter on. Must be in [0, 3].
2454 * @param color The color of the color filter. See
2455 * {@link Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)}.
2456 * @param mode The mode of the color filter. See
2457 * {@link Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)}.
2458 * @hide
2459 */
2460 public void setTextViewCompoundDrawablesRelativeColorFilter(int viewId,
2461 int index, int color, PorterDuff.Mode mode) {
2462 if (index < 0 || index >= 4) {
2463 throw new IllegalArgumentException("index must be in range [0, 3].");
2464 }
2465 addAction(new TextViewDrawableColorFilterAction(viewId, true, index, color, mode));
2466 }
2467
2468 /**
Dan Sandler912282e2015-07-28 22:49:30 -04002469 * Equivalent to calling {@link
2470 * TextView#setCompoundDrawablesWithIntrinsicBounds(Drawable, Drawable, Drawable, Drawable)}
2471 * using the drawables yielded by {@link Icon#loadDrawable(Context)}.
2472 *
2473 * @param viewId The id of the view whose text should change
2474 * @param left an Icon to place to the left of the text, or 0
2475 * @param top an Icon to place above the text, or 0
2476 * @param right an Icon to place to the right of the text, or 0
2477 * @param bottom an Icon to place below the text, or 0
2478 *
2479 * @hide
2480 */
2481 public void setTextViewCompoundDrawables(int viewId, Icon left, Icon top, Icon right, Icon bottom) {
2482 addAction(new TextViewDrawableAction(viewId, false, left, top, right, bottom));
2483 }
2484
2485 /**
2486 * Equivalent to calling {@link
2487 * TextView#setCompoundDrawablesRelativeWithIntrinsicBounds(Drawable, Drawable, Drawable, Drawable)}
2488 * using the drawables yielded by {@link Icon#loadDrawable(Context)}.
2489 *
2490 * @param viewId The id of the view whose text should change
2491 * @param start an Icon to place before the text (relative to the
2492 * layout direction), or 0
2493 * @param top an Icon to place above the text, or 0
2494 * @param end an Icon to place after the text, or 0
2495 * @param bottom an Icon to place below the text, or 0
2496 *
2497 * @hide
2498 */
2499 public void setTextViewCompoundDrawablesRelative(int viewId, Icon start, Icon top, Icon end, Icon bottom) {
2500 addAction(new TextViewDrawableAction(viewId, true, start, top, end, bottom));
2501 }
2502
2503 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002504 * Equivalent to calling ImageView.setImageResource
Jim Millere667a7a2012-08-09 19:22:32 -07002505 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002506 * @param viewId The id of the view whose drawable should change
2507 * @param srcId The new resource id for the drawable
2508 */
Jim Millere667a7a2012-08-09 19:22:32 -07002509 public void setImageViewResource(int viewId, int srcId) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002510 setInt(viewId, "setImageResource", srcId);
2511 }
2512
2513 /**
2514 * Equivalent to calling ImageView.setImageURI
Jim Millere667a7a2012-08-09 19:22:32 -07002515 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002516 * @param viewId The id of the view whose drawable should change
2517 * @param uri The Uri for the image
2518 */
2519 public void setImageViewUri(int viewId, Uri uri) {
2520 setUri(viewId, "setImageURI", uri);
2521 }
2522
2523 /**
2524 * Equivalent to calling ImageView.setImageBitmap
Jim Millere667a7a2012-08-09 19:22:32 -07002525 *
Scott Main93dc6422012-02-24 12:04:06 -08002526 * @param viewId The id of the view whose bitmap should change
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002527 * @param bitmap The new Bitmap for the drawable
2528 */
2529 public void setImageViewBitmap(int viewId, Bitmap bitmap) {
2530 setBitmap(viewId, "setImageBitmap", bitmap);
2531 }
2532
2533 /**
Dan Sandlera22a3802015-05-13 00:12:47 -04002534 * Equivalent to calling ImageView.setImageIcon
2535 *
2536 * @param viewId The id of the view whose bitmap should change
2537 * @param icon The new Icon for the ImageView
2538 */
2539 public void setImageViewIcon(int viewId, Icon icon) {
2540 setIcon(viewId, "setImageIcon", icon);
2541 }
2542
2543 /**
Adam Cohen1480fdd2010-08-25 17:24:53 -07002544 * Equivalent to calling AdapterView.setEmptyView
2545 *
2546 * @param viewId The id of the view on which to set the empty view
2547 * @param emptyViewId The view id of the empty view
2548 */
2549 public void setEmptyView(int viewId, int emptyViewId) {
2550 addAction(new SetEmptyView(viewId, emptyViewId));
2551 }
2552
2553 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002554 * Equivalent to calling {@link Chronometer#setBase Chronometer.setBase},
2555 * {@link Chronometer#setFormat Chronometer.setFormat},
2556 * and {@link Chronometer#start Chronometer.start()} or
2557 * {@link Chronometer#stop Chronometer.stop()}.
Jim Millere667a7a2012-08-09 19:22:32 -07002558 *
Scott Main93dc6422012-02-24 12:04:06 -08002559 * @param viewId The id of the {@link Chronometer} to change
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002560 * @param base The time at which the timer would have read 0:00. This
2561 * time should be based off of
2562 * {@link android.os.SystemClock#elapsedRealtime SystemClock.elapsedRealtime()}.
2563 * @param format The Chronometer format string, or null to
2564 * simply display the timer value.
2565 * @param started True if you want the clock to be started, false if not.
2566 */
2567 public void setChronometer(int viewId, long base, String format, boolean started) {
2568 setLong(viewId, "setBase", base);
2569 setString(viewId, "setFormat", format);
2570 setBoolean(viewId, "setStarted", started);
2571 }
Jim Millere667a7a2012-08-09 19:22:32 -07002572
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002573 /**
2574 * Equivalent to calling {@link ProgressBar#setMax ProgressBar.setMax},
2575 * {@link ProgressBar#setProgress ProgressBar.setProgress}, and
2576 * {@link ProgressBar#setIndeterminate ProgressBar.setIndeterminate}
2577 *
2578 * If indeterminate is true, then the values for max and progress are ignored.
Jim Millere667a7a2012-08-09 19:22:32 -07002579 *
Scott Main93dc6422012-02-24 12:04:06 -08002580 * @param viewId The id of the {@link ProgressBar} to change
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002581 * @param max The 100% value for the progress bar
2582 * @param progress The current value of the progress bar.
Jim Millere667a7a2012-08-09 19:22:32 -07002583 * @param indeterminate True if the progress bar is indeterminate,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002584 * false if not.
2585 */
Jim Millere667a7a2012-08-09 19:22:32 -07002586 public void setProgressBar(int viewId, int max, int progress,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002587 boolean indeterminate) {
2588 setBoolean(viewId, "setIndeterminate", indeterminate);
2589 if (!indeterminate) {
2590 setInt(viewId, "setMax", max);
2591 setInt(viewId, "setProgress", progress);
2592 }
2593 }
Jim Millere667a7a2012-08-09 19:22:32 -07002594
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002595 /**
2596 * Equivalent to calling
2597 * {@link android.view.View#setOnClickListener(android.view.View.OnClickListener)}
2598 * to launch the provided {@link PendingIntent}.
Jim Millere667a7a2012-08-09 19:22:32 -07002599 *
Adam Cohen35ae9ca2010-09-20 15:20:41 -07002600 * When setting the on-click action of items within collections (eg. {@link ListView},
2601 * {@link StackView} etc.), this method will not work. Instead, use {@link
2602 * RemoteViews#setPendingIntentTemplate(int, PendingIntent) in conjunction with
2603 * RemoteViews#setOnClickFillInIntent(int, Intent).
2604 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002605 * @param viewId The id of the view that will trigger the {@link PendingIntent} when clicked
2606 * @param pendingIntent The {@link PendingIntent} to send when user clicks
2607 */
2608 public void setOnClickPendingIntent(int viewId, PendingIntent pendingIntent) {
2609 addAction(new SetOnClickPendingIntent(viewId, pendingIntent));
2610 }
2611
2612 /**
Adam Cohen35ae9ca2010-09-20 15:20:41 -07002613 * When using collections (eg. {@link ListView}, {@link StackView} etc.) in widgets, it is very
2614 * costly to set PendingIntents on the individual items, and is hence not permitted. Instead
2615 * this method should be used to set a single PendingIntent template on the collection, and
2616 * individual items can differentiate their on-click behavior using
2617 * {@link RemoteViews#setOnClickFillInIntent(int, Intent)}.
Adam Cohenca6fd842010-09-03 18:10:35 -07002618 *
2619 * @param viewId The id of the collection who's children will use this PendingIntent template
2620 * when clicked
2621 * @param pendingIntentTemplate The {@link PendingIntent} to be combined with extras specified
2622 * by a child of viewId and executed when that child is clicked
2623 */
2624 public void setPendingIntentTemplate(int viewId, PendingIntent pendingIntentTemplate) {
2625 addAction(new SetPendingIntentTemplate(viewId, pendingIntentTemplate));
2626 }
2627
2628 /**
Adam Cohen35ae9ca2010-09-20 15:20:41 -07002629 * When using collections (eg. {@link ListView}, {@link StackView} etc.) in widgets, it is very
2630 * costly to set PendingIntents on the individual items, and is hence not permitted. Instead
2631 * a single PendingIntent template can be set on the collection, see {@link
2632 * RemoteViews#setPendingIntentTemplate(int, PendingIntent)}, and the individual on-click
2633 * action of a given item can be distinguished by setting a fillInIntent on that item. The
2634 * fillInIntent is then combined with the PendingIntent template in order to determine the final
2635 * intent which will be executed when the item is clicked. This works as follows: any fields
2636 * which are left blank in the PendingIntent template, but are provided by the fillInIntent
2637 * will be overwritten, and the resulting PendingIntent will be used.
2638 *
2639 *
2640 * of the PendingIntent template will then be filled in with the associated fields that are
2641 * set in fillInIntent. See {@link Intent#fillIn(Intent, int)} for more details.
2642 *
2643 * @param viewId The id of the view on which to set the fillInIntent
2644 * @param fillInIntent The intent which will be combined with the parent's PendingIntent
2645 * in order to determine the on-click behavior of the view specified by viewId
2646 */
2647 public void setOnClickFillInIntent(int viewId, Intent fillInIntent) {
2648 addAction(new SetOnClickFillInIntent(viewId, fillInIntent));
2649 }
2650
2651 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002652 * @hide
2653 * Equivalent to calling a combination of {@link Drawable#setAlpha(int)},
2654 * {@link Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)},
2655 * and/or {@link Drawable#setLevel(int)} on the {@link Drawable} of a given
2656 * view.
2657 * <p>
2658 * You can omit specific calls by marking their values with null or -1.
Jim Millere667a7a2012-08-09 19:22:32 -07002659 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002660 * @param viewId The id of the view that contains the target
2661 * {@link Drawable}
2662 * @param targetBackground If true, apply these parameters to the
2663 * {@link Drawable} returned by
2664 * {@link android.view.View#getBackground()}. Otherwise, assume
2665 * the target view is an {@link ImageView} and apply them to
2666 * {@link ImageView#getDrawable()}.
2667 * @param alpha Specify an alpha value for the drawable, or -1 to leave
2668 * unchanged.
2669 * @param colorFilter Specify a color for a
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02002670 * {@link android.graphics.ColorFilter} for this drawable. This will be ignored if
2671 * {@code mode} is {@code null}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002672 * @param mode Specify a PorterDuff mode for this drawable, or null to leave
2673 * unchanged.
2674 * @param level Specify the level for the drawable, or -1 to leave
2675 * unchanged.
2676 */
2677 public void setDrawableParameters(int viewId, boolean targetBackground, int alpha,
2678 int colorFilter, PorterDuff.Mode mode, int level) {
2679 addAction(new SetDrawableParameters(viewId, targetBackground, alpha,
2680 colorFilter, mode, level));
2681 }
2682
2683 /**
Jorim Jaggief72a192014-08-26 21:57:46 +02002684 * @hide
2685 * Equivalent to calling {@link android.widget.ProgressBar#setProgressTintList}.
2686 *
2687 * @param viewId The id of the view whose tint should change
2688 * @param tint the tint to apply, may be {@code null} to clear tint
2689 */
2690 public void setProgressTintList(int viewId, ColorStateList tint) {
2691 addAction(new ReflectionAction(viewId, "setProgressTintList",
2692 ReflectionAction.COLOR_STATE_LIST, tint));
2693 }
2694
2695 /**
2696 * @hide
2697 * Equivalent to calling {@link android.widget.ProgressBar#setProgressBackgroundTintList}.
2698 *
2699 * @param viewId The id of the view whose tint should change
2700 * @param tint the tint to apply, may be {@code null} to clear tint
2701 */
2702 public void setProgressBackgroundTintList(int viewId, ColorStateList tint) {
2703 addAction(new ReflectionAction(viewId, "setProgressBackgroundTintList",
2704 ReflectionAction.COLOR_STATE_LIST, tint));
2705 }
2706
2707 /**
2708 * @hide
2709 * Equivalent to calling {@link android.widget.ProgressBar#setIndeterminateTintList}.
2710 *
2711 * @param viewId The id of the view whose tint should change
2712 * @param tint the tint to apply, may be {@code null} to clear tint
2713 */
2714 public void setProgressIndeterminateTintList(int viewId, ColorStateList tint) {
2715 addAction(new ReflectionAction(viewId, "setIndeterminateTintList",
2716 ReflectionAction.COLOR_STATE_LIST, tint));
2717 }
2718
2719 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002720 * Equivalent to calling {@link android.widget.TextView#setTextColor(int)}.
Jim Millere667a7a2012-08-09 19:22:32 -07002721 *
Scott Main93dc6422012-02-24 12:04:06 -08002722 * @param viewId The id of the view whose text color should change
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002723 * @param color Sets the text color for all the states (normal, selected,
2724 * focused) to be this color.
2725 */
Tor Norbye80756e32015-03-02 09:39:27 -08002726 public void setTextColor(int viewId, @ColorInt int color) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002727 setInt(viewId, "setTextColor", color);
2728 }
2729
Joe Onorato592d0652009-03-24 22:25:52 -07002730 /**
Adam Cohen3b4ca102012-12-14 12:00:41 -08002731 * Equivalent to calling {@link android.widget.AbsListView#setRemoteViewsAdapter(Intent)}.
Winson Chung499cb9f2010-07-16 11:18:17 -07002732 *
Winson Chung037300b2011-03-29 15:40:16 -07002733 * @param appWidgetId The id of the app widget which contains the specified view. (This
2734 * parameter is ignored in this deprecated method)
Adam Cohen50f3d1b2012-12-11 18:36:07 -08002735 * @param viewId The id of the {@link AdapterView}
Winson Chung037300b2011-03-29 15:40:16 -07002736 * @param intent The intent of the service which will be
2737 * providing data to the RemoteViewsAdapter
2738 * @deprecated This method has been deprecated. See
2739 * {@link android.widget.RemoteViews#setRemoteAdapter(int, Intent)}
2740 */
2741 @Deprecated
2742 public void setRemoteAdapter(int appWidgetId, int viewId, Intent intent) {
2743 setRemoteAdapter(viewId, intent);
2744 }
2745
2746 /**
2747 * Equivalent to calling {@link android.widget.AbsListView#setRemoteViewsAdapter(Intent)}.
2748 * Can only be used for App Widgets.
2749 *
Adam Cohen50f3d1b2012-12-11 18:36:07 -08002750 * @param viewId The id of the {@link AdapterView}
Winson Chung81f39eb2011-01-11 18:05:01 -08002751 * @param intent The intent of the service which will be
2752 * providing data to the RemoteViewsAdapter
2753 */
Winson Chung037300b2011-03-29 15:40:16 -07002754 public void setRemoteAdapter(int viewId, Intent intent) {
2755 addAction(new SetRemoteViewsAdapterIntent(viewId, intent));
Winson Chung499cb9f2010-07-16 11:18:17 -07002756 }
2757
2758 /**
Adam Cohen50f3d1b2012-12-11 18:36:07 -08002759 * Creates a simple Adapter for the viewId specified. The viewId must point to an AdapterView,
2760 * ie. {@link ListView}, {@link GridView}, {@link StackView} or {@link AdapterViewAnimator}.
2761 * This is a simpler but less flexible approach to populating collection widgets. Its use is
2762 * encouraged for most scenarios, as long as the total memory within the list of RemoteViews
2763 * is relatively small (ie. doesn't contain large or numerous Bitmaps, see {@link
2764 * RemoteViews#setImageViewBitmap}). In the case of numerous images, the use of API is still
2765 * possible by setting image URIs instead of Bitmaps, see {@link RemoteViews#setImageViewUri}.
2766 *
2767 * This API is supported in the compatibility library for previous API levels, see
2768 * RemoteViewsCompat.
2769 *
2770 * @param viewId The id of the {@link AdapterView}
2771 * @param list The list of RemoteViews which will populate the view specified by viewId.
Adam Cohenb00d9f02013-01-10 14:12:52 -08002772 * @param viewTypeCount The maximum number of unique layout id's used to construct the list of
2773 * RemoteViews. This count cannot change during the life-cycle of a given widget, so this
2774 * parameter should account for the maximum possible number of types that may appear in the
2775 * See {@link Adapter#getViewTypeCount()}.
Adam Cohen33f3aab2013-04-17 13:48:17 -07002776 *
2777 * @hide
Adam Cohen50f3d1b2012-12-11 18:36:07 -08002778 */
Adam Cohenb00d9f02013-01-10 14:12:52 -08002779 public void setRemoteAdapter(int viewId, ArrayList<RemoteViews> list, int viewTypeCount) {
2780 addAction(new SetRemoteViewsAdapterList(viewId, list, viewTypeCount));
Adam Cohen50f3d1b2012-12-11 18:36:07 -08002781 }
2782
2783 /**
Winson Chung499cb9f2010-07-16 11:18:17 -07002784 * Equivalent to calling {@link android.widget.AbsListView#smoothScrollToPosition(int, int)}.
2785 *
Scott Main93dc6422012-02-24 12:04:06 -08002786 * @param viewId The id of the view to change
Winson Chung499cb9f2010-07-16 11:18:17 -07002787 * @param position Scroll to this adapter position
2788 */
2789 public void setScrollPosition(int viewId, int position) {
2790 setInt(viewId, "smoothScrollToPosition", position);
2791 }
2792
2793 /**
2794 * Equivalent to calling {@link android.widget.AbsListView#smoothScrollToPosition(int, int)}.
2795 *
Scott Main93dc6422012-02-24 12:04:06 -08002796 * @param viewId The id of the view to change
Winson Chung95362592010-07-19 16:05:50 -07002797 * @param offset Scroll by this adapter position offset
Winson Chung499cb9f2010-07-16 11:18:17 -07002798 */
2799 public void setRelativeScrollPosition(int viewId, int offset) {
2800 setInt(viewId, "smoothScrollByOffset", offset);
2801 }
2802
2803 /**
Daniel Sandlerd5353b42012-06-21 09:28:07 -04002804 * Equivalent to calling {@link android.view.View#setPadding(int, int, int, int)}.
Daniel Sandler99d1f742012-05-21 16:14:14 -04002805 *
2806 * @param viewId The id of the view to change
2807 * @param left the left padding in pixels
2808 * @param top the top padding in pixels
2809 * @param right the right padding in pixels
2810 * @param bottom the bottom padding in pixels
2811 */
2812 public void setViewPadding(int viewId, int left, int top, int right, int bottom) {
2813 addAction(new ViewPaddingAction(viewId, left, top, right, bottom));
2814 }
2815
2816 /**
Selim Cinek65b2e7c2015-10-26 14:11:31 -07002817 * @hide
2818 * Equivalent to calling {@link android.view.ViewGroup.MarginLayoutParams#setMarginEnd(int)}.
2819 * Only works if the {@link View#getLayoutParams()} supports margins.
2820 * Hidden for now since we don't want to support this for all different layout margins yet.
2821 *
2822 * @param viewId The id of the view to change
2823 * @param endMargin the left padding in pixels
2824 */
2825 public void setViewLayoutMarginEnd(int viewId, int endMargin) {
Adrian Roos9b123cf2016-02-04 14:55:57 -08002826 addAction(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_MARGIN_END, endMargin));
2827 }
2828
2829 /**
2830 * Equivalent to setting {@link android.view.ViewGroup.LayoutParams#width}.
2831 * @hide
2832 */
2833 public void setViewLayoutWidth(int viewId, int layoutWidth) {
2834 mActions.add(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_WIDTH, layoutWidth));
Selim Cinek65b2e7c2015-10-26 14:11:31 -07002835 }
2836
2837 /**
Joe Onorato592d0652009-03-24 22:25:52 -07002838 * Call a method taking one boolean on a view in the layout for this RemoteViews.
2839 *
Scott Main93dc6422012-02-24 12:04:06 -08002840 * @param viewId The id of the view on which to call the method.
Joe Onorato592d0652009-03-24 22:25:52 -07002841 * @param methodName The name of the method to call.
2842 * @param value The value to pass to the method.
2843 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002844 public void setBoolean(int viewId, String methodName, boolean value) {
2845 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.BOOLEAN, value));
2846 }
2847
Joe Onorato592d0652009-03-24 22:25:52 -07002848 /**
2849 * Call a method taking one byte on a view in the layout for this RemoteViews.
2850 *
Scott Main93dc6422012-02-24 12:04:06 -08002851 * @param viewId The id of the view on which to call the method.
Joe Onorato592d0652009-03-24 22:25:52 -07002852 * @param methodName The name of the method to call.
2853 * @param value The value to pass to the method.
2854 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002855 public void setByte(int viewId, String methodName, byte value) {
2856 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.BYTE, value));
2857 }
2858
Joe Onorato592d0652009-03-24 22:25:52 -07002859 /**
2860 * Call a method taking one short on a view in the layout for this RemoteViews.
2861 *
Scott Main93dc6422012-02-24 12:04:06 -08002862 * @param viewId The id of the view on which to call the method.
Joe Onorato592d0652009-03-24 22:25:52 -07002863 * @param methodName The name of the method to call.
2864 * @param value The value to pass to the method.
2865 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002866 public void setShort(int viewId, String methodName, short value) {
2867 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.SHORT, value));
2868 }
2869
Joe Onorato592d0652009-03-24 22:25:52 -07002870 /**
2871 * Call a method taking one int on a view in the layout for this RemoteViews.
2872 *
Scott Main93dc6422012-02-24 12:04:06 -08002873 * @param viewId The id of the view on which to call the method.
Joe Onorato592d0652009-03-24 22:25:52 -07002874 * @param methodName The name of the method to call.
2875 * @param value The value to pass to the method.
2876 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002877 public void setInt(int viewId, String methodName, int value) {
2878 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.INT, value));
2879 }
2880
Joe Onorato592d0652009-03-24 22:25:52 -07002881 /**
2882 * Call a method taking one long on a view in the layout for this RemoteViews.
2883 *
Scott Main93dc6422012-02-24 12:04:06 -08002884 * @param viewId The id of the view on which to call the method.
Joe Onorato592d0652009-03-24 22:25:52 -07002885 * @param methodName The name of the method to call.
2886 * @param value The value to pass to the method.
2887 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002888 public void setLong(int viewId, String methodName, long value) {
2889 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.LONG, value));
2890 }
2891
Joe Onorato592d0652009-03-24 22:25:52 -07002892 /**
2893 * Call a method taking one float on a view in the layout for this RemoteViews.
2894 *
Scott Main93dc6422012-02-24 12:04:06 -08002895 * @param viewId The id of the view on which to call the method.
Joe Onorato592d0652009-03-24 22:25:52 -07002896 * @param methodName The name of the method to call.
2897 * @param value The value to pass to the method.
2898 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002899 public void setFloat(int viewId, String methodName, float value) {
2900 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.FLOAT, value));
2901 }
2902
Joe Onorato592d0652009-03-24 22:25:52 -07002903 /**
2904 * Call a method taking one double on a view in the layout for this RemoteViews.
2905 *
Scott Main93dc6422012-02-24 12:04:06 -08002906 * @param viewId The id of the view on which to call the method.
Joe Onorato592d0652009-03-24 22:25:52 -07002907 * @param methodName The name of the method to call.
2908 * @param value The value to pass to the method.
2909 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002910 public void setDouble(int viewId, String methodName, double value) {
2911 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.DOUBLE, value));
2912 }
2913
Joe Onorato592d0652009-03-24 22:25:52 -07002914 /**
2915 * Call a method taking one char on a view in the layout for this RemoteViews.
2916 *
Scott Main93dc6422012-02-24 12:04:06 -08002917 * @param viewId The id of the view on which to call the method.
Joe Onorato592d0652009-03-24 22:25:52 -07002918 * @param methodName The name of the method to call.
2919 * @param value The value to pass to the method.
2920 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002921 public void setChar(int viewId, String methodName, char value) {
2922 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.CHAR, value));
2923 }
2924
Joe Onorato592d0652009-03-24 22:25:52 -07002925 /**
2926 * Call a method taking one String on a view in the layout for this RemoteViews.
2927 *
Scott Main93dc6422012-02-24 12:04:06 -08002928 * @param viewId The id of the view on which to call the method.
Joe Onorato592d0652009-03-24 22:25:52 -07002929 * @param methodName The name of the method to call.
2930 * @param value The value to pass to the method.
2931 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002932 public void setString(int viewId, String methodName, String value) {
2933 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.STRING, value));
2934 }
2935
Joe Onorato592d0652009-03-24 22:25:52 -07002936 /**
2937 * Call a method taking one CharSequence on a view in the layout for this RemoteViews.
2938 *
Scott Main93dc6422012-02-24 12:04:06 -08002939 * @param viewId The id of the view on which to call the method.
Joe Onorato592d0652009-03-24 22:25:52 -07002940 * @param methodName The name of the method to call.
2941 * @param value The value to pass to the method.
2942 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002943 public void setCharSequence(int viewId, String methodName, CharSequence value) {
2944 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.CHAR_SEQUENCE, value));
2945 }
2946
Joe Onorato592d0652009-03-24 22:25:52 -07002947 /**
2948 * Call a method taking one Uri on a view in the layout for this RemoteViews.
2949 *
Scott Main93dc6422012-02-24 12:04:06 -08002950 * @param viewId The id of the view on which to call the method.
Joe Onorato592d0652009-03-24 22:25:52 -07002951 * @param methodName The name of the method to call.
2952 * @param value The value to pass to the method.
2953 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002954 public void setUri(int viewId, String methodName, Uri value) {
Jeff Sharkeya14acd22013-04-02 18:27:45 -07002955 if (value != null) {
2956 // Resolve any filesystem path before sending remotely
2957 value = value.getCanonicalUri();
2958 if (StrictMode.vmFileUriExposureEnabled()) {
2959 value.checkFileUriExposed("RemoteViews.setUri()");
2960 }
2961 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002962 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.URI, value));
2963 }
2964
Joe Onorato592d0652009-03-24 22:25:52 -07002965 /**
2966 * Call a method taking one Bitmap on a view in the layout for this RemoteViews.
2967 * @more
2968 * <p class="note">The bitmap will be flattened into the parcel if this object is
2969 * sent across processes, so it may end up using a lot of memory, and may be fairly slow.</p>
2970 *
Scott Main93dc6422012-02-24 12:04:06 -08002971 * @param viewId The id of the view on which to call the method.
Joe Onorato592d0652009-03-24 22:25:52 -07002972 * @param methodName The name of the method to call.
2973 * @param value The value to pass to the method.
2974 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002975 public void setBitmap(int viewId, String methodName, Bitmap value) {
Adam Cohen5d200642012-04-24 10:43:31 -07002976 addAction(new BitmapReflectionAction(viewId, methodName, value));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002977 }
2978
2979 /**
Bjorn Bringertd755b062010-01-06 17:15:37 +00002980 * Call a method taking one Bundle on a view in the layout for this RemoteViews.
2981 *
Scott Main93dc6422012-02-24 12:04:06 -08002982 * @param viewId The id of the view on which to call the method.
Bjorn Bringertd755b062010-01-06 17:15:37 +00002983 * @param methodName The name of the method to call.
2984 * @param value The value to pass to the method.
2985 */
2986 public void setBundle(int viewId, String methodName, Bundle value) {
2987 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.BUNDLE, value));
2988 }
2989
2990 /**
Scott Main93dc6422012-02-24 12:04:06 -08002991 * Call a method taking one Intent on a view in the layout for this RemoteViews.
Winson Chung499cb9f2010-07-16 11:18:17 -07002992 *
Scott Main93dc6422012-02-24 12:04:06 -08002993 * @param viewId The id of the view on which to call the method.
2994 * @param methodName The name of the method to call.
2995 * @param value The {@link android.content.Intent} to pass the method.
Winson Chung499cb9f2010-07-16 11:18:17 -07002996 */
2997 public void setIntent(int viewId, String methodName, Intent value) {
2998 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.INTENT, value));
2999 }
3000
3001 /**
Dan Sandlera22a3802015-05-13 00:12:47 -04003002 * Call a method taking one Icon on a view in the layout for this RemoteViews.
3003 *
3004 * @param viewId The id of the view on which to call the method.
3005 * @param methodName The name of the method to call.
3006 * @param value The {@link android.graphics.drawable.Icon} to pass the method.
3007 */
3008 public void setIcon(int viewId, String methodName, Icon value) {
3009 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.ICON, value));
3010 }
3011
3012 /**
Svetoslav Ganov33aef982012-09-13 12:49:03 -07003013 * Equivalent to calling View.setContentDescription(CharSequence).
Svetoslav Ganove261e282011-10-18 17:47:04 -07003014 *
Svetoslav Ganov33aef982012-09-13 12:49:03 -07003015 * @param viewId The id of the view whose content description should change.
3016 * @param contentDescription The new content description for the view.
Svetoslav Ganove261e282011-10-18 17:47:04 -07003017 */
3018 public void setContentDescription(int viewId, CharSequence contentDescription) {
3019 setCharSequence(viewId, "setContentDescription", contentDescription);
3020 }
3021
Svetoslav Ganov33aef982012-09-13 12:49:03 -07003022 /**
Svetoslav6c702902014-10-09 18:40:56 -07003023 * Equivalent to calling {@link android.view.View#setAccessibilityTraversalBefore(int)}.
3024 *
3025 * @param viewId The id of the view whose before view in accessibility traversal to set.
3026 * @param nextId The id of the next in the accessibility traversal.
3027 **/
3028 public void setAccessibilityTraversalBefore(int viewId, int nextId) {
3029 setInt(viewId, "setAccessibilityTraversalBefore", nextId);
3030 }
3031
3032 /**
3033 * Equivalent to calling {@link android.view.View#setAccessibilityTraversalAfter(int)}.
3034 *
3035 * @param viewId The id of the view whose after view in accessibility traversal to set.
3036 * @param nextId The id of the next in the accessibility traversal.
3037 **/
3038 public void setAccessibilityTraversalAfter(int viewId, int nextId) {
3039 setInt(viewId, "setAccessibilityTraversalAfter", nextId);
3040 }
3041
3042 /**
Svetoslav Ganov33aef982012-09-13 12:49:03 -07003043 * Equivalent to calling View.setLabelFor(int).
3044 *
3045 * @param viewId The id of the view whose property to set.
3046 * @param labeledId The id of a view for which this view serves as a label.
3047 */
3048 public void setLabelFor(int viewId, int labeledId) {
3049 setInt(viewId, "setLabelFor", labeledId);
3050 }
3051
Adam Cohen5d200642012-04-24 10:43:31 -07003052 private RemoteViews getRemoteViewsToApply(Context context) {
3053 if (hasLandscapeAndPortraitLayouts()) {
3054 int orientation = context.getResources().getConfiguration().orientation;
3055 if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
3056 return mLandscape;
3057 } else {
3058 return mPortrait;
3059 }
3060 }
3061 return this;
3062 }
3063
Svetoslav Ganove261e282011-10-18 17:47:04 -07003064 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003065 * Inflates the view hierarchy represented by this object and applies
3066 * all of the actions.
Jim Millere667a7a2012-08-09 19:22:32 -07003067 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003068 * <p><strong>Caller beware: this may throw</strong>
Jim Millere667a7a2012-08-09 19:22:32 -07003069 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003070 * @param context Default context to use
3071 * @param parent Parent that the resulting view hierarchy will be attached to. This method
3072 * does <strong>not</strong> attach the hierarchy. The caller should do so when appropriate.
3073 * @return The inflated view hierarchy
3074 */
3075 public View apply(Context context, ViewGroup parent) {
Jim Millere667a7a2012-08-09 19:22:32 -07003076 return apply(context, parent, null);
Dianne Hackborn1927ae82012-06-22 15:21:36 -07003077 }
3078
Dianne Hackborna1940212012-06-28 16:07:22 -07003079 /** @hide */
3080 public View apply(Context context, ViewGroup parent, OnClickHandler handler) {
Adam Cohen5d200642012-04-24 10:43:31 -07003081 RemoteViews rvToApply = getRemoteViewsToApply(context);
3082
Sunny Goyaldd292f42015-12-02 14:29:27 -08003083 View result = inflateView(context, rvToApply, parent);
3084 loadTransitionOverride(context, handler);
3085
3086 rvToApply.performApply(result, parent, handler);
3087
3088 return result;
3089 }
3090
3091 private View inflateView(Context context, RemoteViews rv, ViewGroup parent) {
Kenny Guy77320062014-08-27 21:37:15 +01003092 // RemoteViews may be built by an application installed in another
3093 // user. So build a context that loads resources from that user but
3094 // still returns the current users userId so settings like data / time formats
3095 // are loaded without requiring cross user persmissions.
3096 final Context contextForResources = getContextForResources(context);
3097 Context inflationContext = new ContextWrapper(context) {
3098 @Override
3099 public Resources getResources() {
3100 return contextForResources.getResources();
3101 }
3102 @Override
3103 public Resources.Theme getTheme() {
3104 return contextForResources.getTheme();
3105 }
Dan Sandler706274f2015-07-30 22:32:54 -04003106 @Override
3107 public String getPackageName() {
3108 return contextForResources.getPackageName();
3109 }
Kenny Guy77320062014-08-27 21:37:15 +01003110 };
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003111
Romain Guya5475592009-07-01 17:20:08 -07003112 LayoutInflater inflater = (LayoutInflater)
Kenny Guy77320062014-08-27 21:37:15 +01003113 context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003114
Kenny Guy77320062014-08-27 21:37:15 +01003115 // Clone inflater so we load resources from correct context and
3116 // we don't add a filter to the static version returned by getSystemService.
3117 inflater = inflater.cloneInContext(inflationContext);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003118 inflater.setFilter(this);
Sunny Goyaldd292f42015-12-02 14:29:27 -08003119 return inflater.inflate(rv.getLayoutId(), parent, false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003120 }
Adam Cohen5d200642012-04-24 10:43:31 -07003121
Gus Prevas1ed322b2015-09-17 17:34:46 -04003122 private static void loadTransitionOverride(Context context,
3123 RemoteViews.OnClickHandler handler) {
3124 if (handler != null && context.getResources().getBoolean(
3125 com.android.internal.R.bool.config_overrideRemoteViewsActivityTransition)) {
3126 TypedArray windowStyle = context.getTheme().obtainStyledAttributes(
3127 com.android.internal.R.styleable.Window);
3128 int windowAnimations = windowStyle.getResourceId(
3129 com.android.internal.R.styleable.Window_windowAnimationStyle, 0);
3130 TypedArray windowAnimationStyle = context.obtainStyledAttributes(
3131 windowAnimations, com.android.internal.R.styleable.WindowAnimation);
3132 handler.setEnterAnimationId(windowAnimationStyle.getResourceId(
3133 com.android.internal.R.styleable.
3134 WindowAnimation_activityOpenRemoteViewsEnterAnimation, 0));
3135 windowStyle.recycle();
3136 windowAnimationStyle.recycle();
3137 }
3138 }
3139
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003140 /**
Sunny Goyaldd292f42015-12-02 14:29:27 -08003141 * Implement this interface to receive a callback when
3142 * {@link #applyAsync} or {@link #reapplyAsync} is finished.
3143 * @hide
3144 */
3145 public interface OnViewAppliedListener {
3146 void onViewApplied(View v);
3147
3148 void onError(Exception e);
3149 }
3150
3151 /**
3152 * Applies the views asynchronously, moving as much of the task on the background
3153 * thread as possible.
3154 *
3155 * @see {@link #apply(Context, ViewGroup)}
3156 * @param context Default context to use
3157 * @param parent Parent that the resulting view hierarchy will be attached to. This method
3158 * does <strong>not</strong> attach the hierarchy. The caller should do so when appropriate.
3159 * @param listener the callback to run when all actions have been applied. May be null.
3160 * @param executor The executor to use. If null {@link AsyncTask#THREAD_POOL_EXECUTOR} is used.
3161 * @return CancellationSignal
3162 * @hide
3163 */
3164 public CancellationSignal applyAsync(
3165 Context context, ViewGroup parent, Executor executor, OnViewAppliedListener listener) {
3166 return applyAsync(context, parent, executor, listener, null);
3167 }
3168
3169 private CancellationSignal startTaskOnExecutor(AsyncApplyTask task, Executor executor) {
3170 CancellationSignal cancelSignal = new CancellationSignal();
3171 cancelSignal.setOnCancelListener(task);
3172
3173 task.executeOnExecutor(executor == null ? AsyncTask.THREAD_POOL_EXECUTOR : executor);
3174 return cancelSignal;
3175 }
3176
3177 /** @hide */
3178 public CancellationSignal applyAsync(Context context, ViewGroup parent,
3179 Executor executor, OnViewAppliedListener listener, OnClickHandler handler) {
3180 return startTaskOnExecutor(getAsyncApplyTask(context, parent, listener, handler), executor);
3181 }
3182
3183 private AsyncApplyTask getAsyncApplyTask(Context context, ViewGroup parent,
3184 OnViewAppliedListener listener, OnClickHandler handler) {
3185 return new AsyncApplyTask(getRemoteViewsToApply(context), parent, context, listener,
3186 handler, null);
3187 }
3188
3189 private class AsyncApplyTask extends AsyncTask<Void, Void, ViewTree>
3190 implements CancellationSignal.OnCancelListener {
3191 final RemoteViews mRV;
3192 final ViewGroup mParent;
3193 final Context mContext;
3194 final OnViewAppliedListener mListener;
3195 final OnClickHandler mHandler;
3196
3197 private View mResult;
3198 private ViewTree mTree;
3199 private Action[] mActions;
3200 private Exception mError;
3201
3202 private AsyncApplyTask(
3203 RemoteViews rv, ViewGroup parent, Context context, OnViewAppliedListener listener,
3204 OnClickHandler handler, View result) {
3205 mRV = rv;
3206 mParent = parent;
3207 mContext = context;
3208 mListener = listener;
3209 mHandler = handler;
3210
3211 mResult = result;
3212 loadTransitionOverride(context, handler);
3213 }
3214
3215 @Override
3216 protected ViewTree doInBackground(Void... params) {
3217 try {
3218 if (mResult == null) {
3219 mResult = inflateView(mContext, mRV, mParent);
3220 }
3221
3222 mTree = new ViewTree(mResult);
3223 if (mRV.mActions != null) {
3224 int count = mRV.mActions.size();
3225 mActions = new Action[count];
3226 for (int i = 0; i < count && !isCancelled(); i++) {
3227 // TODO: check if isCanclled in nested views.
3228 mActions[i] = mRV.mActions.get(i).initActionAsync(mTree, mParent, mHandler);
3229 }
3230 } else {
3231 mActions = null;
3232 }
3233 return mTree;
3234 } catch (Exception e) {
3235 mError = e;
3236 return null;
3237 }
3238 }
3239
3240 @Override
3241 protected void onPostExecute(ViewTree viewTree) {
3242 if (mError == null) {
3243 try {
3244 if (mActions != null) {
3245 OnClickHandler handler = mHandler == null
3246 ? DEFAULT_ON_CLICK_HANDLER : mHandler;
3247 for (Action a : mActions) {
3248 a.apply(viewTree.mRoot, mParent, handler);
3249 }
3250 }
3251 } catch (Exception e) {
3252 mError = e;
3253 }
3254 }
3255
3256 if (mListener != null) {
3257 if (mError != null) {
3258 mListener.onError(mError);
3259 } else {
3260 mListener.onViewApplied(viewTree.mRoot);
3261 }
3262 } else if (mError != null) {
3263 if (mError instanceof ActionException) {
3264 throw (ActionException) mError;
3265 } else {
3266 throw new ActionException(mError);
3267 }
3268 }
3269 }
3270
3271 @Override
3272 public void onCancel() {
3273 cancel(true);
3274 }
3275 }
3276
3277 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003278 * Applies all of the actions to the provided view.
3279 *
3280 * <p><strong>Caller beware: this may throw</strong>
Jim Millere667a7a2012-08-09 19:22:32 -07003281 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003282 * @param v The view to apply the actions to. This should be the result of
3283 * the {@link #apply(Context,ViewGroup)} call.
3284 */
3285 public void reapply(Context context, View v) {
Jim Millere667a7a2012-08-09 19:22:32 -07003286 reapply(context, v, null);
Dianne Hackborna1940212012-06-28 16:07:22 -07003287 }
3288
3289 /** @hide */
3290 public void reapply(Context context, View v, OnClickHandler handler) {
Adam Cohen5d200642012-04-24 10:43:31 -07003291 RemoteViews rvToApply = getRemoteViewsToApply(context);
3292
3293 // In the case that a view has this RemoteViews applied in one orientation, is persisted
3294 // across orientation change, and has the RemoteViews re-applied in the new orientation,
3295 // we throw an exception, since the layouts may be completely unrelated.
3296 if (hasLandscapeAndPortraitLayouts()) {
3297 if (v.getId() != rvToApply.getLayoutId()) {
3298 throw new RuntimeException("Attempting to re-apply RemoteViews to a view that" +
3299 " that does not share the same root layout id.");
3300 }
3301 }
3302
Dianne Hackborna1940212012-06-28 16:07:22 -07003303 rvToApply.performApply(v, (ViewGroup) v.getParent(), handler);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003304 }
3305
Sunny Goyaldd292f42015-12-02 14:29:27 -08003306 /**
3307 * Applies all the actions to the provided view, moving as much of the task on the background
3308 * thread as possible.
3309 *
3310 * @see {@link #reapply(Context, View)}
3311 * @param context Default context to use
3312 * @param v The view to apply the actions to. This should be the result of
3313 * the {@link #apply(Context,ViewGroup)} call.
3314 * @param listener the callback to run when all actions have been applied. May be null.
3315 * @param executor The executor to use. If null {@link AsyncTask#THREAD_POOL_EXECUTOR} is used
3316 * @return CancellationSignal
3317 * @hide
3318 */
3319 public CancellationSignal reapplyAsync(
3320 Context context, View v, Executor executor, OnViewAppliedListener listener) {
3321 return reapplyAsync(context, v, executor, listener, null);
3322 }
3323
3324 /** @hide */
3325 public CancellationSignal reapplyAsync(Context context, View v, Executor executor,
3326 OnViewAppliedListener listener, OnClickHandler handler) {
3327 RemoteViews rvToApply = getRemoteViewsToApply(context);
3328
3329 // In the case that a view has this RemoteViews applied in one orientation, is persisted
3330 // across orientation change, and has the RemoteViews re-applied in the new orientation,
3331 // we throw an exception, since the layouts may be completely unrelated.
3332 if (hasLandscapeAndPortraitLayouts()) {
3333 if (v.getId() != rvToApply.getLayoutId()) {
3334 throw new RuntimeException("Attempting to re-apply RemoteViews to a view that" +
3335 " that does not share the same root layout id.");
3336 }
3337 }
3338
3339 return startTaskOnExecutor(new AsyncApplyTask(rvToApply, (ViewGroup) v.getParent(),
3340 context, listener, handler, v), executor);
3341 }
3342
Dianne Hackborna1940212012-06-28 16:07:22 -07003343 private void performApply(View v, ViewGroup parent, OnClickHandler handler) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003344 if (mActions != null) {
Jim Millere667a7a2012-08-09 19:22:32 -07003345 handler = handler == null ? DEFAULT_ON_CLICK_HANDLER : handler;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003346 final int count = mActions.size();
3347 for (int i = 0; i < count; i++) {
3348 Action a = mActions.get(i);
Dianne Hackborna1940212012-06-28 16:07:22 -07003349 a.apply(v, parent, handler);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003350 }
3351 }
3352 }
3353
Kenny Guy77320062014-08-27 21:37:15 +01003354 private Context getContextForResources(Context context) {
Svetoslav976e8bd2014-07-16 15:12:03 -07003355 if (mApplication != null) {
3356 if (context.getUserId() == UserHandle.getUserId(mApplication.uid)
3357 && context.getPackageName().equals(mApplication.packageName)) {
3358 return context;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003359 }
Svetoslav976e8bd2014-07-16 15:12:03 -07003360 try {
3361 return context.createApplicationContext(mApplication,
3362 Context.CONTEXT_RESTRICTED);
3363 } catch (NameNotFoundException e) {
Svet Ganov0da85b62014-08-06 14:11:37 -07003364 Log.e(LOG_TAG, "Package name " + mApplication.packageName + " not found");
Svetoslav976e8bd2014-07-16 15:12:03 -07003365 }
3366 }
3367
3368 return context;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003369 }
3370
Christoph Studer4600f9b2014-07-22 22:44:43 +02003371 /**
3372 * Returns the number of actions in this RemoteViews. Can be used as a sequence number.
3373 *
3374 * @hide
3375 */
3376 public int getSequenceNumber() {
3377 return (mActions == null) ? 0 : mActions.size();
3378 }
3379
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003380 /* (non-Javadoc)
3381 * Used to restrict the views which can be inflated
Jim Millere667a7a2012-08-09 19:22:32 -07003382 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003383 * @see android.view.LayoutInflater.Filter#onLoadClass(java.lang.Class)
3384 */
Gilles Debunnee6ac8b92010-06-17 10:55:04 -07003385 public boolean onLoadClass(Class clazz) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003386 return clazz.isAnnotationPresent(RemoteView.class);
3387 }
Adam Cohen5d200642012-04-24 10:43:31 -07003388
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003389 public int describeContents() {
3390 return 0;
3391 }
3392
3393 public void writeToParcel(Parcel dest, int flags) {
Adam Cohen5d200642012-04-24 10:43:31 -07003394 if (!hasLandscapeAndPortraitLayouts()) {
3395 dest.writeInt(MODE_NORMAL);
3396 // We only write the bitmap cache if we are the root RemoteViews, as this cache
3397 // is shared by all children.
3398 if (mIsRoot) {
3399 mBitmapCache.writeBitmapsToParcel(dest, flags);
3400 }
Svet Ganov0da85b62014-08-06 14:11:37 -07003401 dest.writeParcelable(mApplication, flags);
Adam Cohen5d200642012-04-24 10:43:31 -07003402 dest.writeInt(mLayoutId);
3403 dest.writeInt(mIsWidgetCollectionChild ? 1 : 0);
3404 int count;
3405 if (mActions != null) {
3406 count = mActions.size();
3407 } else {
3408 count = 0;
3409 }
3410 dest.writeInt(count);
3411 for (int i=0; i<count; i++) {
3412 Action a = mActions.get(i);
3413 a.writeToParcel(dest, 0);
3414 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003415 } else {
Adam Cohen5d200642012-04-24 10:43:31 -07003416 dest.writeInt(MODE_HAS_LANDSCAPE_AND_PORTRAIT);
3417 // We only write the bitmap cache if we are the root RemoteViews, as this cache
3418 // is shared by all children.
3419 if (mIsRoot) {
3420 mBitmapCache.writeBitmapsToParcel(dest, flags);
3421 }
3422 mLandscape.writeToParcel(dest, flags);
3423 mPortrait.writeToParcel(dest, flags);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003424 }
Svet Ganov0da85b62014-08-06 14:11:37 -07003425 }
Svetoslav976e8bd2014-07-16 15:12:03 -07003426
Svet Ganov0da85b62014-08-06 14:11:37 -07003427 private static ApplicationInfo getApplicationInfo(String packageName, int userId) {
Svetoslavb6242442014-09-19 13:21:55 -07003428 if (packageName == null) {
3429 return null;
3430 }
3431
Svet Ganov0da85b62014-08-06 14:11:37 -07003432 // Get the application for the passed in package and user.
3433 Application application = ActivityThread.currentApplication();
3434 if (application == null) {
3435 throw new IllegalStateException("Cannot create remote views out of an aplication.");
3436 }
3437
3438 ApplicationInfo applicationInfo = application.getApplicationInfo();
3439 if (UserHandle.getUserId(applicationInfo.uid) != userId
3440 || !applicationInfo.packageName.equals(packageName)) {
3441 try {
Svetoslav14494a82014-08-18 10:43:27 -07003442 Context context = application.getBaseContext().createPackageContextAsUser(
Svet Ganov0da85b62014-08-06 14:11:37 -07003443 packageName, 0, new UserHandle(userId));
3444 applicationInfo = context.getApplicationInfo();
3445 } catch (NameNotFoundException nnfe) {
3446 throw new IllegalArgumentException("No such package " + packageName);
3447 }
3448 }
3449
3450 return applicationInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003451 }
3452
3453 /**
3454 * Parcelable.Creator that instantiates RemoteViews objects
3455 */
3456 public static final Parcelable.Creator<RemoteViews> CREATOR = new Parcelable.Creator<RemoteViews>() {
3457 public RemoteViews createFromParcel(Parcel parcel) {
3458 return new RemoteViews(parcel);
3459 }
3460
3461 public RemoteViews[] newArray(int size) {
3462 return new RemoteViews[size];
3463 }
3464 };
Sunny Goyaldd292f42015-12-02 14:29:27 -08003465
3466 /**
3467 * A representation of the view hierarchy. Only views which have a valid ID are added
3468 * and can be searched.
3469 */
3470 private static class ViewTree {
3471 private final View mRoot;
3472
3473 private ArrayList<ViewTree> mChildren;
3474
3475 private ViewTree(View root) {
3476 mRoot = root;
3477 }
3478
3479 public void createTree() {
3480 if (mChildren != null) {
3481 return;
3482 }
3483
3484 mChildren = new ArrayList<>();
3485 if (mRoot instanceof ViewGroup && mRoot.isRootNamespace()) {
3486 ViewGroup vg = (ViewGroup) mRoot;
3487 int count = vg.getChildCount();
3488 for (int i = 0; i < count; i++) {
3489 addViewChild(vg.getChildAt(i));
3490 }
3491 }
3492 }
3493
3494 public ViewTree findViewTreeById(int id) {
3495 if (mRoot.getId() == id) {
3496 return this;
3497 }
3498 if (mChildren == null) {
3499 return null;
3500 }
3501 for (ViewTree tree : mChildren) {
3502 ViewTree result = tree.findViewTreeById(id);
3503 if (result != null) {
3504 return result;
3505 }
3506 }
3507 return null;
3508 }
3509
3510 public View findViewById(int id) {
3511 if (mChildren == null) {
3512 return mRoot.findViewById(id);
3513 }
3514 ViewTree tree = findViewTreeById(id);
3515 return tree == null ? null : tree.mRoot;
3516 }
3517
3518 public void addChild(ViewTree child) {
3519 if (mChildren == null) {
3520 mChildren = new ArrayList<>();
3521 }
3522 child.createTree();
3523 mChildren.add(child);
3524 }
3525
3526 private void addViewChild(View v) {
3527 final ViewTree target;
3528
3529 // If the view has a valid id, i.e., if can be found using findViewById, add it to the
3530 // tree, otherwise skip this view and add its children instead.
3531 if (v.getId() != 0) {
3532 ViewTree tree = new ViewTree(v);
3533 mChildren.add(tree);
3534 target = tree;
3535 } else {
3536 target = this;
3537 }
3538
3539 if (v instanceof ViewGroup && v.isRootNamespace()) {
3540 if (target.mChildren == null) {
3541 target.mChildren = new ArrayList<>();
3542 ViewGroup vg = (ViewGroup) v;
3543 int count = vg.getChildCount();
3544 for (int i = 0; i < count; i++) {
3545 target.addViewChild(vg.getChildAt(i));
3546 }
3547 }
3548 }
3549 }
3550 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003551}