blob: 8bd63dfc20752824ed047a8146b88e71f417dfd6 [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;
Adrian Roos7da889d2016-03-16 18:38:58 -070063import com.android.internal.util.Preconditions;
Gus Prevas1ed322b2015-09-17 17:34:46 -040064
Winson Chungdc6f79b2012-04-17 17:27:31 -070065import java.lang.annotation.ElementType;
66import java.lang.annotation.Retention;
67import java.lang.annotation.RetentionPolicy;
68import java.lang.annotation.Target;
69import java.lang.reflect.Method;
70import java.util.ArrayList;
Adam Cohenfbe44b72012-09-19 20:36:23 -070071import java.util.HashMap;
Sunny Goyaldd292f42015-12-02 14:29:27 -080072import java.util.concurrent.Executor;
Winson Chungdc6f79b2012-04-17 17:27:31 -070073
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080074/**
75 * A class that describes a view hierarchy that can be displayed in
76 * another process. The hierarchy is inflated from a layout resource
77 * file, and this class provides some basic operations for modifying
78 * the content of the inflated hierarchy.
79 */
80public class RemoteViews implements Parcelable, Filter {
Jim Millere667a7a2012-08-09 19:22:32 -070081
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080082 private static final String LOG_TAG = "RemoteViews";
Jim Millere667a7a2012-08-09 19:22:32 -070083
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080084 /**
Winson Chung81f39eb2011-01-11 18:05:01 -080085 * The intent extra that contains the appWidgetId.
86 * @hide
87 */
88 static final String EXTRA_REMOTEADAPTER_APPWIDGET_ID = "remoteAdapterAppWidgetId";
89
90 /**
Svetoslav976e8bd2014-07-16 15:12:03 -070091 * Application that hosts the remote views.
92 *
93 * @hide
Jeff Sharkey6d515712012-09-20 16:06:08 -070094 */
Svetoslav976e8bd2014-07-16 15:12:03 -070095 private ApplicationInfo mApplication;
Jeff Sharkey6d515712012-09-20 16:06:08 -070096
97 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080098 * The resource ID of the layout file. (Added to the parcel)
99 */
Gilles Debunne30301932010-06-16 18:32:00 -0700100 private final int mLayoutId;
Romain Guya5475592009-07-01 17:20:08 -0700101
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800102 /**
103 * An array of actions to perform on the view tree once it has been
104 * inflated
105 */
106 private ArrayList<Action> mActions;
Jim Millere667a7a2012-08-09 19:22:32 -0700107
Winson Chung3ec9a452010-09-23 16:40:28 -0700108 /**
109 * A class to keep track of memory usage by this RemoteViews
110 */
111 private MemoryUsageCounter mMemoryUsageCounter;
112
Adam Cohen5d200642012-04-24 10:43:31 -0700113 /**
114 * Maps bitmaps to unique indicies to avoid Bitmap duplication.
115 */
116 private BitmapCache mBitmapCache;
117
118 /**
119 * Indicates whether or not this RemoteViews object is contained as a child of any other
120 * RemoteViews.
121 */
122 private boolean mIsRoot = true;
123
124 /**
125 * Constants to whether or not this RemoteViews is composed of a landscape and portrait
126 * RemoteViews.
127 */
128 private static final int MODE_NORMAL = 0;
129 private static final int MODE_HAS_LANDSCAPE_AND_PORTRAIT = 1;
130
131 /**
132 * Used in conjunction with the special constructor
133 * {@link #RemoteViews(RemoteViews, RemoteViews)} to keep track of the landscape and portrait
134 * RemoteViews.
135 */
136 private RemoteViews mLandscape = null;
137 private RemoteViews mPortrait = null;
138
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800139 /**
Adam Cohenca6fd842010-09-03 18:10:35 -0700140 * This flag indicates whether this RemoteViews object is being created from a
141 * RemoteViewsService for use as a child of a widget collection. This flag is used
142 * to determine whether or not certain features are available, in particular,
143 * setting on click extras and setting on click pending intents. The former is enabled,
144 * and the latter disabled when this flag is true.
145 */
Dianne Hackborn1927ae82012-06-22 15:21:36 -0700146 private boolean mIsWidgetCollectionChild = false;
147
148 private static final OnClickHandler DEFAULT_ON_CLICK_HANDLER = new OnClickHandler();
Adam Cohenca6fd842010-09-03 18:10:35 -0700149
Romain Guy484f4d62013-07-22 16:39:16 -0700150 private static final Object[] sMethodsLock = new Object[0];
151 private static final ArrayMap<Class<? extends View>, ArrayMap<MutablePair<String, Class<?>>, Method>> sMethods =
152 new ArrayMap<Class<? extends View>, ArrayMap<MutablePair<String, Class<?>>, Method>>();
Sunny Goyaldd292f42015-12-02 14:29:27 -0800153 private static final ArrayMap<Method, Method> sAsyncMethods = new ArrayMap<>();
154
Romain Guye4d4e202013-07-22 13:02:02 -0700155 private static final ThreadLocal<Object[]> sInvokeArgsTls = new ThreadLocal<Object[]>() {
156 @Override
157 protected Object[] initialValue() {
158 return new Object[1];
159 }
160 };
161
Adam Cohenca6fd842010-09-03 18:10:35 -0700162 /**
Adrian Roosfe84e1f2015-11-04 15:55:39 -0800163 * @hide
164 */
165 public void setRemoteInputs(int viewId, RemoteInput[] remoteInputs) {
166 mActions.add(new SetRemoteInputsAction(viewId, remoteInputs));
167 }
168
169 /**
Romain Guy484f4d62013-07-22 16:39:16 -0700170 * Handle with care!
171 */
172 static class MutablePair<F, S> {
173 F first;
174 S second;
175
176 MutablePair(F first, S second) {
177 this.first = first;
178 this.second = second;
179 }
180
181 @Override
182 public boolean equals(Object o) {
183 if (!(o instanceof MutablePair)) {
184 return false;
185 }
186 MutablePair<?, ?> p = (MutablePair<?, ?>) o;
187 return Objects.equal(p.first, first) && Objects.equal(p.second, second);
188 }
189
190 @Override
191 public int hashCode() {
192 return (first == null ? 0 : first.hashCode()) ^ (second == null ? 0 : second.hashCode());
193 }
194 }
195
196 /**
197 * This pair is used to perform lookups in sMethods without causing allocations.
198 */
199 private final MutablePair<String, Class<?>> mPair =
200 new MutablePair<String, Class<?>>(null, null);
201
202 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800203 * This annotation indicates that a subclass of View is alllowed to be used
Romain Guya5475592009-07-01 17:20:08 -0700204 * with the {@link RemoteViews} mechanism.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800205 */
206 @Target({ ElementType.TYPE })
207 @Retention(RetentionPolicy.RUNTIME)
208 public @interface RemoteView {
209 }
210
211 /**
212 * Exception to send when something goes wrong executing an action
213 *
214 */
215 public static class ActionException extends RuntimeException {
216 public ActionException(Exception ex) {
217 super(ex);
218 }
219 public ActionException(String message) {
220 super(message);
221 }
222 }
Adam Cohena32edd42010-10-26 10:35:01 -0700223
Dianne Hackborn1927ae82012-06-22 15:21:36 -0700224 /** @hide */
225 public static class OnClickHandler {
Gus Prevas1ed322b2015-09-17 17:34:46 -0400226
227 private int mEnterAnimationId;
228
Dianne Hackborn1927ae82012-06-22 15:21:36 -0700229 public boolean onClickHandler(View view, PendingIntent pendingIntent,
Winson Chungae615982010-11-01 12:14:49 -0700230 Intent fillInIntent) {
231 try {
232 // TODO: Unregister this handler if PendingIntent.FLAG_ONE_SHOT?
Winson Chungdc6f79b2012-04-17 17:27:31 -0700233 Context context = view.getContext();
Gus Prevas1ed322b2015-09-17 17:34:46 -0400234 ActivityOptions opts;
235 if (mEnterAnimationId != 0) {
236 opts = ActivityOptions.makeCustomAnimation(context, mEnterAnimationId, 0);
237 } else {
238 opts = ActivityOptions.makeScaleUpAnimation(view,
239 0, 0,
240 view.getMeasuredWidth(), view.getMeasuredHeight());
241 }
Winson Chungae615982010-11-01 12:14:49 -0700242 context.startIntentSender(
243 pendingIntent.getIntentSender(), fillInIntent,
244 Intent.FLAG_ACTIVITY_NEW_TASK,
Winson Chungdc6f79b2012-04-17 17:27:31 -0700245 Intent.FLAG_ACTIVITY_NEW_TASK, 0, opts.toBundle());
Winson Chungae615982010-11-01 12:14:49 -0700246 } catch (IntentSender.SendIntentException e) {
247 android.util.Log.e(LOG_TAG, "Cannot send pending intent: ", e);
248 return false;
249 } catch (Exception e) {
250 android.util.Log.e(LOG_TAG, "Cannot send pending intent due to " +
251 "unknown exception: ", e);
252 return false;
253 }
254 return true;
255 }
Gus Prevas1ed322b2015-09-17 17:34:46 -0400256
257 public void setEnterAnimationId(int enterAnimationId) {
258 mEnterAnimationId = enterAnimationId;
259 }
Dianne Hackborn1927ae82012-06-22 15:21:36 -0700260 }
261
262 /**
263 * Base class for all actions that can be performed on an
264 * inflated view.
265 *
266 * SUBCLASSES MUST BE IMMUTABLE SO CLONE WORKS!!!!!
267 */
268 private abstract static class Action implements Parcelable {
Dianne Hackborna1940212012-06-28 16:07:22 -0700269 public abstract void apply(View root, ViewGroup rootParent,
270 OnClickHandler handler) throws ActionException;
Dianne Hackborn1927ae82012-06-22 15:21:36 -0700271
Adam Cohenfbe44b72012-09-19 20:36:23 -0700272 public static final int MERGE_REPLACE = 0;
273 public static final int MERGE_APPEND = 1;
274 public static final int MERGE_IGNORE = 2;
275
Dianne Hackborn1927ae82012-06-22 15:21:36 -0700276 public int describeContents() {
277 return 0;
278 }
279
280 /**
281 * Overridden by each class to report on it's own memory usage
282 */
283 public void updateMemoryUsageEstimate(MemoryUsageCounter counter) {
Romain Guye4d4e202013-07-22 13:02:02 -0700284 // We currently only calculate Bitmap memory usage, so by default,
285 // don't do anything here
Dianne Hackborn1927ae82012-06-22 15:21:36 -0700286 }
Adam Cohen5d200642012-04-24 10:43:31 -0700287
288 public void setBitmapCache(BitmapCache bitmapCache) {
289 // Do nothing
290 }
Adam Cohenfbe44b72012-09-19 20:36:23 -0700291
292 public int mergeBehavior() {
293 return MERGE_REPLACE;
294 }
295
296 public abstract String getActionName();
297
298 public String getUniqueKey() {
299 return (getActionName() + viewId);
300 }
301
Sunny Goyaldd292f42015-12-02 14:29:27 -0800302 /**
303 * This is called on the background thread. It should perform any non-ui computations
304 * and return the final action which will run on the UI thread.
305 * Override this if some of the tasks can be performed async.
306 */
307 public Action initActionAsync(ViewTree root, ViewGroup rootParent, OnClickHandler handler) {
308 return this;
309 }
310
Adam Cohenfbe44b72012-09-19 20:36:23 -0700311 int viewId;
312 }
313
Adam Cohenbd1e0072012-09-21 16:51:50 -0700314 /**
Sunny Goyaldd292f42015-12-02 14:29:27 -0800315 * Action class used during async inflation of RemoteViews. Subclasses are not parcelable.
316 */
317 private static abstract class RuntimeAction extends Action {
318 @Override
319 public final String getActionName() {
320 return "RuntimeAction";
321 }
322
323 @Override
324 public final void writeToParcel(Parcel dest, int flags) {
325 throw new UnsupportedOperationException();
326 }
327 }
328
329 // Constant used during async execution. It is not parcelable.
330 private static final Action ACTION_NOOP = new RuntimeAction() {
331 @Override
332 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) { }
333 };
334
335 /**
Adam Cohenbd1e0072012-09-21 16:51:50 -0700336 * Merges the passed RemoteViews actions with this RemoteViews actions according to
337 * action-specific merge rules.
Svetoslav976e8bd2014-07-16 15:12:03 -0700338 *
Adam Cohenbd1e0072012-09-21 16:51:50 -0700339 * @param newRv
Svetoslav976e8bd2014-07-16 15:12:03 -0700340 *
Adam Cohenbd1e0072012-09-21 16:51:50 -0700341 * @hide
342 */
Adam Cohenfbe44b72012-09-19 20:36:23 -0700343 public void mergeRemoteViews(RemoteViews newRv) {
Adam Cohen3ff2d862012-09-26 14:07:57 -0700344 if (newRv == null) return;
Adam Cohenfbe44b72012-09-19 20:36:23 -0700345 // We first copy the new RemoteViews, as the process of merging modifies the way the actions
346 // reference the bitmap cache. We don't want to modify the object as it may need to
347 // be merged and applied multiple times.
Adam Cohen3ff2d862012-09-26 14:07:57 -0700348 RemoteViews copy = newRv.clone();
Adam Cohenfbe44b72012-09-19 20:36:23 -0700349
350 HashMap<String, Action> map = new HashMap<String, Action>();
351 if (mActions == null) {
352 mActions = new ArrayList<Action>();
353 }
354
355 int count = mActions.size();
356 for (int i = 0; i < count; i++) {
357 Action a = mActions.get(i);
358 map.put(a.getUniqueKey(), a);
359 }
360
361 ArrayList<Action> newActions = copy.mActions;
362 if (newActions == null) return;
363 count = newActions.size();
364 for (int i = 0; i < count; i++) {
365 Action a = newActions.get(i);
366 String key = newActions.get(i).getUniqueKey();
Adam Cohen3ff2d862012-09-26 14:07:57 -0700367 int mergeBehavior = newActions.get(i).mergeBehavior();
Adam Cohenfbe44b72012-09-19 20:36:23 -0700368 if (map.containsKey(key) && mergeBehavior == Action.MERGE_REPLACE) {
369 mActions.remove(map.get(key));
370 map.remove(key);
371 }
372
373 // If the merge behavior is ignore, we don't bother keeping the extra action
374 if (mergeBehavior == Action.MERGE_REPLACE || mergeBehavior == Action.MERGE_APPEND) {
375 mActions.add(a);
376 }
377 }
378
379 // Because pruning can remove the need for bitmaps, we reconstruct the bitmap cache
380 mBitmapCache = new BitmapCache();
381 setBitmapCache(mBitmapCache);
Romain Guya5475592009-07-01 17:20:08 -0700382 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800383
Adam Cohen1480fdd2010-08-25 17:24:53 -0700384 private class SetEmptyView extends Action {
385 int viewId;
386 int emptyViewId;
387
388 public final static int TAG = 6;
389
390 SetEmptyView(int viewId, int emptyViewId) {
391 this.viewId = viewId;
392 this.emptyViewId = emptyViewId;
393 }
394
395 SetEmptyView(Parcel in) {
396 this.viewId = in.readInt();
397 this.emptyViewId = in.readInt();
398 }
399
400 public void writeToParcel(Parcel out, int flags) {
401 out.writeInt(TAG);
402 out.writeInt(this.viewId);
403 out.writeInt(this.emptyViewId);
404 }
405
406 @Override
Dianne Hackborna1940212012-06-28 16:07:22 -0700407 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
Adam Cohen1480fdd2010-08-25 17:24:53 -0700408 final View view = root.findViewById(viewId);
409 if (!(view instanceof AdapterView<?>)) return;
410
411 AdapterView<?> adapterView = (AdapterView<?>) view;
412
413 final View emptyView = root.findViewById(emptyViewId);
414 if (emptyView == null) return;
415
416 adapterView.setEmptyView(emptyView);
417 }
Adam Cohenfbe44b72012-09-19 20:36:23 -0700418
419 public String getActionName() {
420 return "SetEmptyView";
421 }
Adam Cohen1480fdd2010-08-25 17:24:53 -0700422 }
423
Adam Cohen35ae9ca2010-09-20 15:20:41 -0700424 private class SetOnClickFillInIntent extends Action {
425 public SetOnClickFillInIntent(int id, Intent fillInIntent) {
426 this.viewId = id;
427 this.fillInIntent = fillInIntent;
428 }
429
430 public SetOnClickFillInIntent(Parcel parcel) {
431 viewId = parcel.readInt();
432 fillInIntent = Intent.CREATOR.createFromParcel(parcel);
433 }
434
435 public void writeToParcel(Parcel dest, int flags) {
436 dest.writeInt(TAG);
437 dest.writeInt(viewId);
438 fillInIntent.writeToParcel(dest, 0 /* no flags */);
439 }
440
441 @Override
Dianne Hackborna1940212012-06-28 16:07:22 -0700442 public void apply(View root, ViewGroup rootParent, final OnClickHandler handler) {
Adam Cohen35ae9ca2010-09-20 15:20:41 -0700443 final View target = root.findViewById(viewId);
Joe Onorato2b69ce42010-10-31 11:35:41 -0700444 if (target == null) return;
Adam Cohen35ae9ca2010-09-20 15:20:41 -0700445
446 if (!mIsWidgetCollectionChild) {
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800447 Log.e(LOG_TAG, "The method setOnClickFillInIntent is available " +
Adam Cohen35ae9ca2010-09-20 15:20:41 -0700448 "only from RemoteViewsFactory (ie. on collection items).");
449 return;
450 }
Adam Cohena32edd42010-10-26 10:35:01 -0700451 if (target == root) {
452 target.setTagInternal(com.android.internal.R.id.fillInIntent, fillInIntent);
Romain Guye4d4e202013-07-22 13:02:02 -0700453 } else if (fillInIntent != null) {
Adam Cohen35ae9ca2010-09-20 15:20:41 -0700454 OnClickListener listener = new OnClickListener() {
455 public void onClick(View v) {
456 // Insure that this view is a child of an AdapterView
457 View parent = (View) v.getParent();
Sunny Goyalb880d162016-02-24 14:38:59 -0800458 // Break the for loop on the first encounter of:
459 // 1) an AdapterView,
460 // 2) an AppWidgetHostView that is not a RemoteViewsFrameLayout, or
461 // 3) a null parent.
462 // 2) and 3) are unexpected and catch the case where a child is not
463 // correctly parented in an AdapterView.
Adam Cohen85a08f12012-11-06 11:24:23 -0800464 while (parent != null && !(parent instanceof AdapterView<?>)
Sunny Goyalb880d162016-02-24 14:38:59 -0800465 && !((parent instanceof AppWidgetHostView) &&
466 !(parent instanceof RemoteViewsAdapter.RemoteViewsFrameLayout))) {
Adam Cohen35ae9ca2010-09-20 15:20:41 -0700467 parent = (View) parent.getParent();
468 }
469
Sunny Goyalb880d162016-02-24 14:38:59 -0800470 if (!(parent instanceof AdapterView<?>)) {
Adam Cohen35ae9ca2010-09-20 15:20:41 -0700471 // Somehow they've managed to get this far without having
472 // and AdapterView as a parent.
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800473 Log.e(LOG_TAG, "Collection item doesn't have AdapterView parent");
Adam Cohen35ae9ca2010-09-20 15:20:41 -0700474 return;
475 }
476
477 // Insure that a template pending intent has been set on an ancestor
478 if (!(parent.getTag() instanceof PendingIntent)) {
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800479 Log.e(LOG_TAG, "Attempting setOnClickFillInIntent without" +
Adam Cohena32edd42010-10-26 10:35:01 -0700480 " calling setPendingIntentTemplate on parent.");
Adam Cohen35ae9ca2010-09-20 15:20:41 -0700481 return;
482 }
483
484 PendingIntent pendingIntent = (PendingIntent) parent.getTag();
485
Romain Guye4d4e202013-07-22 13:02:02 -0700486 final Rect rect = getSourceBounds(v);
Adam Cohen35ae9ca2010-09-20 15:20:41 -0700487
488 fillInIntent.setSourceBounds(rect);
Dianne Hackborna1940212012-06-28 16:07:22 -0700489 handler.onClickHandler(v, pendingIntent, fillInIntent);
Adam Cohen35ae9ca2010-09-20 15:20:41 -0700490 }
491
492 };
493 target.setOnClickListener(listener);
494 }
495 }
496
Adam Cohenfbe44b72012-09-19 20:36:23 -0700497 public String getActionName() {
498 return "SetOnClickFillInIntent";
499 }
500
Adam Cohen35ae9ca2010-09-20 15:20:41 -0700501 Intent fillInIntent;
502
503 public final static int TAG = 9;
504 }
505
Adam Cohenca6fd842010-09-03 18:10:35 -0700506 private class SetPendingIntentTemplate extends Action {
507 public SetPendingIntentTemplate(int id, PendingIntent pendingIntentTemplate) {
508 this.viewId = id;
509 this.pendingIntentTemplate = pendingIntentTemplate;
510 }
511
512 public SetPendingIntentTemplate(Parcel parcel) {
513 viewId = parcel.readInt();
514 pendingIntentTemplate = PendingIntent.readPendingIntentOrNullFromParcel(parcel);
515 }
516
517 public void writeToParcel(Parcel dest, int flags) {
518 dest.writeInt(TAG);
519 dest.writeInt(viewId);
520 pendingIntentTemplate.writeToParcel(dest, 0 /* no flags */);
521 }
522
523 @Override
Dianne Hackborna1940212012-06-28 16:07:22 -0700524 public void apply(View root, ViewGroup rootParent, final OnClickHandler handler) {
Adam Cohenca6fd842010-09-03 18:10:35 -0700525 final View target = root.findViewById(viewId);
Joe Onorato2b69ce42010-10-31 11:35:41 -0700526 if (target == null) return;
Adam Cohenca6fd842010-09-03 18:10:35 -0700527
528 // If the view isn't an AdapterView, setting a PendingIntent template doesn't make sense
529 if (target instanceof AdapterView<?>) {
Adam Cohena32edd42010-10-26 10:35:01 -0700530 AdapterView<?> av = (AdapterView<?>) target;
Adam Cohenca6fd842010-09-03 18:10:35 -0700531 // The PendingIntent template is stored in the view's tag.
Adam Cohena32edd42010-10-26 10:35:01 -0700532 OnItemClickListener listener = new OnItemClickListener() {
533 public void onItemClick(AdapterView<?> parent, View view,
534 int position, long id) {
535 // The view should be a frame layout
536 if (view instanceof ViewGroup) {
537 ViewGroup vg = (ViewGroup) view;
538
539 // AdapterViews contain their children in a frame
540 // so we need to go one layer deeper here.
541 if (parent instanceof AdapterViewAnimator) {
542 vg = (ViewGroup) vg.getChildAt(0);
543 }
544 if (vg == null) return;
545
546 Intent fillInIntent = null;
547 int childCount = vg.getChildCount();
548 for (int i = 0; i < childCount; i++) {
549 Object tag = vg.getChildAt(i).getTag(com.android.internal.R.id.fillInIntent);
550 if (tag instanceof Intent) {
551 fillInIntent = (Intent) tag;
552 break;
553 }
554 }
555 if (fillInIntent == null) return;
556
Romain Guye4d4e202013-07-22 13:02:02 -0700557 final Rect rect = getSourceBounds(view);
Adam Cohena32edd42010-10-26 10:35:01 -0700558
559 final Intent intent = new Intent();
560 intent.setSourceBounds(rect);
Dianne Hackborna1940212012-06-28 16:07:22 -0700561 handler.onClickHandler(view, pendingIntentTemplate, fillInIntent);
Adam Cohena32edd42010-10-26 10:35:01 -0700562 }
563 }
564 };
565 av.setOnItemClickListener(listener);
566 av.setTag(pendingIntentTemplate);
Adam Cohenca6fd842010-09-03 18:10:35 -0700567 } else {
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800568 Log.e(LOG_TAG, "Cannot setPendingIntentTemplate on a view which is not" +
Adam Cohen35ae9ca2010-09-20 15:20:41 -0700569 "an AdapterView (id: " + viewId + ")");
Adam Cohenca6fd842010-09-03 18:10:35 -0700570 return;
571 }
572 }
573
Adam Cohenfbe44b72012-09-19 20:36:23 -0700574 public String getActionName() {
575 return "SetPendingIntentTemplate";
576 }
577
Adam Cohenca6fd842010-09-03 18:10:35 -0700578 PendingIntent pendingIntentTemplate;
579
580 public final static int TAG = 8;
581 }
582
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800583 private class SetRemoteViewsAdapterList extends Action {
Adam Cohenb00d9f02013-01-10 14:12:52 -0800584 public SetRemoteViewsAdapterList(int id, ArrayList<RemoteViews> list, int viewTypeCount) {
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800585 this.viewId = id;
586 this.list = list;
Adam Cohenb00d9f02013-01-10 14:12:52 -0800587 this.viewTypeCount = viewTypeCount;
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800588 }
589
590 public SetRemoteViewsAdapterList(Parcel parcel) {
591 viewId = parcel.readInt();
Adam Cohenb00d9f02013-01-10 14:12:52 -0800592 viewTypeCount = parcel.readInt();
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800593 int count = parcel.readInt();
594 list = new ArrayList<RemoteViews>();
595
596 for (int i = 0; i < count; i++) {
597 RemoteViews rv = RemoteViews.CREATOR.createFromParcel(parcel);
598 list.add(rv);
599 }
600 }
601
602 public void writeToParcel(Parcel dest, int flags) {
603 dest.writeInt(TAG);
604 dest.writeInt(viewId);
Adam Cohenb00d9f02013-01-10 14:12:52 -0800605 dest.writeInt(viewTypeCount);
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800606
607 if (list == null || list.size() == 0) {
608 dest.writeInt(0);
609 } else {
610 int count = list.size();
611 dest.writeInt(count);
612 for (int i = 0; i < count; i++) {
613 RemoteViews rv = list.get(i);
614 rv.writeToParcel(dest, flags);
615 }
616 }
617 }
618
619 @Override
620 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
621 final View target = root.findViewById(viewId);
622 if (target == null) return;
623
624 // Ensure that we are applying to an AppWidget root
625 if (!(rootParent instanceof AppWidgetHostView)) {
626 Log.e(LOG_TAG, "SetRemoteViewsAdapterIntent action can only be used for " +
627 "AppWidgets (root id: " + viewId + ")");
628 return;
629 }
630 // Ensure that we are calling setRemoteAdapter on an AdapterView that supports it
631 if (!(target instanceof AbsListView) && !(target instanceof AdapterViewAnimator)) {
632 Log.e(LOG_TAG, "Cannot setRemoteViewsAdapter on a view which is not " +
633 "an AbsListView or AdapterViewAnimator (id: " + viewId + ")");
634 return;
635 }
636
637 if (target instanceof AbsListView) {
638 AbsListView v = (AbsListView) target;
639 Adapter a = v.getAdapter();
Adam Cohenb00d9f02013-01-10 14:12:52 -0800640 if (a instanceof RemoteViewsListAdapter && viewTypeCount <= a.getViewTypeCount()) {
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800641 ((RemoteViewsListAdapter) a).setViewsList(list);
642 } else {
Adam Cohenb00d9f02013-01-10 14:12:52 -0800643 v.setAdapter(new RemoteViewsListAdapter(v.getContext(), list, viewTypeCount));
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800644 }
645 } else if (target instanceof AdapterViewAnimator) {
646 AdapterViewAnimator v = (AdapterViewAnimator) target;
647 Adapter a = v.getAdapter();
Adam Cohenb00d9f02013-01-10 14:12:52 -0800648 if (a instanceof RemoteViewsListAdapter && viewTypeCount <= a.getViewTypeCount()) {
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800649 ((RemoteViewsListAdapter) a).setViewsList(list);
650 } else {
Adam Cohenb00d9f02013-01-10 14:12:52 -0800651 v.setAdapter(new RemoteViewsListAdapter(v.getContext(), list, viewTypeCount));
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800652 }
653 }
654 }
655
656 public String getActionName() {
657 return "SetRemoteViewsAdapterList";
658 }
659
Adam Cohenb00d9f02013-01-10 14:12:52 -0800660 int viewTypeCount;
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800661 ArrayList<RemoteViews> list;
662 public final static int TAG = 15;
663 }
664
Winson Chung037300b2011-03-29 15:40:16 -0700665 private class SetRemoteViewsAdapterIntent extends Action {
666 public SetRemoteViewsAdapterIntent(int id, Intent intent) {
667 this.viewId = id;
668 this.intent = intent;
669 }
670
671 public SetRemoteViewsAdapterIntent(Parcel parcel) {
672 viewId = parcel.readInt();
673 intent = Intent.CREATOR.createFromParcel(parcel);
674 }
675
676 public void writeToParcel(Parcel dest, int flags) {
677 dest.writeInt(TAG);
678 dest.writeInt(viewId);
679 intent.writeToParcel(dest, flags);
680 }
681
682 @Override
Dianne Hackborna1940212012-06-28 16:07:22 -0700683 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
Winson Chung037300b2011-03-29 15:40:16 -0700684 final View target = root.findViewById(viewId);
685 if (target == null) return;
686
687 // Ensure that we are applying to an AppWidget root
688 if (!(rootParent instanceof AppWidgetHostView)) {
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800689 Log.e(LOG_TAG, "SetRemoteViewsAdapterIntent action can only be used for " +
Winson Chung037300b2011-03-29 15:40:16 -0700690 "AppWidgets (root id: " + viewId + ")");
691 return;
692 }
693 // Ensure that we are calling setRemoteAdapter on an AdapterView that supports it
694 if (!(target instanceof AbsListView) && !(target instanceof AdapterViewAnimator)) {
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800695 Log.e(LOG_TAG, "Cannot setRemoteViewsAdapter on a view which is not " +
Winson Chung037300b2011-03-29 15:40:16 -0700696 "an AbsListView or AdapterViewAnimator (id: " + viewId + ")");
697 return;
698 }
699
700 // Embed the AppWidget Id for use in RemoteViewsAdapter when connecting to the intent
701 // RemoteViewsService
702 AppWidgetHostView host = (AppWidgetHostView) rootParent;
703 intent.putExtra(EXTRA_REMOTEADAPTER_APPWIDGET_ID, host.getAppWidgetId());
704 if (target instanceof AbsListView) {
705 AbsListView v = (AbsListView) target;
706 v.setRemoteViewsAdapter(intent);
Adam Cohena6a4cbc2012-09-26 17:36:40 -0700707 v.setRemoteViewsOnClickHandler(handler);
Winson Chung037300b2011-03-29 15:40:16 -0700708 } else if (target instanceof AdapterViewAnimator) {
709 AdapterViewAnimator v = (AdapterViewAnimator) target;
710 v.setRemoteViewsAdapter(intent);
Adam Cohena6a4cbc2012-09-26 17:36:40 -0700711 v.setRemoteViewsOnClickHandler(handler);
Winson Chung037300b2011-03-29 15:40:16 -0700712 }
713 }
714
Adam Cohenfbe44b72012-09-19 20:36:23 -0700715 public String getActionName() {
716 return "SetRemoteViewsAdapterIntent";
717 }
718
Winson Chung037300b2011-03-29 15:40:16 -0700719 Intent intent;
720
721 public final static int TAG = 10;
722 }
723
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800724 /**
725 * Equivalent to calling
726 * {@link android.view.View#setOnClickListener(android.view.View.OnClickListener)}
727 * to launch the provided {@link PendingIntent}.
728 */
729 private class SetOnClickPendingIntent extends Action {
730 public SetOnClickPendingIntent(int id, PendingIntent pendingIntent) {
731 this.viewId = id;
732 this.pendingIntent = pendingIntent;
733 }
Adam Cohenca6fd842010-09-03 18:10:35 -0700734
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800735 public SetOnClickPendingIntent(Parcel parcel) {
736 viewId = parcel.readInt();
Adam Cohenc6151f22012-02-02 21:02:31 -0800737
738 // We check a flag to determine if the parcel contains a PendingIntent.
739 if (parcel.readInt() != 0) {
740 pendingIntent = PendingIntent.readPendingIntentOrNullFromParcel(parcel);
741 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800742 }
Adam Cohenca6fd842010-09-03 18:10:35 -0700743
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800744 public void writeToParcel(Parcel dest, int flags) {
745 dest.writeInt(TAG);
746 dest.writeInt(viewId);
Adam Cohenc6151f22012-02-02 21:02:31 -0800747
748 // We use a flag to indicate whether the parcel contains a valid object.
749 dest.writeInt(pendingIntent != null ? 1 : 0);
750 if (pendingIntent != null) {
751 pendingIntent.writeToParcel(dest, 0 /* no flags */);
752 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800753 }
Adam Cohenca6fd842010-09-03 18:10:35 -0700754
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800755 @Override
Dianne Hackborna1940212012-06-28 16:07:22 -0700756 public void apply(View root, ViewGroup rootParent, final OnClickHandler handler) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800757 final View target = root.findViewById(viewId);
Joe Onorato2b69ce42010-10-31 11:35:41 -0700758 if (target == null) return;
Adam Cohenca6fd842010-09-03 18:10:35 -0700759
760 // If the view is an AdapterView, setting a PendingIntent on click doesn't make much
761 // sense, do they mean to set a PendingIntent template for the AdapterView's children?
762 if (mIsWidgetCollectionChild) {
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800763 Log.w(LOG_TAG, "Cannot setOnClickPendingIntent for collection item " +
Adam Cohenc6151f22012-02-02 21:02:31 -0800764 "(id: " + viewId + ")");
Adam Cohenffc46a52012-04-30 19:54:39 -0700765 ApplicationInfo appInfo = root.getContext().getApplicationInfo();
766
767 // We let this slide for HC and ICS so as to not break compatibility. It should have
768 // been disabled from the outset, but was left open by accident.
769 if (appInfo != null &&
770 appInfo.targetSdkVersion >= Build.VERSION_CODES.JELLY_BEAN) {
771 return;
772 }
Adam Cohenca6fd842010-09-03 18:10:35 -0700773 }
774
Romain Guye4d4e202013-07-22 13:02:02 -0700775 // If the pendingIntent is null, we clear the onClickListener
776 OnClickListener listener = null;
777 if (pendingIntent != null) {
778 listener = new OnClickListener() {
779 public void onClick(View v) {
780 // Find target view location in screen coordinates and
781 // fill into PendingIntent before sending.
782 final Rect rect = getSourceBounds(v);
Jim Millere667a7a2012-08-09 19:22:32 -0700783
Romain Guye4d4e202013-07-22 13:02:02 -0700784 final Intent intent = new Intent();
785 intent.setSourceBounds(rect);
786 handler.onClickHandler(v, pendingIntent, intent);
787 }
788 };
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800789 }
Romain Guye4d4e202013-07-22 13:02:02 -0700790 target.setOnClickListener(listener);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800791 }
Adam Cohenc6151f22012-02-02 21:02:31 -0800792
Adam Cohenfbe44b72012-09-19 20:36:23 -0700793 public String getActionName() {
794 return "SetOnClickPendingIntent";
795 }
796
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800797 PendingIntent pendingIntent;
798
799 public final static int TAG = 1;
800 }
801
Romain Guye4d4e202013-07-22 13:02:02 -0700802 private static Rect getSourceBounds(View v) {
803 final float appScale = v.getContext().getResources()
804 .getCompatibilityInfo().applicationScale;
805 final int[] pos = new int[2];
806 v.getLocationOnScreen(pos);
807
808 final Rect rect = new Rect();
809 rect.left = (int) (pos[0] * appScale + 0.5f);
810 rect.top = (int) (pos[1] * appScale + 0.5f);
811 rect.right = (int) ((pos[0] + v.getWidth()) * appScale + 0.5f);
812 rect.bottom = (int) ((pos[1] + v.getHeight()) * appScale + 0.5f);
813 return rect;
814 }
815
Romain Guy484f4d62013-07-22 16:39:16 -0700816 private Method getMethod(View view, String methodName, Class<?> paramType) {
Romain Guye4d4e202013-07-22 13:02:02 -0700817 Method method;
818 Class<? extends View> klass = view.getClass();
819
820 synchronized (sMethodsLock) {
Romain Guy484f4d62013-07-22 16:39:16 -0700821 ArrayMap<MutablePair<String, Class<?>>, Method> methods = sMethods.get(klass);
Romain Guye4d4e202013-07-22 13:02:02 -0700822 if (methods == null) {
Romain Guy484f4d62013-07-22 16:39:16 -0700823 methods = new ArrayMap<MutablePair<String, Class<?>>, Method>();
Romain Guye4d4e202013-07-22 13:02:02 -0700824 sMethods.put(klass, methods);
825 }
826
Romain Guy484f4d62013-07-22 16:39:16 -0700827 mPair.first = methodName;
828 mPair.second = paramType;
829
830 method = methods.get(mPair);
Romain Guye4d4e202013-07-22 13:02:02 -0700831 if (method == null) {
832 try {
Romain Guy9870e5c2013-07-23 13:09:51 -0700833 if (paramType == null) {
834 method = klass.getMethod(methodName);
835 } else {
836 method = klass.getMethod(methodName, paramType);
837 }
Romain Guye4d4e202013-07-22 13:02:02 -0700838 } catch (NoSuchMethodException ex) {
839 throw new ActionException("view: " + klass.getName() + " doesn't have method: "
840 + methodName + getParameters(paramType));
841 }
842
843 if (!method.isAnnotationPresent(RemotableViewMethod.class)) {
844 throw new ActionException("view: " + klass.getName()
845 + " can't use method with RemoteViews: "
846 + methodName + getParameters(paramType));
847 }
848
Romain Guy484f4d62013-07-22 16:39:16 -0700849 methods.put(new MutablePair<String, Class<?>>(methodName, paramType), method);
Romain Guye4d4e202013-07-22 13:02:02 -0700850 }
851 }
852
853 return method;
854 }
855
Sunny Goyaldd292f42015-12-02 14:29:27 -0800856 /**
857 * @return the async implementation of the provided method.
858 */
859 private Method getAsyncMethod(Method method) {
860 synchronized (sAsyncMethods) {
861 int valueIndex = sAsyncMethods.indexOfKey(method);
862 if (valueIndex >= 0) {
863 return sAsyncMethods.valueAt(valueIndex);
864 }
865
866 RemotableViewMethod annotation = method.getAnnotation(RemotableViewMethod.class);
867 Method asyncMethod = null;
868 if (!annotation.asyncImpl().isEmpty()) {
869 try {
870 asyncMethod = method.getDeclaringClass()
871 .getMethod(annotation.asyncImpl(), method.getParameterTypes());
872 if (!asyncMethod.getReturnType().equals(Runnable.class)) {
873 throw new ActionException("Async implementation for " + method.getName() +
874 " does not return a Runnable");
875 }
876 } catch (NoSuchMethodException ex) {
877 throw new ActionException("Async implementation declared but not defined for " +
878 method.getName());
879 }
880 }
881 sAsyncMethods.put(method, asyncMethod);
882 return asyncMethod;
883 }
884 }
885
Romain Guye4d4e202013-07-22 13:02:02 -0700886 private static String getParameters(Class<?> paramType) {
887 if (paramType == null) return "()";
888 return "(" + paramType + ")";
889 }
890
891 private static Object[] wrapArg(Object value) {
892 Object[] args = sInvokeArgsTls.get();
893 args[0] = value;
894 return args;
895 }
896
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800897 /**
898 * Equivalent to calling a combination of {@link Drawable#setAlpha(int)},
899 * {@link Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)},
900 * and/or {@link Drawable#setLevel(int)} on the {@link Drawable} of a given view.
901 * <p>
902 * These operations will be performed on the {@link Drawable} returned by the
903 * target {@link View#getBackground()} by default. If targetBackground is false,
904 * we assume the target is an {@link ImageView} and try applying the operations
905 * to {@link ImageView#getDrawable()}.
906 * <p>
907 * You can omit specific calls by marking their values with null or -1.
908 */
909 private class SetDrawableParameters extends Action {
910 public SetDrawableParameters(int id, boolean targetBackground, int alpha,
911 int colorFilter, PorterDuff.Mode mode, int level) {
912 this.viewId = id;
913 this.targetBackground = targetBackground;
914 this.alpha = alpha;
915 this.colorFilter = colorFilter;
916 this.filterMode = mode;
917 this.level = level;
918 }
Jim Millere667a7a2012-08-09 19:22:32 -0700919
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800920 public SetDrawableParameters(Parcel parcel) {
921 viewId = parcel.readInt();
922 targetBackground = parcel.readInt() != 0;
923 alpha = parcel.readInt();
924 colorFilter = parcel.readInt();
925 boolean hasMode = parcel.readInt() != 0;
926 if (hasMode) {
927 filterMode = PorterDuff.Mode.valueOf(parcel.readString());
928 } else {
929 filterMode = null;
930 }
931 level = parcel.readInt();
932 }
Jim Millere667a7a2012-08-09 19:22:32 -0700933
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800934 public void writeToParcel(Parcel dest, int flags) {
935 dest.writeInt(TAG);
936 dest.writeInt(viewId);
937 dest.writeInt(targetBackground ? 1 : 0);
938 dest.writeInt(alpha);
939 dest.writeInt(colorFilter);
940 if (filterMode != null) {
941 dest.writeInt(1);
942 dest.writeString(filterMode.toString());
943 } else {
944 dest.writeInt(0);
945 }
946 dest.writeInt(level);
947 }
Jim Millere667a7a2012-08-09 19:22:32 -0700948
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800949 @Override
Dianne Hackborna1940212012-06-28 16:07:22 -0700950 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800951 final View target = root.findViewById(viewId);
Joe Onorato2b69ce42010-10-31 11:35:41 -0700952 if (target == null) return;
Jim Millere667a7a2012-08-09 19:22:32 -0700953
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800954 // Pick the correct drawable to modify for this view
955 Drawable targetDrawable = null;
956 if (targetBackground) {
957 targetDrawable = target.getBackground();
958 } else if (target instanceof ImageView) {
959 ImageView imageView = (ImageView) target;
960 targetDrawable = imageView.getDrawable();
961 }
Jim Millere667a7a2012-08-09 19:22:32 -0700962
Romain Guya5475592009-07-01 17:20:08 -0700963 if (targetDrawable != null) {
964 // Perform modifications only if values are set correctly
965 if (alpha != -1) {
Jorim Jaggi03a7c4c2015-06-01 16:25:39 -0700966 targetDrawable.mutate().setAlpha(alpha);
Romain Guya5475592009-07-01 17:20:08 -0700967 }
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +0200968 if (filterMode != null) {
Jorim Jaggi03a7c4c2015-06-01 16:25:39 -0700969 targetDrawable.mutate().setColorFilter(colorFilter, filterMode);
Romain Guya5475592009-07-01 17:20:08 -0700970 }
971 if (level != -1) {
Jorim Jaggi03a7c4c2015-06-01 16:25:39 -0700972 targetDrawable.mutate().setLevel(level);
Romain Guya5475592009-07-01 17:20:08 -0700973 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800974 }
975 }
Winson Chung3ec9a452010-09-23 16:40:28 -0700976
Adam Cohenfbe44b72012-09-19 20:36:23 -0700977 public String getActionName() {
978 return "SetDrawableParameters";
979 }
980
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800981 boolean targetBackground;
982 int alpha;
983 int colorFilter;
984 PorterDuff.Mode filterMode;
985 int level;
986
987 public final static int TAG = 3;
988 }
Jim Millere667a7a2012-08-09 19:22:32 -0700989
Romain Guy484f4d62013-07-22 16:39:16 -0700990 private final class ReflectionActionWithoutParams extends Action {
991 final String methodName;
Adam Cohen2dd21972010-08-15 18:20:04 -0700992
993 public final static int TAG = 5;
994
995 ReflectionActionWithoutParams(int viewId, String methodName) {
996 this.viewId = viewId;
997 this.methodName = methodName;
998 }
999
1000 ReflectionActionWithoutParams(Parcel in) {
1001 this.viewId = in.readInt();
1002 this.methodName = in.readString();
1003 }
1004
1005 public void writeToParcel(Parcel out, int flags) {
1006 out.writeInt(TAG);
1007 out.writeInt(this.viewId);
1008 out.writeString(this.methodName);
1009 }
1010
1011 @Override
Dianne Hackborna1940212012-06-28 16:07:22 -07001012 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
Adam Cohen2dd21972010-08-15 18:20:04 -07001013 final View view = root.findViewById(viewId);
Joe Onorato2b69ce42010-10-31 11:35:41 -07001014 if (view == null) return;
Adam Cohen2dd21972010-08-15 18:20:04 -07001015
Adam Cohen2dd21972010-08-15 18:20:04 -07001016 try {
Romain Guye4d4e202013-07-22 13:02:02 -07001017 getMethod(view, this.methodName, null).invoke(view);
1018 } catch (ActionException e) {
1019 throw e;
Adam Cohen2dd21972010-08-15 18:20:04 -07001020 } catch (Exception ex) {
1021 throw new ActionException(ex);
1022 }
1023 }
Adam Cohenfbe44b72012-09-19 20:36:23 -07001024
1025 public int mergeBehavior() {
1026 // we don't need to build up showNext or showPrevious calls
1027 if (methodName.equals("showNext") || methodName.equals("showPrevious")) {
1028 return MERGE_IGNORE;
1029 } else {
1030 return MERGE_REPLACE;
1031 }
1032 }
1033
1034 public String getActionName() {
1035 return "ReflectionActionWithoutParams";
1036 }
Adam Cohen2dd21972010-08-15 18:20:04 -07001037 }
1038
Adam Cohen5d200642012-04-24 10:43:31 -07001039 private static class BitmapCache {
1040 ArrayList<Bitmap> mBitmaps;
1041
1042 public BitmapCache() {
1043 mBitmaps = new ArrayList<Bitmap>();
1044 }
1045
1046 public BitmapCache(Parcel source) {
1047 int count = source.readInt();
1048 mBitmaps = new ArrayList<Bitmap>();
1049 for (int i = 0; i < count; i++) {
1050 Bitmap b = Bitmap.CREATOR.createFromParcel(source);
1051 mBitmaps.add(b);
1052 }
1053 }
1054
1055 public int getBitmapId(Bitmap b) {
1056 if (b == null) {
1057 return -1;
1058 } else {
1059 if (mBitmaps.contains(b)) {
1060 return mBitmaps.indexOf(b);
1061 } else {
1062 mBitmaps.add(b);
1063 return (mBitmaps.size() - 1);
1064 }
1065 }
1066 }
1067
1068 public Bitmap getBitmapForId(int id) {
1069 if (id == -1 || id >= mBitmaps.size()) {
1070 return null;
1071 } else {
1072 return mBitmaps.get(id);
1073 }
1074 }
1075
1076 public void writeBitmapsToParcel(Parcel dest, int flags) {
1077 int count = mBitmaps.size();
1078 dest.writeInt(count);
1079 for (int i = 0; i < count; i++) {
1080 mBitmaps.get(i).writeToParcel(dest, flags);
1081 }
1082 }
1083
1084 public void assimilate(BitmapCache bitmapCache) {
1085 ArrayList<Bitmap> bitmapsToBeAdded = bitmapCache.mBitmaps;
1086 int count = bitmapsToBeAdded.size();
1087 for (int i = 0; i < count; i++) {
1088 Bitmap b = bitmapsToBeAdded.get(i);
1089 if (!mBitmaps.contains(b)) {
1090 mBitmaps.add(b);
1091 }
1092 }
1093 }
1094
1095 public void addBitmapMemory(MemoryUsageCounter memoryCounter) {
1096 for (int i = 0; i < mBitmaps.size(); i++) {
1097 memoryCounter.addBitmapMemory(mBitmaps.get(i));
1098 }
1099 }
Adrian Roos7da889d2016-03-16 18:38:58 -07001100
1101 @Override
1102 protected BitmapCache clone() {
1103 BitmapCache bitmapCache = new BitmapCache();
1104 bitmapCache.mBitmaps.addAll(mBitmaps);
1105 return bitmapCache;
1106 }
Adam Cohen5d200642012-04-24 10:43:31 -07001107 }
1108
1109 private class BitmapReflectionAction extends Action {
1110 int bitmapId;
Adam Cohen5d200642012-04-24 10:43:31 -07001111 Bitmap bitmap;
1112 String methodName;
1113
1114 BitmapReflectionAction(int viewId, String methodName, Bitmap bitmap) {
1115 this.bitmap = bitmap;
1116 this.viewId = viewId;
1117 this.methodName = methodName;
1118 bitmapId = mBitmapCache.getBitmapId(bitmap);
1119 }
1120
1121 BitmapReflectionAction(Parcel in) {
1122 viewId = in.readInt();
1123 methodName = in.readString();
1124 bitmapId = in.readInt();
1125 bitmap = mBitmapCache.getBitmapForId(bitmapId);
1126 }
1127
1128 @Override
1129 public void writeToParcel(Parcel dest, int flags) {
1130 dest.writeInt(TAG);
1131 dest.writeInt(viewId);
1132 dest.writeString(methodName);
1133 dest.writeInt(bitmapId);
1134 }
1135
1136 @Override
Dianne Hackborna1940212012-06-28 16:07:22 -07001137 public void apply(View root, ViewGroup rootParent,
1138 OnClickHandler handler) throws ActionException {
Adam Cohen5d200642012-04-24 10:43:31 -07001139 ReflectionAction ra = new ReflectionAction(viewId, methodName, ReflectionAction.BITMAP,
1140 bitmap);
Dianne Hackborna1940212012-06-28 16:07:22 -07001141 ra.apply(root, rootParent, handler);
Adam Cohen5d200642012-04-24 10:43:31 -07001142 }
1143
1144 @Override
1145 public void setBitmapCache(BitmapCache bitmapCache) {
1146 bitmapId = bitmapCache.getBitmapId(bitmap);
1147 }
1148
Adam Cohenfbe44b72012-09-19 20:36:23 -07001149 public String getActionName() {
1150 return "BitmapReflectionAction";
1151 }
1152
Adam Cohen5d200642012-04-24 10:43:31 -07001153 public final static int TAG = 12;
1154 }
1155
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001156 /**
1157 * Base class for the reflection actions.
1158 */
Romain Guy484f4d62013-07-22 16:39:16 -07001159 private final class ReflectionAction extends Action {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001160 static final int TAG = 2;
1161
1162 static final int BOOLEAN = 1;
1163 static final int BYTE = 2;
1164 static final int SHORT = 3;
1165 static final int INT = 4;
1166 static final int LONG = 5;
1167 static final int FLOAT = 6;
1168 static final int DOUBLE = 7;
1169 static final int CHAR = 8;
1170 static final int STRING = 9;
1171 static final int CHAR_SEQUENCE = 10;
1172 static final int URI = 11;
Adam Cohenfbe44b72012-09-19 20:36:23 -07001173 // BITMAP actions are never stored in the list of actions. They are only used locally
1174 // to implement BitmapReflectionAction, which eliminates duplicates using BitmapCache.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001175 static final int BITMAP = 12;
Bjorn Bringertd755b062010-01-06 17:15:37 +00001176 static final int BUNDLE = 13;
Winson Chung499cb9f2010-07-16 11:18:17 -07001177 static final int INTENT = 14;
Jorim Jaggief72a192014-08-26 21:57:46 +02001178 static final int COLOR_STATE_LIST = 15;
Dan Sandlera22a3802015-05-13 00:12:47 -04001179 static final int ICON = 16;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001180
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001181 String methodName;
1182 int type;
1183 Object value;
1184
1185 ReflectionAction(int viewId, String methodName, int type, Object value) {
1186 this.viewId = viewId;
1187 this.methodName = methodName;
1188 this.type = type;
1189 this.value = value;
1190 }
1191
1192 ReflectionAction(Parcel in) {
1193 this.viewId = in.readInt();
1194 this.methodName = in.readString();
1195 this.type = in.readInt();
Romain Guya5475592009-07-01 17:20:08 -07001196 //noinspection ConstantIfStatement
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001197 if (false) {
Adam Cohen50f3d1b2012-12-11 18:36:07 -08001198 Log.d(LOG_TAG, "read viewId=0x" + Integer.toHexString(this.viewId)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001199 + " methodName=" + this.methodName + " type=" + this.type);
1200 }
Adam Cohenc6151f22012-02-02 21:02:31 -08001201
1202 // For some values that may have been null, we first check a flag to see if they were
1203 // written to the parcel.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001204 switch (this.type) {
1205 case BOOLEAN:
1206 this.value = in.readInt() != 0;
1207 break;
1208 case BYTE:
1209 this.value = in.readByte();
1210 break;
1211 case SHORT:
1212 this.value = (short)in.readInt();
1213 break;
1214 case INT:
1215 this.value = in.readInt();
1216 break;
1217 case LONG:
1218 this.value = in.readLong();
1219 break;
1220 case FLOAT:
1221 this.value = in.readFloat();
1222 break;
1223 case DOUBLE:
1224 this.value = in.readDouble();
1225 break;
1226 case CHAR:
1227 this.value = (char)in.readInt();
1228 break;
1229 case STRING:
1230 this.value = in.readString();
1231 break;
1232 case CHAR_SEQUENCE:
1233 this.value = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
1234 break;
1235 case URI:
Adam Cohenc6151f22012-02-02 21:02:31 -08001236 if (in.readInt() != 0) {
1237 this.value = Uri.CREATOR.createFromParcel(in);
1238 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001239 break;
1240 case BITMAP:
Adam Cohenc6151f22012-02-02 21:02:31 -08001241 if (in.readInt() != 0) {
1242 this.value = Bitmap.CREATOR.createFromParcel(in);
1243 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001244 break;
Bjorn Bringertd755b062010-01-06 17:15:37 +00001245 case BUNDLE:
1246 this.value = in.readBundle();
1247 break;
Winson Chung499cb9f2010-07-16 11:18:17 -07001248 case INTENT:
Adam Cohenc6151f22012-02-02 21:02:31 -08001249 if (in.readInt() != 0) {
1250 this.value = Intent.CREATOR.createFromParcel(in);
1251 }
Winson Chung499cb9f2010-07-16 11:18:17 -07001252 break;
Jorim Jaggief72a192014-08-26 21:57:46 +02001253 case COLOR_STATE_LIST:
1254 if (in.readInt() != 0) {
1255 this.value = ColorStateList.CREATOR.createFromParcel(in);
1256 }
1257 break;
Dan Sandlera22a3802015-05-13 00:12:47 -04001258 case ICON:
1259 if (in.readInt() != 0) {
1260 this.value = Icon.CREATOR.createFromParcel(in);
1261 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001262 default:
1263 break;
1264 }
1265 }
1266
1267 public void writeToParcel(Parcel out, int flags) {
1268 out.writeInt(TAG);
1269 out.writeInt(this.viewId);
1270 out.writeString(this.methodName);
1271 out.writeInt(this.type);
Romain Guya5475592009-07-01 17:20:08 -07001272 //noinspection ConstantIfStatement
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001273 if (false) {
Adam Cohen50f3d1b2012-12-11 18:36:07 -08001274 Log.d(LOG_TAG, "write viewId=0x" + Integer.toHexString(this.viewId)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001275 + " methodName=" + this.methodName + " type=" + this.type);
1276 }
Adam Cohenc6151f22012-02-02 21:02:31 -08001277
1278 // For some values which are null, we record an integer flag to indicate whether
1279 // we have written a valid value to the parcel.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001280 switch (this.type) {
1281 case BOOLEAN:
Romain Guya5475592009-07-01 17:20:08 -07001282 out.writeInt((Boolean) this.value ? 1 : 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001283 break;
1284 case BYTE:
Romain Guya5475592009-07-01 17:20:08 -07001285 out.writeByte((Byte) this.value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001286 break;
1287 case SHORT:
Romain Guya5475592009-07-01 17:20:08 -07001288 out.writeInt((Short) this.value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001289 break;
1290 case INT:
Romain Guya5475592009-07-01 17:20:08 -07001291 out.writeInt((Integer) this.value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001292 break;
1293 case LONG:
Romain Guya5475592009-07-01 17:20:08 -07001294 out.writeLong((Long) this.value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001295 break;
1296 case FLOAT:
Romain Guya5475592009-07-01 17:20:08 -07001297 out.writeFloat((Float) this.value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001298 break;
1299 case DOUBLE:
Romain Guya5475592009-07-01 17:20:08 -07001300 out.writeDouble((Double) this.value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001301 break;
1302 case CHAR:
1303 out.writeInt((int)((Character)this.value).charValue());
1304 break;
1305 case STRING:
1306 out.writeString((String)this.value);
1307 break;
1308 case CHAR_SEQUENCE:
Jim Millere667a7a2012-08-09 19:22:32 -07001309 TextUtils.writeToParcel((CharSequence)this.value, out, flags);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001310 break;
1311 case URI:
Adam Cohenc6151f22012-02-02 21:02:31 -08001312 out.writeInt(this.value != null ? 1 : 0);
1313 if (this.value != null) {
1314 ((Uri)this.value).writeToParcel(out, flags);
1315 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001316 break;
1317 case BITMAP:
Adam Cohenc6151f22012-02-02 21:02:31 -08001318 out.writeInt(this.value != null ? 1 : 0);
1319 if (this.value != null) {
1320 ((Bitmap)this.value).writeToParcel(out, flags);
1321 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001322 break;
Bjorn Bringertd755b062010-01-06 17:15:37 +00001323 case BUNDLE:
1324 out.writeBundle((Bundle) this.value);
1325 break;
Winson Chung499cb9f2010-07-16 11:18:17 -07001326 case INTENT:
Adam Cohenc6151f22012-02-02 21:02:31 -08001327 out.writeInt(this.value != null ? 1 : 0);
1328 if (this.value != null) {
1329 ((Intent)this.value).writeToParcel(out, flags);
1330 }
Winson Chung499cb9f2010-07-16 11:18:17 -07001331 break;
Jorim Jaggief72a192014-08-26 21:57:46 +02001332 case COLOR_STATE_LIST:
1333 out.writeInt(this.value != null ? 1 : 0);
1334 if (this.value != null) {
1335 ((ColorStateList)this.value).writeToParcel(out, flags);
1336 }
Dan Sandlera22a3802015-05-13 00:12:47 -04001337 break;
1338 case ICON:
1339 out.writeInt(this.value != null ? 1 : 0);
1340 if (this.value != null) {
1341 ((Icon)this.value).writeToParcel(out, flags);
1342 }
1343 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001344 default:
1345 break;
1346 }
1347 }
1348
Romain Guye4d4e202013-07-22 13:02:02 -07001349 private Class<?> getParameterType() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001350 switch (this.type) {
1351 case BOOLEAN:
1352 return boolean.class;
1353 case BYTE:
1354 return byte.class;
1355 case SHORT:
1356 return short.class;
1357 case INT:
1358 return int.class;
1359 case LONG:
1360 return long.class;
1361 case FLOAT:
1362 return float.class;
1363 case DOUBLE:
1364 return double.class;
1365 case CHAR:
1366 return char.class;
1367 case STRING:
1368 return String.class;
1369 case CHAR_SEQUENCE:
1370 return CharSequence.class;
1371 case URI:
1372 return Uri.class;
1373 case BITMAP:
1374 return Bitmap.class;
Bjorn Bringertd755b062010-01-06 17:15:37 +00001375 case BUNDLE:
1376 return Bundle.class;
Winson Chung499cb9f2010-07-16 11:18:17 -07001377 case INTENT:
1378 return Intent.class;
Jorim Jaggief72a192014-08-26 21:57:46 +02001379 case COLOR_STATE_LIST:
1380 return ColorStateList.class;
Dan Sandlera22a3802015-05-13 00:12:47 -04001381 case ICON:
1382 return Icon.class;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001383 default:
1384 return null;
1385 }
1386 }
1387
1388 @Override
Dianne Hackborna1940212012-06-28 16:07:22 -07001389 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001390 final View view = root.findViewById(viewId);
Joe Onorato2b69ce42010-10-31 11:35:41 -07001391 if (view == null) return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001392
Romain Guye4d4e202013-07-22 13:02:02 -07001393 Class<?> param = getParameterType();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001394 if (param == null) {
1395 throw new ActionException("bad type: " + this.type);
1396 }
1397
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001398 try {
Romain Guye4d4e202013-07-22 13:02:02 -07001399 getMethod(view, this.methodName, param).invoke(view, wrapArg(this.value));
1400 } catch (ActionException e) {
1401 throw e;
1402 } catch (Exception ex) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001403 throw new ActionException(ex);
1404 }
1405 }
Winson Chung3ec9a452010-09-23 16:40:28 -07001406
Sunny Goyaldd292f42015-12-02 14:29:27 -08001407 @Override
1408 public Action initActionAsync(ViewTree root, ViewGroup rootParent, OnClickHandler handler) {
1409 final View view = root.findViewById(viewId);
1410 if (view == null) return ACTION_NOOP;
1411
1412 Class<?> param = getParameterType();
1413 if (param == null) {
1414 throw new ActionException("bad type: " + this.type);
1415 }
1416
1417 try {
1418 Method method = getMethod(view, this.methodName, param);
1419 Method asyncMethod = getAsyncMethod(method);
1420
1421 if (asyncMethod != null) {
1422 Runnable endAction = (Runnable) asyncMethod.invoke(view, wrapArg(this.value));
1423 if (endAction == null) {
1424 return ACTION_NOOP;
1425 } else {
1426 return new RunnableAction(endAction);
1427 }
1428 }
1429 } catch (ActionException e) {
1430 throw e;
1431 } catch (Exception ex) {
1432 throw new ActionException(ex);
1433 }
1434
1435 return this;
1436 }
1437
Adam Cohenfbe44b72012-09-19 20:36:23 -07001438 public int mergeBehavior() {
1439 // smoothScrollBy is cumulative, everything else overwites.
1440 if (methodName.equals("smoothScrollBy")) {
1441 return MERGE_APPEND;
1442 } else {
1443 return MERGE_REPLACE;
Winson Chung3ec9a452010-09-23 16:40:28 -07001444 }
1445 }
Adam Cohenfbe44b72012-09-19 20:36:23 -07001446
1447 public String getActionName() {
1448 // Each type of reflection action corresponds to a setter, so each should be seen as
1449 // unique from the standpoint of merging.
1450 return "ReflectionAction" + this.methodName + this.type;
1451 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001452 }
1453
Sunny Goyaldd292f42015-12-02 14:29:27 -08001454 /**
1455 * This is only used for async execution of actions and it not parcelable.
1456 */
1457 private static final class RunnableAction extends RuntimeAction {
1458 private final Runnable mRunnable;
1459
1460 RunnableAction(Runnable r) {
1461 mRunnable = r;
1462 }
1463
1464 @Override
1465 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
1466 mRunnable.run();
1467 }
1468 }
1469
Adam Cohen5d200642012-04-24 10:43:31 -07001470 private void configureRemoteViewsAsChild(RemoteViews rv) {
1471 mBitmapCache.assimilate(rv.mBitmapCache);
1472 rv.setBitmapCache(mBitmapCache);
1473 rv.setNotRoot();
1474 }
1475
1476 void setNotRoot() {
1477 mIsRoot = false;
1478 }
1479
Jeff Sharkey1162fd72009-11-04 17:58:08 -08001480 /**
1481 * Equivalent to calling {@link ViewGroup#addView(View)} after inflating the
1482 * given {@link RemoteViews}, or calling {@link ViewGroup#removeAllViews()}
1483 * when null. This allows users to build "nested" {@link RemoteViews}.
1484 */
1485 private class ViewGroupAction extends Action {
1486 public ViewGroupAction(int viewId, RemoteViews nestedViews) {
1487 this.viewId = viewId;
1488 this.nestedViews = nestedViews;
Adam Cohenc431c152012-04-26 18:42:17 -07001489 if (nestedViews != null) {
1490 configureRemoteViewsAsChild(nestedViews);
1491 }
Jeff Sharkey1162fd72009-11-04 17:58:08 -08001492 }
1493
Adam Cohen5d200642012-04-24 10:43:31 -07001494 public ViewGroupAction(Parcel parcel, BitmapCache bitmapCache) {
Jeff Sharkey1162fd72009-11-04 17:58:08 -08001495 viewId = parcel.readInt();
Adam Cohen5d200642012-04-24 10:43:31 -07001496 boolean nestedViewsNull = parcel.readInt() == 0;
1497 if (!nestedViewsNull) {
1498 nestedViews = new RemoteViews(parcel, bitmapCache);
1499 } else {
1500 nestedViews = null;
1501 }
Jeff Sharkey1162fd72009-11-04 17:58:08 -08001502 }
1503
1504 public void writeToParcel(Parcel dest, int flags) {
1505 dest.writeInt(TAG);
1506 dest.writeInt(viewId);
Adam Cohen5d200642012-04-24 10:43:31 -07001507 if (nestedViews != null) {
1508 dest.writeInt(1);
1509 nestedViews.writeToParcel(dest, flags);
1510 } else {
1511 // signifies null
1512 dest.writeInt(0);
1513 }
Jeff Sharkey1162fd72009-11-04 17:58:08 -08001514 }
1515
1516 @Override
Dianne Hackborna1940212012-06-28 16:07:22 -07001517 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
Jeff Sharkey1162fd72009-11-04 17:58:08 -08001518 final Context context = root.getContext();
1519 final ViewGroup target = (ViewGroup) root.findViewById(viewId);
Joe Onorato2b69ce42010-10-31 11:35:41 -07001520 if (target == null) return;
Jeff Sharkey1162fd72009-11-04 17:58:08 -08001521 if (nestedViews != null) {
1522 // Inflate nested views and add as children
Dianne Hackborna1940212012-06-28 16:07:22 -07001523 target.addView(nestedViews.apply(context, target, handler));
Joe Onorato2b69ce42010-10-31 11:35:41 -07001524 } else {
Jeff Sharkey1162fd72009-11-04 17:58:08 -08001525 // Clear all children when nested views omitted
1526 target.removeAllViews();
1527 }
1528 }
1529
Winson Chung3ec9a452010-09-23 16:40:28 -07001530 @Override
Sunny Goyaldd292f42015-12-02 14:29:27 -08001531 public Action initActionAsync(ViewTree root, ViewGroup rootParent, OnClickHandler handler) {
1532 // In the async implementation, update the view tree so that subsequent calls to
1533 // findViewById return the currect view.
1534 root.createTree();
1535 ViewTree target = root.findViewTreeById(viewId);
1536 if ((target == null) || !(target.mRoot instanceof ViewGroup)) {
1537 return ACTION_NOOP;
1538 }
1539 if (nestedViews == null) {
1540 // Clear all children when nested views omitted
1541 target.mChildren = null;
1542 return this;
1543 } else {
1544 // Inflate nested views and perform all the async tasks for the child remoteView.
1545 final Context context = root.mRoot.getContext();
1546 final AsyncApplyTask task = nestedViews.getAsyncApplyTask(
1547 context, (ViewGroup) target.mRoot, null, handler);
1548 final ViewTree tree = task.doInBackground();
1549
1550 // Update the global view tree, so that next call to findViewTreeById
1551 // goes through the subtree as well.
1552 target.addChild(tree);
1553
1554 return new RuntimeAction() {
1555
1556 @Override
1557 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) throws ActionException {
1558 // This view will exist as we have already made sure
1559 final ViewGroup target = (ViewGroup) root.findViewById(viewId);
1560 task.onPostExecute(tree);
1561 target.addView(task.mResult);
1562 }
1563 };
1564 }
1565 }
1566
1567 @Override
Winson Chung3ec9a452010-09-23 16:40:28 -07001568 public void updateMemoryUsageEstimate(MemoryUsageCounter counter) {
1569 if (nestedViews != null) {
Adam Cohen5d200642012-04-24 10:43:31 -07001570 counter.increment(nestedViews.estimateMemoryUsage());
1571 }
1572 }
1573
1574 @Override
1575 public void setBitmapCache(BitmapCache bitmapCache) {
1576 if (nestedViews != null) {
1577 nestedViews.setBitmapCache(bitmapCache);
Winson Chung3ec9a452010-09-23 16:40:28 -07001578 }
1579 }
1580
Adam Cohenfbe44b72012-09-19 20:36:23 -07001581 public String getActionName() {
Romain Guye4d4e202013-07-22 13:02:02 -07001582 return "ViewGroupAction" + (nestedViews == null ? "Remove" : "Add");
Adam Cohenfbe44b72012-09-19 20:36:23 -07001583 }
1584
1585 public int mergeBehavior() {
1586 return MERGE_APPEND;
1587 }
1588
Jeff Sharkey1162fd72009-11-04 17:58:08 -08001589 RemoteViews nestedViews;
1590
1591 public final static int TAG = 4;
1592 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001593
1594 /**
Daniel Sandler820ba322012-03-23 16:36:00 -05001595 * Helper action to set compound drawables on a TextView. Supports relative
1596 * (s/t/e/b) or cardinal (l/t/r/b) arrangement.
1597 */
1598 private class TextViewDrawableAction extends Action {
1599 public TextViewDrawableAction(int viewId, boolean isRelative, int d1, int d2, int d3, int d4) {
1600 this.viewId = viewId;
1601 this.isRelative = isRelative;
Dan Sandler912282e2015-07-28 22:49:30 -04001602 this.useIcons = false;
Daniel Sandler820ba322012-03-23 16:36:00 -05001603 this.d1 = d1;
1604 this.d2 = d2;
1605 this.d3 = d3;
1606 this.d4 = d4;
1607 }
1608
Dan Sandler912282e2015-07-28 22:49:30 -04001609 public TextViewDrawableAction(int viewId, boolean isRelative,
1610 Icon i1, Icon i2, Icon i3, Icon i4) {
1611 this.viewId = viewId;
1612 this.isRelative = isRelative;
1613 this.useIcons = true;
1614 this.i1 = i1;
1615 this.i2 = i2;
1616 this.i3 = i3;
1617 this.i4 = i4;
1618 }
1619
Daniel Sandler820ba322012-03-23 16:36:00 -05001620 public TextViewDrawableAction(Parcel parcel) {
1621 viewId = parcel.readInt();
1622 isRelative = (parcel.readInt() != 0);
Dan Sandler912282e2015-07-28 22:49:30 -04001623 useIcons = (parcel.readInt() != 0);
1624 if (useIcons) {
1625 if (parcel.readInt() != 0) {
1626 i1 = Icon.CREATOR.createFromParcel(parcel);
1627 }
1628 if (parcel.readInt() != 0) {
1629 i2 = Icon.CREATOR.createFromParcel(parcel);
1630 }
1631 if (parcel.readInt() != 0) {
1632 i3 = Icon.CREATOR.createFromParcel(parcel);
1633 }
1634 if (parcel.readInt() != 0) {
1635 i4 = Icon.CREATOR.createFromParcel(parcel);
1636 }
1637 } else {
1638 d1 = parcel.readInt();
1639 d2 = parcel.readInt();
1640 d3 = parcel.readInt();
1641 d4 = parcel.readInt();
1642 }
Daniel Sandler820ba322012-03-23 16:36:00 -05001643 }
1644
1645 public void writeToParcel(Parcel dest, int flags) {
1646 dest.writeInt(TAG);
1647 dest.writeInt(viewId);
1648 dest.writeInt(isRelative ? 1 : 0);
Dan Sandler912282e2015-07-28 22:49:30 -04001649 dest.writeInt(useIcons ? 1 : 0);
1650 if (useIcons) {
1651 if (i1 != null) {
1652 dest.writeInt(1);
1653 i1.writeToParcel(dest, 0);
1654 } else {
1655 dest.writeInt(0);
1656 }
1657 if (i2 != null) {
1658 dest.writeInt(1);
1659 i2.writeToParcel(dest, 0);
1660 } else {
1661 dest.writeInt(0);
1662 }
1663 if (i3 != null) {
1664 dest.writeInt(1);
1665 i3.writeToParcel(dest, 0);
1666 } else {
1667 dest.writeInt(0);
1668 }
1669 if (i4 != null) {
1670 dest.writeInt(1);
1671 i4.writeToParcel(dest, 0);
1672 } else {
1673 dest.writeInt(0);
1674 }
1675 } else {
1676 dest.writeInt(d1);
1677 dest.writeInt(d2);
1678 dest.writeInt(d3);
1679 dest.writeInt(d4);
1680 }
Daniel Sandler820ba322012-03-23 16:36:00 -05001681 }
1682
1683 @Override
Dianne Hackborna1940212012-06-28 16:07:22 -07001684 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
Daniel Sandler820ba322012-03-23 16:36:00 -05001685 final TextView target = (TextView) root.findViewById(viewId);
1686 if (target == null) return;
Sunny Goyaldd292f42015-12-02 14:29:27 -08001687 if (drawablesLoaded) {
1688 if (isRelative) {
1689 target.setCompoundDrawablesRelativeWithIntrinsicBounds(id1, id2, id3, id4);
1690 } else {
1691 target.setCompoundDrawablesWithIntrinsicBounds(id1, id2, id3, id4);
1692 }
1693 } else if (useIcons) {
Dan Sandler912282e2015-07-28 22:49:30 -04001694 final Context ctx = target.getContext();
1695 final Drawable id1 = i1 == null ? null : i1.loadDrawable(ctx);
1696 final Drawable id2 = i2 == null ? null : i2.loadDrawable(ctx);
1697 final Drawable id3 = i3 == null ? null : i3.loadDrawable(ctx);
1698 final Drawable id4 = i4 == null ? null : i4.loadDrawable(ctx);
1699 if (isRelative) {
1700 target.setCompoundDrawablesRelativeWithIntrinsicBounds(id1, id2, id3, id4);
1701 } else {
1702 target.setCompoundDrawablesWithIntrinsicBounds(id1, id2, id3, id4);
1703 }
Daniel Sandler820ba322012-03-23 16:36:00 -05001704 } else {
Dan Sandler912282e2015-07-28 22:49:30 -04001705 if (isRelative) {
1706 target.setCompoundDrawablesRelativeWithIntrinsicBounds(d1, d2, d3, d4);
1707 } else {
1708 target.setCompoundDrawablesWithIntrinsicBounds(d1, d2, d3, d4);
1709 }
Daniel Sandler820ba322012-03-23 16:36:00 -05001710 }
1711 }
1712
Sunny Goyaldd292f42015-12-02 14:29:27 -08001713 @Override
1714 public Action initActionAsync(ViewTree root, ViewGroup rootParent, OnClickHandler handler) {
1715 final TextView target = (TextView) root.findViewById(viewId);
1716 if (target == null) return ACTION_NOOP;
1717
1718 TextViewDrawableAction copy = useIcons ?
1719 new TextViewDrawableAction(viewId, isRelative, i1, i2, i3, i4) :
1720 new TextViewDrawableAction(viewId, isRelative, d1, d2, d3, d4);
1721
1722 // Load the drawables on the background thread.
1723 copy.drawablesLoaded = true;
1724 final Context ctx = target.getContext();
1725
1726 if (useIcons) {
1727 copy.id1 = i1 == null ? null : i1.loadDrawable(ctx);
1728 copy.id2 = i2 == null ? null : i2.loadDrawable(ctx);
1729 copy.id3 = i3 == null ? null : i3.loadDrawable(ctx);
1730 copy.id4 = i4 == null ? null : i4.loadDrawable(ctx);
1731 } else {
1732 copy.id1 = d1 == 0 ? null : ctx.getDrawable(d1);
1733 copy.id2 = d2 == 0 ? null : ctx.getDrawable(d2);
1734 copy.id3 = d3 == 0 ? null : ctx.getDrawable(d3);
1735 copy.id4 = d4 == 0 ? null : ctx.getDrawable(d4);
1736 }
1737 return copy;
1738 }
1739
Adam Cohenfbe44b72012-09-19 20:36:23 -07001740 public String getActionName() {
1741 return "TextViewDrawableAction";
1742 }
1743
Daniel Sandler820ba322012-03-23 16:36:00 -05001744 boolean isRelative = false;
Dan Sandler912282e2015-07-28 22:49:30 -04001745 boolean useIcons = false;
Daniel Sandler820ba322012-03-23 16:36:00 -05001746 int d1, d2, d3, d4;
Dan Sandler912282e2015-07-28 22:49:30 -04001747 Icon i1, i2, i3, i4;
Daniel Sandler820ba322012-03-23 16:36:00 -05001748
Sunny Goyaldd292f42015-12-02 14:29:27 -08001749 boolean drawablesLoaded = false;
1750 Drawable id1, id2, id3, id4;
1751
Daniel Sandler820ba322012-03-23 16:36:00 -05001752 public final static int TAG = 11;
1753 }
1754
1755 /**
Daniel Sandler99d1f742012-05-21 16:14:14 -04001756 * Helper action to set text size on a TextView in any supported units.
Daniel Sandler7264f712012-05-21 14:48:23 -04001757 */
1758 private class TextViewSizeAction extends Action {
1759 public TextViewSizeAction(int viewId, int units, float size) {
1760 this.viewId = viewId;
1761 this.units = units;
1762 this.size = size;
1763 }
1764
1765 public TextViewSizeAction(Parcel parcel) {
1766 viewId = parcel.readInt();
1767 units = parcel.readInt();
1768 size = parcel.readFloat();
1769 }
1770
1771 public void writeToParcel(Parcel dest, int flags) {
1772 dest.writeInt(TAG);
1773 dest.writeInt(viewId);
1774 dest.writeInt(units);
1775 dest.writeFloat(size);
1776 }
1777
1778 @Override
Dianne Hackborna1940212012-06-28 16:07:22 -07001779 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
Daniel Sandler7264f712012-05-21 14:48:23 -04001780 final TextView target = (TextView) root.findViewById(viewId);
1781 if (target == null) return;
1782 target.setTextSize(units, size);
1783 }
1784
Adam Cohenfbe44b72012-09-19 20:36:23 -07001785 public String getActionName() {
1786 return "TextViewSizeAction";
1787 }
1788
Daniel Sandler7264f712012-05-21 14:48:23 -04001789 int units;
1790 float size;
1791
1792 public final static int TAG = 13;
1793 }
1794
1795 /**
Daniel Sandler99d1f742012-05-21 16:14:14 -04001796 * Helper action to set padding on a View.
1797 */
1798 private class ViewPaddingAction extends Action {
1799 public ViewPaddingAction(int viewId, int left, int top, int right, int bottom) {
1800 this.viewId = viewId;
1801 this.left = left;
1802 this.top = top;
1803 this.right = right;
1804 this.bottom = bottom;
1805 }
1806
1807 public ViewPaddingAction(Parcel parcel) {
1808 viewId = parcel.readInt();
1809 left = parcel.readInt();
1810 top = parcel.readInt();
1811 right = parcel.readInt();
1812 bottom = parcel.readInt();
1813 }
1814
1815 public void writeToParcel(Parcel dest, int flags) {
1816 dest.writeInt(TAG);
1817 dest.writeInt(viewId);
1818 dest.writeInt(left);
1819 dest.writeInt(top);
1820 dest.writeInt(right);
1821 dest.writeInt(bottom);
1822 }
1823
1824 @Override
Dianne Hackborna1940212012-06-28 16:07:22 -07001825 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
Daniel Sandler99d1f742012-05-21 16:14:14 -04001826 final View target = root.findViewById(viewId);
1827 if (target == null) return;
1828 target.setPadding(left, top, right, bottom);
1829 }
1830
Adam Cohenfbe44b72012-09-19 20:36:23 -07001831 public String getActionName() {
1832 return "ViewPaddingAction";
1833 }
1834
Daniel Sandler99d1f742012-05-21 16:14:14 -04001835 int left, top, right, bottom;
1836
1837 public final static int TAG = 14;
1838 }
1839
1840 /**
Adrian Roos9b123cf2016-02-04 14:55:57 -08001841 * Helper action to set layout params on a View.
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001842 */
Adrian Roos9b123cf2016-02-04 14:55:57 -08001843 private class LayoutParamAction extends Action {
1844
1845 /** Set marginEnd */
1846 public static final int LAYOUT_MARGIN_END = 1;
1847 /** Set width */
1848 public static final int LAYOUT_WIDTH = 2;
1849
1850 /**
1851 * @param viewId ID of the view alter
1852 * @param property which layout parameter to alter
1853 * @param value new value of the layout parameter
1854 */
1855 public LayoutParamAction(int viewId, int property, int value) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001856 this.viewId = viewId;
Adrian Roos9b123cf2016-02-04 14:55:57 -08001857 this.property = property;
1858 this.value = value;
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001859 }
1860
Adrian Roos9b123cf2016-02-04 14:55:57 -08001861 public LayoutParamAction(Parcel parcel) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001862 viewId = parcel.readInt();
Adrian Roos9b123cf2016-02-04 14:55:57 -08001863 property = parcel.readInt();
1864 value = parcel.readInt();
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001865 }
1866
1867 public void writeToParcel(Parcel dest, int flags) {
1868 dest.writeInt(TAG);
1869 dest.writeInt(viewId);
Adrian Roos9b123cf2016-02-04 14:55:57 -08001870 dest.writeInt(property);
1871 dest.writeInt(value);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001872 }
1873
1874 @Override
1875 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
1876 final View target = root.findViewById(viewId);
1877 if (target == null) {
1878 return;
1879 }
1880 ViewGroup.LayoutParams layoutParams = target.getLayoutParams();
Adrian Roos9b123cf2016-02-04 14:55:57 -08001881 if (layoutParams == null) {
1882 return;
1883 }
1884 switch (property) {
1885 case LAYOUT_MARGIN_END:
1886 if (layoutParams instanceof ViewGroup.MarginLayoutParams) {
1887 ((ViewGroup.MarginLayoutParams) layoutParams).setMarginEnd(value);
1888 target.setLayoutParams(layoutParams);
1889 }
1890 break;
1891 case LAYOUT_WIDTH:
1892 layoutParams.width = value;
1893 target.setLayoutParams(layoutParams);
1894 break;
1895 default:
1896 throw new IllegalArgumentException("Unknown property " + property);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001897 }
1898 }
1899
1900 public String getActionName() {
Adrian Roos9b123cf2016-02-04 14:55:57 -08001901 return "LayoutParamAction" + property + ".";
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001902 }
1903
Adrian Roos9b123cf2016-02-04 14:55:57 -08001904 int property;
1905 int value;
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001906
1907 public final static int TAG = 19;
1908 }
1909
1910 /**
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01001911 * Helper action to set a color filter on a compound drawable on a TextView. Supports relative
1912 * (s/t/e/b) or cardinal (l/t/r/b) arrangement.
1913 */
1914 private class TextViewDrawableColorFilterAction extends Action {
1915 public TextViewDrawableColorFilterAction(int viewId, boolean isRelative, int index,
1916 int color, PorterDuff.Mode mode) {
1917 this.viewId = viewId;
1918 this.isRelative = isRelative;
1919 this.index = index;
1920 this.color = color;
1921 this.mode = mode;
1922 }
1923
1924 public TextViewDrawableColorFilterAction(Parcel parcel) {
1925 viewId = parcel.readInt();
1926 isRelative = (parcel.readInt() != 0);
1927 index = parcel.readInt();
1928 color = parcel.readInt();
1929 mode = readPorterDuffMode(parcel);
1930 }
1931
1932 private PorterDuff.Mode readPorterDuffMode(Parcel parcel) {
1933 int mode = parcel.readInt();
1934 if (mode >= 0 && mode < PorterDuff.Mode.values().length) {
1935 return PorterDuff.Mode.values()[mode];
1936 } else {
1937 return PorterDuff.Mode.CLEAR;
1938 }
1939 }
1940
1941 public void writeToParcel(Parcel dest, int flags) {
1942 dest.writeInt(TAG);
1943 dest.writeInt(viewId);
1944 dest.writeInt(isRelative ? 1 : 0);
1945 dest.writeInt(index);
1946 dest.writeInt(color);
1947 dest.writeInt(mode.ordinal());
1948 }
1949
1950 @Override
1951 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
1952 final TextView target = (TextView) root.findViewById(viewId);
1953 if (target == null) return;
1954 Drawable[] drawables = isRelative
1955 ? target.getCompoundDrawablesRelative()
1956 : target.getCompoundDrawables();
1957 if (index < 0 || index >= 4) {
1958 throw new IllegalStateException("index must be in range [0, 3].");
1959 }
1960 Drawable d = drawables[index];
1961 if (d != null) {
1962 d.mutate();
1963 d.setColorFilter(color, mode);
1964 }
1965 }
1966
1967 public String getActionName() {
1968 return "TextViewDrawableColorFilterAction";
1969 }
1970
1971 final boolean isRelative;
1972 final int index;
1973 final int color;
1974 final PorterDuff.Mode mode;
1975
1976 public final static int TAG = 17;
1977 }
1978
1979 /**
Adrian Roosfe84e1f2015-11-04 15:55:39 -08001980 * Helper action to add a view tag with RemoteInputs.
1981 */
1982 private class SetRemoteInputsAction extends Action {
1983
1984 public SetRemoteInputsAction(int viewId, RemoteInput[] remoteInputs) {
1985 this.viewId = viewId;
1986 this.remoteInputs = remoteInputs;
1987 }
1988
1989 public SetRemoteInputsAction(Parcel parcel) {
1990 viewId = parcel.readInt();
Adrian Roos5dd685f2016-02-24 12:05:51 -08001991 remoteInputs = parcel.createTypedArray(RemoteInput.CREATOR);
Adrian Roosfe84e1f2015-11-04 15:55:39 -08001992 }
1993
1994 public void writeToParcel(Parcel dest, int flags) {
1995 dest.writeInt(TAG);
1996 dest.writeInt(viewId);
Adrian Roos5dd685f2016-02-24 12:05:51 -08001997 dest.writeTypedArray(remoteInputs, flags);
Adrian Roosfe84e1f2015-11-04 15:55:39 -08001998 }
1999
2000 @Override
2001 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
2002 final TextView target = (TextView) root.findViewById(viewId);
2003 if (target == null) return;
2004
2005 target.setTagInternal(R.id.remote_input_tag, remoteInputs);
2006 }
2007
2008 public String getActionName() {
2009 return "SetRemoteInputsAction";
2010 }
2011
2012 final Parcelable[] remoteInputs;
2013 public final static int TAG = 18;
2014 }
2015
2016 /**
Winson Chung3ec9a452010-09-23 16:40:28 -07002017 * Simple class used to keep track of memory usage in a RemoteViews.
2018 *
2019 */
2020 private class MemoryUsageCounter {
2021 public void clear() {
Adam Cohen5d200642012-04-24 10:43:31 -07002022 mMemoryUsage = 0;
Winson Chung3ec9a452010-09-23 16:40:28 -07002023 }
2024
Adam Cohen5d200642012-04-24 10:43:31 -07002025 public void increment(int numBytes) {
2026 mMemoryUsage += numBytes;
Winson Chung3ec9a452010-09-23 16:40:28 -07002027 }
2028
Adam Cohen5d200642012-04-24 10:43:31 -07002029 public int getMemoryUsage() {
2030 return mMemoryUsage;
Winson Chung3ec9a452010-09-23 16:40:28 -07002031 }
2032
Romain Guye4d4e202013-07-22 13:02:02 -07002033 @SuppressWarnings("deprecation")
Adam Cohen5d200642012-04-24 10:43:31 -07002034 public void addBitmapMemory(Bitmap b) {
2035 final Bitmap.Config c = b.getConfig();
2036 // If we don't know, be pessimistic and assume 4
2037 int bpp = 4;
2038 if (c != null) {
2039 switch (c) {
Svetoslav976e8bd2014-07-16 15:12:03 -07002040 case ALPHA_8:
2041 bpp = 1;
2042 break;
2043 case RGB_565:
2044 case ARGB_4444:
2045 bpp = 2;
2046 break;
2047 case ARGB_8888:
2048 bpp = 4;
2049 break;
Adam Cohen5d200642012-04-24 10:43:31 -07002050 }
2051 }
2052 increment(b.getWidth() * b.getHeight() * bpp);
2053 }
2054
2055 int mMemoryUsage;
Winson Chung3ec9a452010-09-23 16:40:28 -07002056 }
2057
2058 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002059 * Create a new RemoteViews object that will display the views contained
2060 * in the specified layout file.
Jim Millere667a7a2012-08-09 19:22:32 -07002061 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002062 * @param packageName Name of the package that contains the layout resource
2063 * @param layoutId The id of the layout resource
2064 */
2065 public RemoteViews(String packageName, int layoutId) {
Svet Ganov0da85b62014-08-06 14:11:37 -07002066 this(getApplicationInfo(packageName, UserHandle.myUserId()), layoutId);
2067 }
2068
2069 /**
2070 * Create a new RemoteViews object that will display the views contained
2071 * in the specified layout file.
2072 *
2073 * @param packageName Name of the package that contains the layout resource.
2074 * @param userId The user under which the package is running.
2075 * @param layoutId The id of the layout resource.
2076 *
2077 * @hide
2078 */
2079 public RemoteViews(String packageName, int userId, int layoutId) {
2080 this(getApplicationInfo(packageName, userId), layoutId);
2081 }
2082
2083 /**
2084 * Create a new RemoteViews object that will display the views contained
2085 * in the specified layout file.
2086 *
2087 * @param application The application whose content is shown by the views.
2088 * @param layoutId The id of the layout resource.
Kenny Guy77320062014-08-27 21:37:15 +01002089 *
2090 * @hide
Svet Ganov0da85b62014-08-06 14:11:37 -07002091 */
Kenny Guy77320062014-08-27 21:37:15 +01002092 protected RemoteViews(ApplicationInfo application, int layoutId) {
Svet Ganov0da85b62014-08-06 14:11:37 -07002093 mApplication = application;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002094 mLayoutId = layoutId;
Adam Cohen5d200642012-04-24 10:43:31 -07002095 mBitmapCache = new BitmapCache();
Winson Chung3ec9a452010-09-23 16:40:28 -07002096 // setup the memory usage statistics
2097 mMemoryUsageCounter = new MemoryUsageCounter();
2098 recalculateMemoryUsage();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002099 }
2100
Adam Cohen5d200642012-04-24 10:43:31 -07002101 private boolean hasLandscapeAndPortraitLayouts() {
2102 return (mLandscape != null) && (mPortrait != null);
2103 }
2104
Jeff Sharkey6d515712012-09-20 16:06:08 -07002105 /**
Adam Cohen5d200642012-04-24 10:43:31 -07002106 * Create a new RemoteViews object that will inflate as the specified
2107 * landspace or portrait RemoteViews, depending on the current configuration.
2108 *
2109 * @param landscape The RemoteViews to inflate in landscape configuration
2110 * @param portrait The RemoteViews to inflate in portrait configuration
2111 */
2112 public RemoteViews(RemoteViews landscape, RemoteViews portrait) {
2113 if (landscape == null || portrait == null) {
2114 throw new RuntimeException("Both RemoteViews must be non-null");
2115 }
Svet Ganov0da85b62014-08-06 14:11:37 -07002116 if (landscape.mApplication.uid != portrait.mApplication.uid
2117 || !landscape.mApplication.packageName.equals(portrait.mApplication.packageName)) {
2118 throw new RuntimeException("Both RemoteViews must share the same package and user");
Adam Cohen5d200642012-04-24 10:43:31 -07002119 }
Svet Ganov0da85b62014-08-06 14:11:37 -07002120 mApplication = portrait.mApplication;
Adam Cohen5d200642012-04-24 10:43:31 -07002121 mLayoutId = portrait.getLayoutId();
2122
2123 mLandscape = landscape;
2124 mPortrait = portrait;
2125
2126 // setup the memory usage statistics
2127 mMemoryUsageCounter = new MemoryUsageCounter();
2128
2129 mBitmapCache = new BitmapCache();
2130 configureRemoteViewsAsChild(landscape);
2131 configureRemoteViewsAsChild(portrait);
2132
2133 recalculateMemoryUsage();
2134 }
2135
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002136 /**
2137 * Reads a RemoteViews object from a parcel.
Jim Millere667a7a2012-08-09 19:22:32 -07002138 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002139 * @param parcel
2140 */
2141 public RemoteViews(Parcel parcel) {
Adam Cohen5d200642012-04-24 10:43:31 -07002142 this(parcel, null);
2143 }
Adam Cohenca6fd842010-09-03 18:10:35 -07002144
Adam Cohen5d200642012-04-24 10:43:31 -07002145 private RemoteViews(Parcel parcel, BitmapCache bitmapCache) {
2146 int mode = parcel.readInt();
2147
2148 // We only store a bitmap cache in the root of the RemoteViews.
2149 if (bitmapCache == null) {
2150 mBitmapCache = new BitmapCache(parcel);
2151 } else {
2152 setBitmapCache(bitmapCache);
2153 setNotRoot();
2154 }
2155
2156 if (mode == MODE_NORMAL) {
Svet Ganov0da85b62014-08-06 14:11:37 -07002157 mApplication = parcel.readParcelable(null);
Adam Cohen5d200642012-04-24 10:43:31 -07002158 mLayoutId = parcel.readInt();
Romain Guye4d4e202013-07-22 13:02:02 -07002159 mIsWidgetCollectionChild = parcel.readInt() == 1;
Adam Cohen5d200642012-04-24 10:43:31 -07002160
2161 int count = parcel.readInt();
2162 if (count > 0) {
2163 mActions = new ArrayList<Action>(count);
2164 for (int i=0; i<count; i++) {
2165 int tag = parcel.readInt();
2166 switch (tag) {
Svetoslav976e8bd2014-07-16 15:12:03 -07002167 case SetOnClickPendingIntent.TAG:
2168 mActions.add(new SetOnClickPendingIntent(parcel));
2169 break;
2170 case SetDrawableParameters.TAG:
2171 mActions.add(new SetDrawableParameters(parcel));
2172 break;
2173 case ReflectionAction.TAG:
2174 mActions.add(new ReflectionAction(parcel));
2175 break;
2176 case ViewGroupAction.TAG:
2177 mActions.add(new ViewGroupAction(parcel, mBitmapCache));
2178 break;
2179 case ReflectionActionWithoutParams.TAG:
2180 mActions.add(new ReflectionActionWithoutParams(parcel));
2181 break;
2182 case SetEmptyView.TAG:
2183 mActions.add(new SetEmptyView(parcel));
2184 break;
2185 case SetPendingIntentTemplate.TAG:
2186 mActions.add(new SetPendingIntentTemplate(parcel));
2187 break;
2188 case SetOnClickFillInIntent.TAG:
2189 mActions.add(new SetOnClickFillInIntent(parcel));
2190 break;
2191 case SetRemoteViewsAdapterIntent.TAG:
2192 mActions.add(new SetRemoteViewsAdapterIntent(parcel));
2193 break;
2194 case TextViewDrawableAction.TAG:
2195 mActions.add(new TextViewDrawableAction(parcel));
2196 break;
2197 case TextViewSizeAction.TAG:
2198 mActions.add(new TextViewSizeAction(parcel));
2199 break;
2200 case ViewPaddingAction.TAG:
2201 mActions.add(new ViewPaddingAction(parcel));
2202 break;
2203 case BitmapReflectionAction.TAG:
2204 mActions.add(new BitmapReflectionAction(parcel));
2205 break;
2206 case SetRemoteViewsAdapterList.TAG:
2207 mActions.add(new SetRemoteViewsAdapterList(parcel));
2208 break;
2209 case TextViewDrawableColorFilterAction.TAG:
2210 mActions.add(new TextViewDrawableColorFilterAction(parcel));
2211 break;
Adrian Roosfe84e1f2015-11-04 15:55:39 -08002212 case SetRemoteInputsAction.TAG:
2213 mActions.add(new SetRemoteInputsAction(parcel));
2214 break;
Adrian Roos9b123cf2016-02-04 14:55:57 -08002215 case LayoutParamAction.TAG:
2216 mActions.add(new LayoutParamAction(parcel));
Selim Cinek65b2e7c2015-10-26 14:11:31 -07002217 break;
Svetoslav976e8bd2014-07-16 15:12:03 -07002218 default:
2219 throw new ActionException("Tag " + tag + " not found");
Adam Cohen5d200642012-04-24 10:43:31 -07002220 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002221 }
2222 }
Adam Cohen5d200642012-04-24 10:43:31 -07002223 } else {
2224 // MODE_HAS_LANDSCAPE_AND_PORTRAIT
2225 mLandscape = new RemoteViews(parcel, mBitmapCache);
2226 mPortrait = new RemoteViews(parcel, mBitmapCache);
Svet Ganov0da85b62014-08-06 14:11:37 -07002227 mApplication = mPortrait.mApplication;
Adam Cohen5d200642012-04-24 10:43:31 -07002228 mLayoutId = mPortrait.getLayoutId();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002229 }
Winson Chung3ec9a452010-09-23 16:40:28 -07002230
2231 // setup the memory usage statistics
2232 mMemoryUsageCounter = new MemoryUsageCounter();
2233 recalculateMemoryUsage();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002234 }
2235
Winson Chung3ec9a452010-09-23 16:40:28 -07002236
Adam Cohen3ff2d862012-09-26 14:07:57 -07002237 public RemoteViews clone() {
Adrian Roos7da889d2016-03-16 18:38:58 -07002238 Preconditions.checkState(mIsRoot, "RemoteView has been attached to another RemoteView. "
2239 + "May only clone the root of a RemoteView hierarchy.");
2240
Adam Cohen3ff2d862012-09-26 14:07:57 -07002241 Parcel p = Parcel.obtain();
Adrian Roos7da889d2016-03-16 18:38:58 -07002242
2243 // Do not parcel the Bitmap cache - doing so creates an expensive copy of all bitmaps.
2244 // Instead pretend we're not owning the cache while parceling.
2245 mIsRoot = false;
Adam Cohen3ff2d862012-09-26 14:07:57 -07002246 writeToParcel(p, 0);
2247 p.setDataPosition(0);
Adrian Roos7da889d2016-03-16 18:38:58 -07002248 mIsRoot = true;
2249
2250 RemoteViews rv = new RemoteViews(p, mBitmapCache.clone());
2251 rv.mIsRoot = true;
2252
Maunik Shahdb1a9a32014-06-19 14:18:39 +05302253 p.recycle();
2254 return rv;
Joe Onorato18e69df2010-05-17 22:26:12 -07002255 }
2256
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002257 public String getPackage() {
Svetoslavb6242442014-09-19 13:21:55 -07002258 return (mApplication != null) ? mApplication.packageName : null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002259 }
2260
Adam Cohen5d200642012-04-24 10:43:31 -07002261 /**
Adrian Roos7da889d2016-03-16 18:38:58 -07002262 * Returns the layout id of the root layout associated with this RemoteViews. In the case
Adam Cohen5d200642012-04-24 10:43:31 -07002263 * that the RemoteViews has both a landscape and portrait root, this will return the layout
2264 * id associated with the portrait layout.
2265 *
2266 * @return the layout id.
2267 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002268 public int getLayoutId() {
2269 return mLayoutId;
2270 }
2271
Winson Chung3ec9a452010-09-23 16:40:28 -07002272 /*
Adam Cohenca6fd842010-09-03 18:10:35 -07002273 * This flag indicates whether this RemoteViews object is being created from a
2274 * RemoteViewsService for use as a child of a widget collection. This flag is used
2275 * to determine whether or not certain features are available, in particular,
2276 * setting on click extras and setting on click pending intents. The former is enabled,
2277 * and the latter disabled when this flag is true.
2278 */
2279 void setIsWidgetCollectionChild(boolean isWidgetCollectionChild) {
2280 mIsWidgetCollectionChild = isWidgetCollectionChild;
2281 }
2282
2283 /**
Winson Chung3ec9a452010-09-23 16:40:28 -07002284 * Updates the memory usage statistics.
2285 */
2286 private void recalculateMemoryUsage() {
2287 mMemoryUsageCounter.clear();
2288
Adam Cohen5d200642012-04-24 10:43:31 -07002289 if (!hasLandscapeAndPortraitLayouts()) {
2290 // Accumulate the memory usage for each action
2291 if (mActions != null) {
2292 final int count = mActions.size();
2293 for (int i= 0; i < count; ++i) {
2294 mActions.get(i).updateMemoryUsageEstimate(mMemoryUsageCounter);
2295 }
Winson Chung3ec9a452010-09-23 16:40:28 -07002296 }
Adam Cohen5d200642012-04-24 10:43:31 -07002297 if (mIsRoot) {
2298 mBitmapCache.addBitmapMemory(mMemoryUsageCounter);
2299 }
2300 } else {
2301 mMemoryUsageCounter.increment(mLandscape.estimateMemoryUsage());
2302 mMemoryUsageCounter.increment(mPortrait.estimateMemoryUsage());
2303 mBitmapCache.addBitmapMemory(mMemoryUsageCounter);
2304 }
2305 }
2306
2307 /**
2308 * Recursively sets BitmapCache in the hierarchy and update the bitmap ids.
2309 */
2310 private void setBitmapCache(BitmapCache bitmapCache) {
2311 mBitmapCache = bitmapCache;
2312 if (!hasLandscapeAndPortraitLayouts()) {
2313 if (mActions != null) {
2314 final int count = mActions.size();
2315 for (int i= 0; i < count; ++i) {
2316 mActions.get(i).setBitmapCache(bitmapCache);
2317 }
2318 }
2319 } else {
2320 mLandscape.setBitmapCache(bitmapCache);
2321 mPortrait.setBitmapCache(bitmapCache);
Winson Chung3ec9a452010-09-23 16:40:28 -07002322 }
2323 }
2324
2325 /**
2326 * Returns an estimate of the bitmap heap memory usage for this RemoteViews.
2327 */
Adam Cohen311c79c2012-05-10 14:44:38 -07002328 /** @hide */
2329 public int estimateMemoryUsage() {
Adam Cohen5d200642012-04-24 10:43:31 -07002330 return mMemoryUsageCounter.getMemoryUsage();
Winson Chung3ec9a452010-09-23 16:40:28 -07002331 }
2332
2333 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002334 * Add an action to be executed on the remote side when apply is called.
Jim Millere667a7a2012-08-09 19:22:32 -07002335 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002336 * @param a The action to add
2337 */
2338 private void addAction(Action a) {
Adam Cohen5d200642012-04-24 10:43:31 -07002339 if (hasLandscapeAndPortraitLayouts()) {
2340 throw new RuntimeException("RemoteViews specifying separate landscape and portrait" +
2341 " layouts cannot be modified. Instead, fully configure the landscape and" +
2342 " portrait layouts individually before constructing the combined layout.");
2343 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002344 if (mActions == null) {
2345 mActions = new ArrayList<Action>();
2346 }
2347 mActions.add(a);
Winson Chung3ec9a452010-09-23 16:40:28 -07002348
2349 // update the memory usage stats
2350 a.updateMemoryUsageEstimate(mMemoryUsageCounter);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002351 }
Jeff Sharkey1162fd72009-11-04 17:58:08 -08002352
2353 /**
2354 * Equivalent to calling {@link ViewGroup#addView(View)} after inflating the
2355 * given {@link RemoteViews}. This allows users to build "nested"
2356 * {@link RemoteViews}. In cases where consumers of {@link RemoteViews} may
2357 * recycle layouts, use {@link #removeAllViews(int)} to clear any existing
2358 * children.
2359 *
2360 * @param viewId The id of the parent {@link ViewGroup} to add child into.
2361 * @param nestedView {@link RemoteViews} that describes the child.
2362 */
2363 public void addView(int viewId, RemoteViews nestedView) {
2364 addAction(new ViewGroupAction(viewId, nestedView));
2365 }
2366
2367 /**
2368 * Equivalent to calling {@link ViewGroup#removeAllViews()}.
2369 *
2370 * @param viewId The id of the parent {@link ViewGroup} to remove all
2371 * children from.
2372 */
2373 public void removeAllViews(int viewId) {
2374 addAction(new ViewGroupAction(viewId, null));
2375 }
2376
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002377 /**
Adam Cohen0b96a572011-02-10 15:56:16 -08002378 * Equivalent to calling {@link AdapterViewAnimator#showNext()}
Adam Cohen2dd21972010-08-15 18:20:04 -07002379 *
Adam Cohen0b96a572011-02-10 15:56:16 -08002380 * @param viewId The id of the view on which to call {@link AdapterViewAnimator#showNext()}
Adam Cohen2dd21972010-08-15 18:20:04 -07002381 */
2382 public void showNext(int viewId) {
2383 addAction(new ReflectionActionWithoutParams(viewId, "showNext"));
2384 }
2385
2386 /**
Adam Cohen0b96a572011-02-10 15:56:16 -08002387 * Equivalent to calling {@link AdapterViewAnimator#showPrevious()}
Adam Cohen2dd21972010-08-15 18:20:04 -07002388 *
Adam Cohen0b96a572011-02-10 15:56:16 -08002389 * @param viewId The id of the view on which to call {@link AdapterViewAnimator#showPrevious()}
Adam Cohen2dd21972010-08-15 18:20:04 -07002390 */
2391 public void showPrevious(int viewId) {
2392 addAction(new ReflectionActionWithoutParams(viewId, "showPrevious"));
2393 }
2394
2395 /**
Adam Cohen0b96a572011-02-10 15:56:16 -08002396 * Equivalent to calling {@link AdapterViewAnimator#setDisplayedChild(int)}
2397 *
2398 * @param viewId The id of the view on which to call
2399 * {@link AdapterViewAnimator#setDisplayedChild(int)}
2400 */
2401 public void setDisplayedChild(int viewId, int childIndex) {
2402 setInt(viewId, "setDisplayedChild", childIndex);
2403 }
2404
2405 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002406 * Equivalent to calling View.setVisibility
Jim Millere667a7a2012-08-09 19:22:32 -07002407 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002408 * @param viewId The id of the view whose visibility should change
2409 * @param visibility The new visibility for the view
2410 */
2411 public void setViewVisibility(int viewId, int visibility) {
2412 setInt(viewId, "setVisibility", visibility);
2413 }
Adam Cohenca6fd842010-09-03 18:10:35 -07002414
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002415 /**
2416 * Equivalent to calling TextView.setText
Jim Millere667a7a2012-08-09 19:22:32 -07002417 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002418 * @param viewId The id of the view whose text should change
2419 * @param text The new text for the view
2420 */
2421 public void setTextViewText(int viewId, CharSequence text) {
2422 setCharSequence(viewId, "setText", text);
2423 }
Daniel Sandler7264f712012-05-21 14:48:23 -04002424
2425 /**
Daniel Sandler7264f712012-05-21 14:48:23 -04002426 * Equivalent to calling {@link TextView#setTextSize(int, float)}
Jim Millere667a7a2012-08-09 19:22:32 -07002427 *
Daniel Sandler7264f712012-05-21 14:48:23 -04002428 * @param viewId The id of the view whose text size should change
2429 * @param units The units of size (e.g. COMPLEX_UNIT_SP)
2430 * @param size The size of the text
2431 */
2432 public void setTextViewTextSize(int viewId, int units, float size) {
2433 addAction(new TextViewSizeAction(viewId, units, size));
2434 }
2435
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002436 /**
Jim Millere667a7a2012-08-09 19:22:32 -07002437 * Equivalent to calling
Daniel Sandler820ba322012-03-23 16:36:00 -05002438 * {@link TextView#setCompoundDrawablesWithIntrinsicBounds(int, int, int, int)}.
2439 *
2440 * @param viewId The id of the view whose text should change
2441 * @param left The id of a drawable to place to the left of the text, or 0
2442 * @param top The id of a drawable to place above the text, or 0
2443 * @param right The id of a drawable to place to the right of the text, or 0
Jim Millere667a7a2012-08-09 19:22:32 -07002444 * @param bottom The id of a drawable to place below the text, or 0
Daniel Sandler820ba322012-03-23 16:36:00 -05002445 */
2446 public void setTextViewCompoundDrawables(int viewId, int left, int top, int right, int bottom) {
2447 addAction(new TextViewDrawableAction(viewId, false, left, top, right, bottom));
2448 }
2449
2450 /**
Jim Millere667a7a2012-08-09 19:22:32 -07002451 * Equivalent to calling {@link
Daniel Sandler820ba322012-03-23 16:36:00 -05002452 * TextView#setCompoundDrawablesRelativeWithIntrinsicBounds(int, int, int, int)}.
2453 *
2454 * @param viewId The id of the view whose text should change
Jim Millere667a7a2012-08-09 19:22:32 -07002455 * @param start The id of a drawable to place before the text (relative to the
Daniel Sandler820ba322012-03-23 16:36:00 -05002456 * layout direction), or 0
2457 * @param top The id of a drawable to place above the text, or 0
2458 * @param end The id of a drawable to place after the text, or 0
Fabrice Di Meglio66388dc2012-05-03 18:51:57 -07002459 * @param bottom The id of a drawable to place below the text, or 0
Daniel Sandler820ba322012-03-23 16:36:00 -05002460 */
2461 public void setTextViewCompoundDrawablesRelative(int viewId, int start, int top, int end, int bottom) {
2462 addAction(new TextViewDrawableAction(viewId, true, start, top, end, bottom));
2463 }
2464
2465 /**
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01002466 * Equivalent to applying a color filter on one of the drawables in
2467 * {@link android.widget.TextView#getCompoundDrawablesRelative()}.
2468 *
2469 * @param viewId The id of the view whose text should change.
2470 * @param index The index of the drawable in the array of
2471 * {@link android.widget.TextView#getCompoundDrawablesRelative()} to set the color
2472 * filter on. Must be in [0, 3].
2473 * @param color The color of the color filter. See
2474 * {@link Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)}.
2475 * @param mode The mode of the color filter. See
2476 * {@link Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)}.
2477 * @hide
2478 */
2479 public void setTextViewCompoundDrawablesRelativeColorFilter(int viewId,
2480 int index, int color, PorterDuff.Mode mode) {
2481 if (index < 0 || index >= 4) {
2482 throw new IllegalArgumentException("index must be in range [0, 3].");
2483 }
2484 addAction(new TextViewDrawableColorFilterAction(viewId, true, index, color, mode));
2485 }
2486
2487 /**
Dan Sandler912282e2015-07-28 22:49:30 -04002488 * Equivalent to calling {@link
2489 * TextView#setCompoundDrawablesWithIntrinsicBounds(Drawable, Drawable, Drawable, Drawable)}
2490 * using the drawables yielded by {@link Icon#loadDrawable(Context)}.
2491 *
2492 * @param viewId The id of the view whose text should change
2493 * @param left an Icon to place to the left of the text, or 0
2494 * @param top an Icon to place above the text, or 0
2495 * @param right an Icon to place to the right of the text, or 0
2496 * @param bottom an Icon to place below the text, or 0
2497 *
2498 * @hide
2499 */
2500 public void setTextViewCompoundDrawables(int viewId, Icon left, Icon top, Icon right, Icon bottom) {
2501 addAction(new TextViewDrawableAction(viewId, false, left, top, right, bottom));
2502 }
2503
2504 /**
2505 * Equivalent to calling {@link
2506 * TextView#setCompoundDrawablesRelativeWithIntrinsicBounds(Drawable, Drawable, Drawable, Drawable)}
2507 * using the drawables yielded by {@link Icon#loadDrawable(Context)}.
2508 *
2509 * @param viewId The id of the view whose text should change
2510 * @param start an Icon to place before the text (relative to the
2511 * layout direction), or 0
2512 * @param top an Icon to place above the text, or 0
2513 * @param end an Icon to place after the text, or 0
2514 * @param bottom an Icon to place below the text, or 0
2515 *
2516 * @hide
2517 */
2518 public void setTextViewCompoundDrawablesRelative(int viewId, Icon start, Icon top, Icon end, Icon bottom) {
2519 addAction(new TextViewDrawableAction(viewId, true, start, top, end, bottom));
2520 }
2521
2522 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002523 * Equivalent to calling ImageView.setImageResource
Jim Millere667a7a2012-08-09 19:22:32 -07002524 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002525 * @param viewId The id of the view whose drawable should change
2526 * @param srcId The new resource id for the drawable
2527 */
Jim Millere667a7a2012-08-09 19:22:32 -07002528 public void setImageViewResource(int viewId, int srcId) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002529 setInt(viewId, "setImageResource", srcId);
2530 }
2531
2532 /**
2533 * Equivalent to calling ImageView.setImageURI
Jim Millere667a7a2012-08-09 19:22:32 -07002534 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002535 * @param viewId The id of the view whose drawable should change
2536 * @param uri The Uri for the image
2537 */
2538 public void setImageViewUri(int viewId, Uri uri) {
2539 setUri(viewId, "setImageURI", uri);
2540 }
2541
2542 /**
2543 * Equivalent to calling ImageView.setImageBitmap
Jim Millere667a7a2012-08-09 19:22:32 -07002544 *
Scott Main93dc6422012-02-24 12:04:06 -08002545 * @param viewId The id of the view whose bitmap should change
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002546 * @param bitmap The new Bitmap for the drawable
2547 */
2548 public void setImageViewBitmap(int viewId, Bitmap bitmap) {
2549 setBitmap(viewId, "setImageBitmap", bitmap);
2550 }
2551
2552 /**
Dan Sandlera22a3802015-05-13 00:12:47 -04002553 * Equivalent to calling ImageView.setImageIcon
2554 *
2555 * @param viewId The id of the view whose bitmap should change
2556 * @param icon The new Icon for the ImageView
2557 */
2558 public void setImageViewIcon(int viewId, Icon icon) {
2559 setIcon(viewId, "setImageIcon", icon);
2560 }
2561
2562 /**
Adam Cohen1480fdd2010-08-25 17:24:53 -07002563 * Equivalent to calling AdapterView.setEmptyView
2564 *
2565 * @param viewId The id of the view on which to set the empty view
2566 * @param emptyViewId The view id of the empty view
2567 */
2568 public void setEmptyView(int viewId, int emptyViewId) {
2569 addAction(new SetEmptyView(viewId, emptyViewId));
2570 }
2571
2572 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002573 * Equivalent to calling {@link Chronometer#setBase Chronometer.setBase},
2574 * {@link Chronometer#setFormat Chronometer.setFormat},
2575 * and {@link Chronometer#start Chronometer.start()} or
2576 * {@link Chronometer#stop Chronometer.stop()}.
Jim Millere667a7a2012-08-09 19:22:32 -07002577 *
Scott Main93dc6422012-02-24 12:04:06 -08002578 * @param viewId The id of the {@link Chronometer} to change
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002579 * @param base The time at which the timer would have read 0:00. This
2580 * time should be based off of
2581 * {@link android.os.SystemClock#elapsedRealtime SystemClock.elapsedRealtime()}.
2582 * @param format The Chronometer format string, or null to
2583 * simply display the timer value.
2584 * @param started True if you want the clock to be started, false if not.
Selim Cinek81c23aa2016-02-25 16:23:13 -08002585 *
2586 * @see #setChronometerCountsDown(int, boolean)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002587 */
2588 public void setChronometer(int viewId, long base, String format, boolean started) {
2589 setLong(viewId, "setBase", base);
2590 setString(viewId, "setFormat", format);
2591 setBoolean(viewId, "setStarted", started);
2592 }
Jim Millere667a7a2012-08-09 19:22:32 -07002593
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002594 /**
Selim Cinek81c23aa2016-02-25 16:23:13 -08002595 * Equivalent to calling {@link Chronometer#setCountDown(boolean) Chronometer.setCountDown} on
2596 * the chronometer with the given viewId.
2597 *
2598 * @param viewId The id of the {@link Chronometer} to change
2599 * @param isCountDown True if you want the chronometer to count down to base instead of
2600 * counting up.
2601 */
2602 public void setChronometerCountsDown(int viewId, boolean isCountDown) {
2603 setBoolean(viewId, "setCountDown", isCountDown);
2604 }
2605
2606 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002607 * Equivalent to calling {@link ProgressBar#setMax ProgressBar.setMax},
2608 * {@link ProgressBar#setProgress ProgressBar.setProgress}, and
2609 * {@link ProgressBar#setIndeterminate ProgressBar.setIndeterminate}
2610 *
2611 * If indeterminate is true, then the values for max and progress are ignored.
Jim Millere667a7a2012-08-09 19:22:32 -07002612 *
Scott Main93dc6422012-02-24 12:04:06 -08002613 * @param viewId The id of the {@link ProgressBar} to change
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002614 * @param max The 100% value for the progress bar
2615 * @param progress The current value of the progress bar.
Jim Millere667a7a2012-08-09 19:22:32 -07002616 * @param indeterminate True if the progress bar is indeterminate,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002617 * false if not.
2618 */
Jim Millere667a7a2012-08-09 19:22:32 -07002619 public void setProgressBar(int viewId, int max, int progress,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002620 boolean indeterminate) {
2621 setBoolean(viewId, "setIndeterminate", indeterminate);
2622 if (!indeterminate) {
2623 setInt(viewId, "setMax", max);
2624 setInt(viewId, "setProgress", progress);
2625 }
2626 }
Jim Millere667a7a2012-08-09 19:22:32 -07002627
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002628 /**
2629 * Equivalent to calling
2630 * {@link android.view.View#setOnClickListener(android.view.View.OnClickListener)}
2631 * to launch the provided {@link PendingIntent}.
Jim Millere667a7a2012-08-09 19:22:32 -07002632 *
Adam Cohen35ae9ca2010-09-20 15:20:41 -07002633 * When setting the on-click action of items within collections (eg. {@link ListView},
2634 * {@link StackView} etc.), this method will not work. Instead, use {@link
2635 * RemoteViews#setPendingIntentTemplate(int, PendingIntent) in conjunction with
2636 * RemoteViews#setOnClickFillInIntent(int, Intent).
2637 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002638 * @param viewId The id of the view that will trigger the {@link PendingIntent} when clicked
2639 * @param pendingIntent The {@link PendingIntent} to send when user clicks
2640 */
2641 public void setOnClickPendingIntent(int viewId, PendingIntent pendingIntent) {
2642 addAction(new SetOnClickPendingIntent(viewId, pendingIntent));
2643 }
2644
2645 /**
Adam Cohen35ae9ca2010-09-20 15:20:41 -07002646 * When using collections (eg. {@link ListView}, {@link StackView} etc.) in widgets, it is very
2647 * costly to set PendingIntents on the individual items, and is hence not permitted. Instead
2648 * this method should be used to set a single PendingIntent template on the collection, and
2649 * individual items can differentiate their on-click behavior using
2650 * {@link RemoteViews#setOnClickFillInIntent(int, Intent)}.
Adam Cohenca6fd842010-09-03 18:10:35 -07002651 *
2652 * @param viewId The id of the collection who's children will use this PendingIntent template
2653 * when clicked
2654 * @param pendingIntentTemplate The {@link PendingIntent} to be combined with extras specified
2655 * by a child of viewId and executed when that child is clicked
2656 */
2657 public void setPendingIntentTemplate(int viewId, PendingIntent pendingIntentTemplate) {
2658 addAction(new SetPendingIntentTemplate(viewId, pendingIntentTemplate));
2659 }
2660
2661 /**
Adam Cohen35ae9ca2010-09-20 15:20:41 -07002662 * When using collections (eg. {@link ListView}, {@link StackView} etc.) in widgets, it is very
2663 * costly to set PendingIntents on the individual items, and is hence not permitted. Instead
2664 * a single PendingIntent template can be set on the collection, see {@link
2665 * RemoteViews#setPendingIntentTemplate(int, PendingIntent)}, and the individual on-click
2666 * action of a given item can be distinguished by setting a fillInIntent on that item. The
2667 * fillInIntent is then combined with the PendingIntent template in order to determine the final
2668 * intent which will be executed when the item is clicked. This works as follows: any fields
2669 * which are left blank in the PendingIntent template, but are provided by the fillInIntent
2670 * will be overwritten, and the resulting PendingIntent will be used.
2671 *
2672 *
2673 * of the PendingIntent template will then be filled in with the associated fields that are
2674 * set in fillInIntent. See {@link Intent#fillIn(Intent, int)} for more details.
2675 *
2676 * @param viewId The id of the view on which to set the fillInIntent
2677 * @param fillInIntent The intent which will be combined with the parent's PendingIntent
2678 * in order to determine the on-click behavior of the view specified by viewId
2679 */
2680 public void setOnClickFillInIntent(int viewId, Intent fillInIntent) {
2681 addAction(new SetOnClickFillInIntent(viewId, fillInIntent));
2682 }
2683
2684 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002685 * @hide
2686 * Equivalent to calling a combination of {@link Drawable#setAlpha(int)},
2687 * {@link Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)},
2688 * and/or {@link Drawable#setLevel(int)} on the {@link Drawable} of a given
2689 * view.
2690 * <p>
2691 * You can omit specific calls by marking their values with null or -1.
Jim Millere667a7a2012-08-09 19:22:32 -07002692 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002693 * @param viewId The id of the view that contains the target
2694 * {@link Drawable}
2695 * @param targetBackground If true, apply these parameters to the
2696 * {@link Drawable} returned by
2697 * {@link android.view.View#getBackground()}. Otherwise, assume
2698 * the target view is an {@link ImageView} and apply them to
2699 * {@link ImageView#getDrawable()}.
2700 * @param alpha Specify an alpha value for the drawable, or -1 to leave
2701 * unchanged.
2702 * @param colorFilter Specify a color for a
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02002703 * {@link android.graphics.ColorFilter} for this drawable. This will be ignored if
2704 * {@code mode} is {@code null}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002705 * @param mode Specify a PorterDuff mode for this drawable, or null to leave
2706 * unchanged.
2707 * @param level Specify the level for the drawable, or -1 to leave
2708 * unchanged.
2709 */
2710 public void setDrawableParameters(int viewId, boolean targetBackground, int alpha,
2711 int colorFilter, PorterDuff.Mode mode, int level) {
2712 addAction(new SetDrawableParameters(viewId, targetBackground, alpha,
2713 colorFilter, mode, level));
2714 }
2715
2716 /**
Jorim Jaggief72a192014-08-26 21:57:46 +02002717 * @hide
2718 * Equivalent to calling {@link android.widget.ProgressBar#setProgressTintList}.
2719 *
2720 * @param viewId The id of the view whose tint should change
2721 * @param tint the tint to apply, may be {@code null} to clear tint
2722 */
2723 public void setProgressTintList(int viewId, ColorStateList tint) {
2724 addAction(new ReflectionAction(viewId, "setProgressTintList",
2725 ReflectionAction.COLOR_STATE_LIST, tint));
2726 }
2727
2728 /**
2729 * @hide
2730 * Equivalent to calling {@link android.widget.ProgressBar#setProgressBackgroundTintList}.
2731 *
2732 * @param viewId The id of the view whose tint should change
2733 * @param tint the tint to apply, may be {@code null} to clear tint
2734 */
2735 public void setProgressBackgroundTintList(int viewId, ColorStateList tint) {
2736 addAction(new ReflectionAction(viewId, "setProgressBackgroundTintList",
2737 ReflectionAction.COLOR_STATE_LIST, tint));
2738 }
2739
2740 /**
2741 * @hide
2742 * Equivalent to calling {@link android.widget.ProgressBar#setIndeterminateTintList}.
2743 *
2744 * @param viewId The id of the view whose tint should change
2745 * @param tint the tint to apply, may be {@code null} to clear tint
2746 */
2747 public void setProgressIndeterminateTintList(int viewId, ColorStateList tint) {
2748 addAction(new ReflectionAction(viewId, "setIndeterminateTintList",
2749 ReflectionAction.COLOR_STATE_LIST, tint));
2750 }
2751
2752 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002753 * Equivalent to calling {@link android.widget.TextView#setTextColor(int)}.
Jim Millere667a7a2012-08-09 19:22:32 -07002754 *
Scott Main93dc6422012-02-24 12:04:06 -08002755 * @param viewId The id of the view whose text color should change
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002756 * @param color Sets the text color for all the states (normal, selected,
2757 * focused) to be this color.
2758 */
Tor Norbye80756e32015-03-02 09:39:27 -08002759 public void setTextColor(int viewId, @ColorInt int color) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002760 setInt(viewId, "setTextColor", color);
2761 }
2762
Joe Onorato592d0652009-03-24 22:25:52 -07002763 /**
Adam Cohen3b4ca102012-12-14 12:00:41 -08002764 * Equivalent to calling {@link android.widget.AbsListView#setRemoteViewsAdapter(Intent)}.
Winson Chung499cb9f2010-07-16 11:18:17 -07002765 *
Winson Chung037300b2011-03-29 15:40:16 -07002766 * @param appWidgetId The id of the app widget which contains the specified view. (This
2767 * parameter is ignored in this deprecated method)
Adam Cohen50f3d1b2012-12-11 18:36:07 -08002768 * @param viewId The id of the {@link AdapterView}
Winson Chung037300b2011-03-29 15:40:16 -07002769 * @param intent The intent of the service which will be
2770 * providing data to the RemoteViewsAdapter
2771 * @deprecated This method has been deprecated. See
2772 * {@link android.widget.RemoteViews#setRemoteAdapter(int, Intent)}
2773 */
2774 @Deprecated
2775 public void setRemoteAdapter(int appWidgetId, int viewId, Intent intent) {
2776 setRemoteAdapter(viewId, intent);
2777 }
2778
2779 /**
2780 * Equivalent to calling {@link android.widget.AbsListView#setRemoteViewsAdapter(Intent)}.
2781 * Can only be used for App Widgets.
2782 *
Adam Cohen50f3d1b2012-12-11 18:36:07 -08002783 * @param viewId The id of the {@link AdapterView}
Winson Chung81f39eb2011-01-11 18:05:01 -08002784 * @param intent The intent of the service which will be
2785 * providing data to the RemoteViewsAdapter
2786 */
Winson Chung037300b2011-03-29 15:40:16 -07002787 public void setRemoteAdapter(int viewId, Intent intent) {
2788 addAction(new SetRemoteViewsAdapterIntent(viewId, intent));
Winson Chung499cb9f2010-07-16 11:18:17 -07002789 }
2790
2791 /**
Adam Cohen50f3d1b2012-12-11 18:36:07 -08002792 * Creates a simple Adapter for the viewId specified. The viewId must point to an AdapterView,
2793 * ie. {@link ListView}, {@link GridView}, {@link StackView} or {@link AdapterViewAnimator}.
2794 * This is a simpler but less flexible approach to populating collection widgets. Its use is
2795 * encouraged for most scenarios, as long as the total memory within the list of RemoteViews
2796 * is relatively small (ie. doesn't contain large or numerous Bitmaps, see {@link
2797 * RemoteViews#setImageViewBitmap}). In the case of numerous images, the use of API is still
2798 * possible by setting image URIs instead of Bitmaps, see {@link RemoteViews#setImageViewUri}.
2799 *
2800 * This API is supported in the compatibility library for previous API levels, see
2801 * RemoteViewsCompat.
2802 *
2803 * @param viewId The id of the {@link AdapterView}
2804 * @param list The list of RemoteViews which will populate the view specified by viewId.
Adam Cohenb00d9f02013-01-10 14:12:52 -08002805 * @param viewTypeCount The maximum number of unique layout id's used to construct the list of
2806 * RemoteViews. This count cannot change during the life-cycle of a given widget, so this
2807 * parameter should account for the maximum possible number of types that may appear in the
2808 * See {@link Adapter#getViewTypeCount()}.
Adam Cohen33f3aab2013-04-17 13:48:17 -07002809 *
2810 * @hide
Adam Cohen50f3d1b2012-12-11 18:36:07 -08002811 */
Adam Cohenb00d9f02013-01-10 14:12:52 -08002812 public void setRemoteAdapter(int viewId, ArrayList<RemoteViews> list, int viewTypeCount) {
2813 addAction(new SetRemoteViewsAdapterList(viewId, list, viewTypeCount));
Adam Cohen50f3d1b2012-12-11 18:36:07 -08002814 }
2815
2816 /**
Winson Chung499cb9f2010-07-16 11:18:17 -07002817 * Equivalent to calling {@link android.widget.AbsListView#smoothScrollToPosition(int, int)}.
2818 *
Scott Main93dc6422012-02-24 12:04:06 -08002819 * @param viewId The id of the view to change
Winson Chung499cb9f2010-07-16 11:18:17 -07002820 * @param position Scroll to this adapter position
2821 */
2822 public void setScrollPosition(int viewId, int position) {
2823 setInt(viewId, "smoothScrollToPosition", position);
2824 }
2825
2826 /**
2827 * Equivalent to calling {@link android.widget.AbsListView#smoothScrollToPosition(int, int)}.
2828 *
Scott Main93dc6422012-02-24 12:04:06 -08002829 * @param viewId The id of the view to change
Winson Chung95362592010-07-19 16:05:50 -07002830 * @param offset Scroll by this adapter position offset
Winson Chung499cb9f2010-07-16 11:18:17 -07002831 */
2832 public void setRelativeScrollPosition(int viewId, int offset) {
2833 setInt(viewId, "smoothScrollByOffset", offset);
2834 }
2835
2836 /**
Daniel Sandlerd5353b42012-06-21 09:28:07 -04002837 * Equivalent to calling {@link android.view.View#setPadding(int, int, int, int)}.
Daniel Sandler99d1f742012-05-21 16:14:14 -04002838 *
2839 * @param viewId The id of the view to change
2840 * @param left the left padding in pixels
2841 * @param top the top padding in pixels
2842 * @param right the right padding in pixels
2843 * @param bottom the bottom padding in pixels
2844 */
2845 public void setViewPadding(int viewId, int left, int top, int right, int bottom) {
2846 addAction(new ViewPaddingAction(viewId, left, top, right, bottom));
2847 }
2848
2849 /**
Selim Cinek65b2e7c2015-10-26 14:11:31 -07002850 * @hide
2851 * Equivalent to calling {@link android.view.ViewGroup.MarginLayoutParams#setMarginEnd(int)}.
2852 * Only works if the {@link View#getLayoutParams()} supports margins.
2853 * Hidden for now since we don't want to support this for all different layout margins yet.
2854 *
2855 * @param viewId The id of the view to change
2856 * @param endMargin the left padding in pixels
2857 */
2858 public void setViewLayoutMarginEnd(int viewId, int endMargin) {
Adrian Roos9b123cf2016-02-04 14:55:57 -08002859 addAction(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_MARGIN_END, endMargin));
2860 }
2861
2862 /**
2863 * Equivalent to setting {@link android.view.ViewGroup.LayoutParams#width}.
2864 * @hide
2865 */
2866 public void setViewLayoutWidth(int viewId, int layoutWidth) {
2867 mActions.add(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_WIDTH, layoutWidth));
Selim Cinek65b2e7c2015-10-26 14:11:31 -07002868 }
2869
2870 /**
Joe Onorato592d0652009-03-24 22:25:52 -07002871 * Call a method taking one boolean 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 setBoolean(int viewId, String methodName, boolean value) {
2878 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.BOOLEAN, value));
2879 }
2880
Joe Onorato592d0652009-03-24 22:25:52 -07002881 /**
2882 * Call a method taking one byte 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 setByte(int viewId, String methodName, byte value) {
2889 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.BYTE, value));
2890 }
2891
Joe Onorato592d0652009-03-24 22:25:52 -07002892 /**
2893 * Call a method taking one short 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 setShort(int viewId, String methodName, short value) {
2900 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.SHORT, value));
2901 }
2902
Joe Onorato592d0652009-03-24 22:25:52 -07002903 /**
2904 * Call a method taking one int 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 setInt(int viewId, String methodName, int value) {
2911 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.INT, value));
2912 }
2913
Joe Onorato592d0652009-03-24 22:25:52 -07002914 /**
2915 * Call a method taking one long 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 setLong(int viewId, String methodName, long value) {
2922 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.LONG, value));
2923 }
2924
Joe Onorato592d0652009-03-24 22:25:52 -07002925 /**
2926 * Call a method taking one float 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 setFloat(int viewId, String methodName, float value) {
2933 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.FLOAT, value));
2934 }
2935
Joe Onorato592d0652009-03-24 22:25:52 -07002936 /**
2937 * Call a method taking one double 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 setDouble(int viewId, String methodName, double value) {
2944 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.DOUBLE, value));
2945 }
2946
Joe Onorato592d0652009-03-24 22:25:52 -07002947 /**
2948 * Call a method taking one char 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 setChar(int viewId, String methodName, char value) {
2955 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.CHAR, value));
2956 }
2957
Joe Onorato592d0652009-03-24 22:25:52 -07002958 /**
2959 * Call a method taking one String on a view in the layout for this RemoteViews.
2960 *
Scott Main93dc6422012-02-24 12:04:06 -08002961 * @param viewId The id of the view on which to call the method.
Joe Onorato592d0652009-03-24 22:25:52 -07002962 * @param methodName The name of the method to call.
2963 * @param value The value to pass to the method.
2964 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002965 public void setString(int viewId, String methodName, String value) {
2966 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.STRING, value));
2967 }
2968
Joe Onorato592d0652009-03-24 22:25:52 -07002969 /**
2970 * Call a method taking one CharSequence on a view in the layout for this RemoteViews.
2971 *
Scott Main93dc6422012-02-24 12:04:06 -08002972 * @param viewId The id of the view on which to call the method.
Joe Onorato592d0652009-03-24 22:25:52 -07002973 * @param methodName The name of the method to call.
2974 * @param value The value to pass to the method.
2975 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002976 public void setCharSequence(int viewId, String methodName, CharSequence value) {
2977 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.CHAR_SEQUENCE, value));
2978 }
2979
Joe Onorato592d0652009-03-24 22:25:52 -07002980 /**
2981 * Call a method taking one Uri on a view in the layout for this RemoteViews.
2982 *
Scott Main93dc6422012-02-24 12:04:06 -08002983 * @param viewId The id of the view on which to call the method.
Joe Onorato592d0652009-03-24 22:25:52 -07002984 * @param methodName The name of the method to call.
2985 * @param value The value to pass to the method.
2986 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002987 public void setUri(int viewId, String methodName, Uri value) {
Jeff Sharkeya14acd22013-04-02 18:27:45 -07002988 if (value != null) {
2989 // Resolve any filesystem path before sending remotely
2990 value = value.getCanonicalUri();
2991 if (StrictMode.vmFileUriExposureEnabled()) {
2992 value.checkFileUriExposed("RemoteViews.setUri()");
2993 }
2994 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002995 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.URI, value));
2996 }
2997
Joe Onorato592d0652009-03-24 22:25:52 -07002998 /**
2999 * Call a method taking one Bitmap on a view in the layout for this RemoteViews.
3000 * @more
3001 * <p class="note">The bitmap will be flattened into the parcel if this object is
3002 * sent across processes, so it may end up using a lot of memory, and may be fairly slow.</p>
3003 *
Scott Main93dc6422012-02-24 12:04:06 -08003004 * @param viewId The id of the view on which to call the method.
Joe Onorato592d0652009-03-24 22:25:52 -07003005 * @param methodName The name of the method to call.
3006 * @param value The value to pass to the method.
3007 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003008 public void setBitmap(int viewId, String methodName, Bitmap value) {
Adam Cohen5d200642012-04-24 10:43:31 -07003009 addAction(new BitmapReflectionAction(viewId, methodName, value));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003010 }
3011
3012 /**
Bjorn Bringertd755b062010-01-06 17:15:37 +00003013 * Call a method taking one Bundle on a view in the layout for this RemoteViews.
3014 *
Scott Main93dc6422012-02-24 12:04:06 -08003015 * @param viewId The id of the view on which to call the method.
Bjorn Bringertd755b062010-01-06 17:15:37 +00003016 * @param methodName The name of the method to call.
3017 * @param value The value to pass to the method.
3018 */
3019 public void setBundle(int viewId, String methodName, Bundle value) {
3020 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.BUNDLE, value));
3021 }
3022
3023 /**
Scott Main93dc6422012-02-24 12:04:06 -08003024 * Call a method taking one Intent on a view in the layout for this RemoteViews.
Winson Chung499cb9f2010-07-16 11:18:17 -07003025 *
Scott Main93dc6422012-02-24 12:04:06 -08003026 * @param viewId The id of the view on which to call the method.
3027 * @param methodName The name of the method to call.
3028 * @param value The {@link android.content.Intent} to pass the method.
Winson Chung499cb9f2010-07-16 11:18:17 -07003029 */
3030 public void setIntent(int viewId, String methodName, Intent value) {
3031 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.INTENT, value));
3032 }
3033
3034 /**
Dan Sandlera22a3802015-05-13 00:12:47 -04003035 * Call a method taking one Icon on a view in the layout for this RemoteViews.
3036 *
3037 * @param viewId The id of the view on which to call the method.
3038 * @param methodName The name of the method to call.
3039 * @param value The {@link android.graphics.drawable.Icon} to pass the method.
3040 */
3041 public void setIcon(int viewId, String methodName, Icon value) {
3042 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.ICON, value));
3043 }
3044
3045 /**
Svetoslav Ganov33aef982012-09-13 12:49:03 -07003046 * Equivalent to calling View.setContentDescription(CharSequence).
Svetoslav Ganove261e282011-10-18 17:47:04 -07003047 *
Svetoslav Ganov33aef982012-09-13 12:49:03 -07003048 * @param viewId The id of the view whose content description should change.
3049 * @param contentDescription The new content description for the view.
Svetoslav Ganove261e282011-10-18 17:47:04 -07003050 */
3051 public void setContentDescription(int viewId, CharSequence contentDescription) {
3052 setCharSequence(viewId, "setContentDescription", contentDescription);
3053 }
3054
Svetoslav Ganov33aef982012-09-13 12:49:03 -07003055 /**
Svetoslav6c702902014-10-09 18:40:56 -07003056 * Equivalent to calling {@link android.view.View#setAccessibilityTraversalBefore(int)}.
3057 *
3058 * @param viewId The id of the view whose before view in accessibility traversal to set.
3059 * @param nextId The id of the next in the accessibility traversal.
3060 **/
3061 public void setAccessibilityTraversalBefore(int viewId, int nextId) {
3062 setInt(viewId, "setAccessibilityTraversalBefore", nextId);
3063 }
3064
3065 /**
3066 * Equivalent to calling {@link android.view.View#setAccessibilityTraversalAfter(int)}.
3067 *
3068 * @param viewId The id of the view whose after view in accessibility traversal to set.
3069 * @param nextId The id of the next in the accessibility traversal.
3070 **/
3071 public void setAccessibilityTraversalAfter(int viewId, int nextId) {
3072 setInt(viewId, "setAccessibilityTraversalAfter", nextId);
3073 }
3074
3075 /**
Svetoslav Ganov33aef982012-09-13 12:49:03 -07003076 * Equivalent to calling View.setLabelFor(int).
3077 *
3078 * @param viewId The id of the view whose property to set.
3079 * @param labeledId The id of a view for which this view serves as a label.
3080 */
3081 public void setLabelFor(int viewId, int labeledId) {
3082 setInt(viewId, "setLabelFor", labeledId);
3083 }
3084
Adam Cohen5d200642012-04-24 10:43:31 -07003085 private RemoteViews getRemoteViewsToApply(Context context) {
3086 if (hasLandscapeAndPortraitLayouts()) {
3087 int orientation = context.getResources().getConfiguration().orientation;
3088 if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
3089 return mLandscape;
3090 } else {
3091 return mPortrait;
3092 }
3093 }
3094 return this;
3095 }
3096
Svetoslav Ganove261e282011-10-18 17:47:04 -07003097 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003098 * Inflates the view hierarchy represented by this object and applies
3099 * all of the actions.
Jim Millere667a7a2012-08-09 19:22:32 -07003100 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003101 * <p><strong>Caller beware: this may throw</strong>
Jim Millere667a7a2012-08-09 19:22:32 -07003102 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003103 * @param context Default context to use
3104 * @param parent Parent that the resulting view hierarchy will be attached to. This method
3105 * does <strong>not</strong> attach the hierarchy. The caller should do so when appropriate.
3106 * @return The inflated view hierarchy
3107 */
3108 public View apply(Context context, ViewGroup parent) {
Jim Millere667a7a2012-08-09 19:22:32 -07003109 return apply(context, parent, null);
Dianne Hackborn1927ae82012-06-22 15:21:36 -07003110 }
3111
Dianne Hackborna1940212012-06-28 16:07:22 -07003112 /** @hide */
3113 public View apply(Context context, ViewGroup parent, OnClickHandler handler) {
Adam Cohen5d200642012-04-24 10:43:31 -07003114 RemoteViews rvToApply = getRemoteViewsToApply(context);
3115
Sunny Goyaldd292f42015-12-02 14:29:27 -08003116 View result = inflateView(context, rvToApply, parent);
3117 loadTransitionOverride(context, handler);
3118
3119 rvToApply.performApply(result, parent, handler);
3120
3121 return result;
3122 }
3123
3124 private View inflateView(Context context, RemoteViews rv, ViewGroup parent) {
Kenny Guy77320062014-08-27 21:37:15 +01003125 // RemoteViews may be built by an application installed in another
3126 // user. So build a context that loads resources from that user but
3127 // still returns the current users userId so settings like data / time formats
3128 // are loaded without requiring cross user persmissions.
3129 final Context contextForResources = getContextForResources(context);
3130 Context inflationContext = new ContextWrapper(context) {
3131 @Override
3132 public Resources getResources() {
3133 return contextForResources.getResources();
3134 }
3135 @Override
3136 public Resources.Theme getTheme() {
3137 return contextForResources.getTheme();
3138 }
Dan Sandler706274f2015-07-30 22:32:54 -04003139 @Override
3140 public String getPackageName() {
3141 return contextForResources.getPackageName();
3142 }
Kenny Guy77320062014-08-27 21:37:15 +01003143 };
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003144
Romain Guya5475592009-07-01 17:20:08 -07003145 LayoutInflater inflater = (LayoutInflater)
Kenny Guy77320062014-08-27 21:37:15 +01003146 context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003147
Kenny Guy77320062014-08-27 21:37:15 +01003148 // Clone inflater so we load resources from correct context and
3149 // we don't add a filter to the static version returned by getSystemService.
3150 inflater = inflater.cloneInContext(inflationContext);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003151 inflater.setFilter(this);
Sunny Goyaldd292f42015-12-02 14:29:27 -08003152 return inflater.inflate(rv.getLayoutId(), parent, false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003153 }
Adam Cohen5d200642012-04-24 10:43:31 -07003154
Gus Prevas1ed322b2015-09-17 17:34:46 -04003155 private static void loadTransitionOverride(Context context,
3156 RemoteViews.OnClickHandler handler) {
3157 if (handler != null && context.getResources().getBoolean(
3158 com.android.internal.R.bool.config_overrideRemoteViewsActivityTransition)) {
3159 TypedArray windowStyle = context.getTheme().obtainStyledAttributes(
3160 com.android.internal.R.styleable.Window);
3161 int windowAnimations = windowStyle.getResourceId(
3162 com.android.internal.R.styleable.Window_windowAnimationStyle, 0);
3163 TypedArray windowAnimationStyle = context.obtainStyledAttributes(
3164 windowAnimations, com.android.internal.R.styleable.WindowAnimation);
3165 handler.setEnterAnimationId(windowAnimationStyle.getResourceId(
3166 com.android.internal.R.styleable.
3167 WindowAnimation_activityOpenRemoteViewsEnterAnimation, 0));
3168 windowStyle.recycle();
3169 windowAnimationStyle.recycle();
3170 }
3171 }
3172
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003173 /**
Sunny Goyaldd292f42015-12-02 14:29:27 -08003174 * Implement this interface to receive a callback when
3175 * {@link #applyAsync} or {@link #reapplyAsync} is finished.
3176 * @hide
3177 */
3178 public interface OnViewAppliedListener {
3179 void onViewApplied(View v);
3180
3181 void onError(Exception e);
3182 }
3183
3184 /**
3185 * Applies the views asynchronously, moving as much of the task on the background
3186 * thread as possible.
3187 *
3188 * @see {@link #apply(Context, ViewGroup)}
3189 * @param context Default context to use
3190 * @param parent Parent that the resulting view hierarchy will be attached to. This method
3191 * does <strong>not</strong> attach the hierarchy. The caller should do so when appropriate.
3192 * @param listener the callback to run when all actions have been applied. May be null.
3193 * @param executor The executor to use. If null {@link AsyncTask#THREAD_POOL_EXECUTOR} is used.
3194 * @return CancellationSignal
3195 * @hide
3196 */
3197 public CancellationSignal applyAsync(
3198 Context context, ViewGroup parent, Executor executor, OnViewAppliedListener listener) {
3199 return applyAsync(context, parent, executor, listener, null);
3200 }
3201
3202 private CancellationSignal startTaskOnExecutor(AsyncApplyTask task, Executor executor) {
3203 CancellationSignal cancelSignal = new CancellationSignal();
3204 cancelSignal.setOnCancelListener(task);
3205
3206 task.executeOnExecutor(executor == null ? AsyncTask.THREAD_POOL_EXECUTOR : executor);
3207 return cancelSignal;
3208 }
3209
3210 /** @hide */
3211 public CancellationSignal applyAsync(Context context, ViewGroup parent,
3212 Executor executor, OnViewAppliedListener listener, OnClickHandler handler) {
3213 return startTaskOnExecutor(getAsyncApplyTask(context, parent, listener, handler), executor);
3214 }
3215
3216 private AsyncApplyTask getAsyncApplyTask(Context context, ViewGroup parent,
3217 OnViewAppliedListener listener, OnClickHandler handler) {
3218 return new AsyncApplyTask(getRemoteViewsToApply(context), parent, context, listener,
3219 handler, null);
3220 }
3221
3222 private class AsyncApplyTask extends AsyncTask<Void, Void, ViewTree>
3223 implements CancellationSignal.OnCancelListener {
3224 final RemoteViews mRV;
3225 final ViewGroup mParent;
3226 final Context mContext;
3227 final OnViewAppliedListener mListener;
3228 final OnClickHandler mHandler;
3229
3230 private View mResult;
3231 private ViewTree mTree;
3232 private Action[] mActions;
3233 private Exception mError;
3234
3235 private AsyncApplyTask(
3236 RemoteViews rv, ViewGroup parent, Context context, OnViewAppliedListener listener,
3237 OnClickHandler handler, View result) {
3238 mRV = rv;
3239 mParent = parent;
3240 mContext = context;
3241 mListener = listener;
3242 mHandler = handler;
3243
3244 mResult = result;
3245 loadTransitionOverride(context, handler);
3246 }
3247
3248 @Override
3249 protected ViewTree doInBackground(Void... params) {
3250 try {
3251 if (mResult == null) {
3252 mResult = inflateView(mContext, mRV, mParent);
3253 }
3254
3255 mTree = new ViewTree(mResult);
3256 if (mRV.mActions != null) {
3257 int count = mRV.mActions.size();
3258 mActions = new Action[count];
3259 for (int i = 0; i < count && !isCancelled(); i++) {
3260 // TODO: check if isCanclled in nested views.
3261 mActions[i] = mRV.mActions.get(i).initActionAsync(mTree, mParent, mHandler);
3262 }
3263 } else {
3264 mActions = null;
3265 }
3266 return mTree;
3267 } catch (Exception e) {
3268 mError = e;
3269 return null;
3270 }
3271 }
3272
3273 @Override
3274 protected void onPostExecute(ViewTree viewTree) {
3275 if (mError == null) {
3276 try {
3277 if (mActions != null) {
3278 OnClickHandler handler = mHandler == null
3279 ? DEFAULT_ON_CLICK_HANDLER : mHandler;
3280 for (Action a : mActions) {
3281 a.apply(viewTree.mRoot, mParent, handler);
3282 }
3283 }
3284 } catch (Exception e) {
3285 mError = e;
3286 }
3287 }
3288
3289 if (mListener != null) {
3290 if (mError != null) {
3291 mListener.onError(mError);
3292 } else {
3293 mListener.onViewApplied(viewTree.mRoot);
3294 }
3295 } else if (mError != null) {
3296 if (mError instanceof ActionException) {
3297 throw (ActionException) mError;
3298 } else {
3299 throw new ActionException(mError);
3300 }
3301 }
3302 }
3303
3304 @Override
3305 public void onCancel() {
3306 cancel(true);
3307 }
3308 }
3309
3310 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003311 * Applies all of the actions to the provided view.
3312 *
3313 * <p><strong>Caller beware: this may throw</strong>
Jim Millere667a7a2012-08-09 19:22:32 -07003314 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003315 * @param v The view to apply the actions to. This should be the result of
3316 * the {@link #apply(Context,ViewGroup)} call.
3317 */
3318 public void reapply(Context context, View v) {
Jim Millere667a7a2012-08-09 19:22:32 -07003319 reapply(context, v, null);
Dianne Hackborna1940212012-06-28 16:07:22 -07003320 }
3321
3322 /** @hide */
3323 public void reapply(Context context, View v, OnClickHandler handler) {
Adam Cohen5d200642012-04-24 10:43:31 -07003324 RemoteViews rvToApply = getRemoteViewsToApply(context);
3325
3326 // In the case that a view has this RemoteViews applied in one orientation, is persisted
3327 // across orientation change, and has the RemoteViews re-applied in the new orientation,
3328 // we throw an exception, since the layouts may be completely unrelated.
3329 if (hasLandscapeAndPortraitLayouts()) {
3330 if (v.getId() != rvToApply.getLayoutId()) {
3331 throw new RuntimeException("Attempting to re-apply RemoteViews to a view that" +
3332 " that does not share the same root layout id.");
3333 }
3334 }
3335
Dianne Hackborna1940212012-06-28 16:07:22 -07003336 rvToApply.performApply(v, (ViewGroup) v.getParent(), handler);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003337 }
3338
Sunny Goyaldd292f42015-12-02 14:29:27 -08003339 /**
3340 * Applies all the actions to the provided view, moving as much of the task on the background
3341 * thread as possible.
3342 *
3343 * @see {@link #reapply(Context, View)}
3344 * @param context Default context to use
3345 * @param v The view to apply the actions to. This should be the result of
3346 * the {@link #apply(Context,ViewGroup)} call.
3347 * @param listener the callback to run when all actions have been applied. May be null.
3348 * @param executor The executor to use. If null {@link AsyncTask#THREAD_POOL_EXECUTOR} is used
3349 * @return CancellationSignal
3350 * @hide
3351 */
3352 public CancellationSignal reapplyAsync(
3353 Context context, View v, Executor executor, OnViewAppliedListener listener) {
3354 return reapplyAsync(context, v, executor, listener, null);
3355 }
3356
3357 /** @hide */
3358 public CancellationSignal reapplyAsync(Context context, View v, Executor executor,
3359 OnViewAppliedListener listener, OnClickHandler handler) {
3360 RemoteViews rvToApply = getRemoteViewsToApply(context);
3361
3362 // In the case that a view has this RemoteViews applied in one orientation, is persisted
3363 // across orientation change, and has the RemoteViews re-applied in the new orientation,
3364 // we throw an exception, since the layouts may be completely unrelated.
3365 if (hasLandscapeAndPortraitLayouts()) {
3366 if (v.getId() != rvToApply.getLayoutId()) {
3367 throw new RuntimeException("Attempting to re-apply RemoteViews to a view that" +
3368 " that does not share the same root layout id.");
3369 }
3370 }
3371
3372 return startTaskOnExecutor(new AsyncApplyTask(rvToApply, (ViewGroup) v.getParent(),
3373 context, listener, handler, v), executor);
3374 }
3375
Dianne Hackborna1940212012-06-28 16:07:22 -07003376 private void performApply(View v, ViewGroup parent, OnClickHandler handler) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003377 if (mActions != null) {
Jim Millere667a7a2012-08-09 19:22:32 -07003378 handler = handler == null ? DEFAULT_ON_CLICK_HANDLER : handler;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003379 final int count = mActions.size();
3380 for (int i = 0; i < count; i++) {
3381 Action a = mActions.get(i);
Dianne Hackborna1940212012-06-28 16:07:22 -07003382 a.apply(v, parent, handler);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003383 }
3384 }
3385 }
3386
Kenny Guy77320062014-08-27 21:37:15 +01003387 private Context getContextForResources(Context context) {
Svetoslav976e8bd2014-07-16 15:12:03 -07003388 if (mApplication != null) {
3389 if (context.getUserId() == UserHandle.getUserId(mApplication.uid)
3390 && context.getPackageName().equals(mApplication.packageName)) {
3391 return context;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003392 }
Svetoslav976e8bd2014-07-16 15:12:03 -07003393 try {
3394 return context.createApplicationContext(mApplication,
3395 Context.CONTEXT_RESTRICTED);
3396 } catch (NameNotFoundException e) {
Svet Ganov0da85b62014-08-06 14:11:37 -07003397 Log.e(LOG_TAG, "Package name " + mApplication.packageName + " not found");
Svetoslav976e8bd2014-07-16 15:12:03 -07003398 }
3399 }
3400
3401 return context;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003402 }
3403
Christoph Studer4600f9b2014-07-22 22:44:43 +02003404 /**
3405 * Returns the number of actions in this RemoteViews. Can be used as a sequence number.
3406 *
3407 * @hide
3408 */
3409 public int getSequenceNumber() {
3410 return (mActions == null) ? 0 : mActions.size();
3411 }
3412
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003413 /* (non-Javadoc)
3414 * Used to restrict the views which can be inflated
Jim Millere667a7a2012-08-09 19:22:32 -07003415 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003416 * @see android.view.LayoutInflater.Filter#onLoadClass(java.lang.Class)
3417 */
Gilles Debunnee6ac8b92010-06-17 10:55:04 -07003418 public boolean onLoadClass(Class clazz) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003419 return clazz.isAnnotationPresent(RemoteView.class);
3420 }
Adam Cohen5d200642012-04-24 10:43:31 -07003421
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003422 public int describeContents() {
3423 return 0;
3424 }
3425
3426 public void writeToParcel(Parcel dest, int flags) {
Adam Cohen5d200642012-04-24 10:43:31 -07003427 if (!hasLandscapeAndPortraitLayouts()) {
3428 dest.writeInt(MODE_NORMAL);
3429 // We only write the bitmap cache if we are the root RemoteViews, as this cache
3430 // is shared by all children.
3431 if (mIsRoot) {
3432 mBitmapCache.writeBitmapsToParcel(dest, flags);
3433 }
Svet Ganov0da85b62014-08-06 14:11:37 -07003434 dest.writeParcelable(mApplication, flags);
Adam Cohen5d200642012-04-24 10:43:31 -07003435 dest.writeInt(mLayoutId);
3436 dest.writeInt(mIsWidgetCollectionChild ? 1 : 0);
3437 int count;
3438 if (mActions != null) {
3439 count = mActions.size();
3440 } else {
3441 count = 0;
3442 }
3443 dest.writeInt(count);
3444 for (int i=0; i<count; i++) {
3445 Action a = mActions.get(i);
3446 a.writeToParcel(dest, 0);
3447 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003448 } else {
Adam Cohen5d200642012-04-24 10:43:31 -07003449 dest.writeInt(MODE_HAS_LANDSCAPE_AND_PORTRAIT);
3450 // We only write the bitmap cache if we are the root RemoteViews, as this cache
3451 // is shared by all children.
3452 if (mIsRoot) {
3453 mBitmapCache.writeBitmapsToParcel(dest, flags);
3454 }
3455 mLandscape.writeToParcel(dest, flags);
3456 mPortrait.writeToParcel(dest, flags);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003457 }
Svet Ganov0da85b62014-08-06 14:11:37 -07003458 }
Svetoslav976e8bd2014-07-16 15:12:03 -07003459
Svet Ganov0da85b62014-08-06 14:11:37 -07003460 private static ApplicationInfo getApplicationInfo(String packageName, int userId) {
Svetoslavb6242442014-09-19 13:21:55 -07003461 if (packageName == null) {
3462 return null;
3463 }
3464
Svet Ganov0da85b62014-08-06 14:11:37 -07003465 // Get the application for the passed in package and user.
3466 Application application = ActivityThread.currentApplication();
3467 if (application == null) {
3468 throw new IllegalStateException("Cannot create remote views out of an aplication.");
3469 }
3470
3471 ApplicationInfo applicationInfo = application.getApplicationInfo();
3472 if (UserHandle.getUserId(applicationInfo.uid) != userId
3473 || !applicationInfo.packageName.equals(packageName)) {
3474 try {
Svetoslav14494a82014-08-18 10:43:27 -07003475 Context context = application.getBaseContext().createPackageContextAsUser(
Svet Ganov0da85b62014-08-06 14:11:37 -07003476 packageName, 0, new UserHandle(userId));
3477 applicationInfo = context.getApplicationInfo();
3478 } catch (NameNotFoundException nnfe) {
3479 throw new IllegalArgumentException("No such package " + packageName);
3480 }
3481 }
3482
3483 return applicationInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003484 }
3485
3486 /**
3487 * Parcelable.Creator that instantiates RemoteViews objects
3488 */
3489 public static final Parcelable.Creator<RemoteViews> CREATOR = new Parcelable.Creator<RemoteViews>() {
3490 public RemoteViews createFromParcel(Parcel parcel) {
3491 return new RemoteViews(parcel);
3492 }
3493
3494 public RemoteViews[] newArray(int size) {
3495 return new RemoteViews[size];
3496 }
3497 };
Sunny Goyaldd292f42015-12-02 14:29:27 -08003498
3499 /**
3500 * A representation of the view hierarchy. Only views which have a valid ID are added
3501 * and can be searched.
3502 */
3503 private static class ViewTree {
3504 private final View mRoot;
3505
3506 private ArrayList<ViewTree> mChildren;
3507
3508 private ViewTree(View root) {
3509 mRoot = root;
3510 }
3511
3512 public void createTree() {
3513 if (mChildren != null) {
3514 return;
3515 }
3516
3517 mChildren = new ArrayList<>();
3518 if (mRoot instanceof ViewGroup && mRoot.isRootNamespace()) {
3519 ViewGroup vg = (ViewGroup) mRoot;
3520 int count = vg.getChildCount();
3521 for (int i = 0; i < count; i++) {
3522 addViewChild(vg.getChildAt(i));
3523 }
3524 }
3525 }
3526
3527 public ViewTree findViewTreeById(int id) {
3528 if (mRoot.getId() == id) {
3529 return this;
3530 }
3531 if (mChildren == null) {
3532 return null;
3533 }
3534 for (ViewTree tree : mChildren) {
3535 ViewTree result = tree.findViewTreeById(id);
3536 if (result != null) {
3537 return result;
3538 }
3539 }
3540 return null;
3541 }
3542
3543 public View findViewById(int id) {
3544 if (mChildren == null) {
3545 return mRoot.findViewById(id);
3546 }
3547 ViewTree tree = findViewTreeById(id);
3548 return tree == null ? null : tree.mRoot;
3549 }
3550
3551 public void addChild(ViewTree child) {
3552 if (mChildren == null) {
3553 mChildren = new ArrayList<>();
3554 }
3555 child.createTree();
3556 mChildren.add(child);
3557 }
3558
3559 private void addViewChild(View v) {
3560 final ViewTree target;
3561
3562 // If the view has a valid id, i.e., if can be found using findViewById, add it to the
3563 // tree, otherwise skip this view and add its children instead.
3564 if (v.getId() != 0) {
3565 ViewTree tree = new ViewTree(v);
3566 mChildren.add(tree);
3567 target = tree;
3568 } else {
3569 target = this;
3570 }
3571
3572 if (v instanceof ViewGroup && v.isRootNamespace()) {
3573 if (target.mChildren == null) {
3574 target.mChildren = new ArrayList<>();
3575 ViewGroup vg = (ViewGroup) v;
3576 int count = vg.getChildCount();
3577 for (int i = 0; i < count; i++) {
3578 target.addViewChild(vg.getChildAt(i));
3579 }
3580 }
3581 }
3582 }
3583 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003584}