blob: 6d2cea6b5c3c90f4ea3efcbaa91d2d1e45f40c5f [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;
Jorim Jaggie6e108e2016-03-28 13:38:45 -070020import android.app.ActivityManager.StackId;
Winson Chungdc6f79b2012-04-17 17:27:31 -070021import android.app.ActivityOptions;
Svetoslav976e8bd2014-07-16 15:12:03 -070022import android.app.ActivityThread;
Svet Ganov0da85b62014-08-06 14:11:37 -070023import android.app.Application;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080024import android.app.PendingIntent;
Adrian Roosfe84e1f2015-11-04 15:55:39 -080025import android.app.RemoteInput;
Adam Cohen1480fdd2010-08-25 17:24:53 -070026import android.appwidget.AppWidgetHostView;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080027import android.content.Context;
Kenny Guy77320062014-08-27 21:37:15 +010028import android.content.ContextWrapper;
Dianne Hackbornfa82f222009-09-17 15:14:12 -070029import android.content.Intent;
30import android.content.IntentSender;
Adam Cohenffc46a52012-04-30 19:54:39 -070031import android.content.pm.ApplicationInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080032import android.content.pm.PackageManager.NameNotFoundException;
Jorim Jaggief72a192014-08-26 21:57:46 +020033import android.content.res.ColorStateList;
Adam Cohen5d200642012-04-24 10:43:31 -070034import android.content.res.Configuration;
Kenny Guy77320062014-08-27 21:37:15 +010035import android.content.res.Resources;
Gus Prevas1ed322b2015-09-17 17:34:46 -040036import android.content.res.TypedArray;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080037import android.graphics.Bitmap;
38import android.graphics.PorterDuff;
Joe Onorato75970652009-12-02 23:04:55 -080039import android.graphics.Rect;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080040import android.graphics.drawable.Drawable;
Dan Sandlera22a3802015-05-13 00:12:47 -040041import android.graphics.drawable.Icon;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080042import android.net.Uri;
Sunny Goyaldd292f42015-12-02 14:29:27 -080043import android.os.AsyncTask;
Adam Cohenffc46a52012-04-30 19:54:39 -070044import android.os.Build;
Bjorn Bringertd755b062010-01-06 17:15:37 +000045import android.os.Bundle;
Sunny Goyaldd292f42015-12-02 14:29:27 -080046import android.os.CancellationSignal;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080047import android.os.Parcel;
48import android.os.Parcelable;
Jeff Sharkeya14acd22013-04-02 18:27:45 -070049import android.os.StrictMode;
Jeff Sharkey6d515712012-09-20 16:06:08 -070050import android.os.UserHandle;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080051import android.text.TextUtils;
Romain Guye4d4e202013-07-22 13:02:02 -070052import android.util.ArrayMap;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080053import android.util.Log;
54import android.view.LayoutInflater;
Winson Chungdc6f79b2012-04-17 17:27:31 -070055import android.view.LayoutInflater.Filter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080056import android.view.RemotableViewMethod;
57import android.view.View;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080058import android.view.View.OnClickListener;
Winson Chungdc6f79b2012-04-17 17:27:31 -070059import android.view.ViewGroup;
Adam Cohena32edd42010-10-26 10:35:01 -070060import android.widget.AdapterView.OnItemClickListener;
Romain Guy484f4d62013-07-22 16:39:16 -070061import libcore.util.Objects;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080062
Gus Prevas1ed322b2015-09-17 17:34:46 -040063import com.android.internal.R;
Adrian Roos7da889d2016-03-16 18:38:58 -070064import com.android.internal.util.Preconditions;
Gus Prevas1ed322b2015-09-17 17:34:46 -040065
Winson Chungdc6f79b2012-04-17 17:27:31 -070066import java.lang.annotation.ElementType;
67import java.lang.annotation.Retention;
68import java.lang.annotation.RetentionPolicy;
69import java.lang.annotation.Target;
70import java.lang.reflect.Method;
71import java.util.ArrayList;
Adam Cohenfbe44b72012-09-19 20:36:23 -070072import java.util.HashMap;
Sunny Goyaldd292f42015-12-02 14:29:27 -080073import java.util.concurrent.Executor;
Winson Chungdc6f79b2012-04-17 17:27:31 -070074
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080075/**
76 * A class that describes a view hierarchy that can be displayed in
77 * another process. The hierarchy is inflated from a layout resource
78 * file, and this class provides some basic operations for modifying
79 * the content of the inflated hierarchy.
80 */
81public class RemoteViews implements Parcelable, Filter {
Jim Millere667a7a2012-08-09 19:22:32 -070082
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080083 private static final String LOG_TAG = "RemoteViews";
Jim Millere667a7a2012-08-09 19:22:32 -070084
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080085 /**
Winson Chung81f39eb2011-01-11 18:05:01 -080086 * The intent extra that contains the appWidgetId.
87 * @hide
88 */
89 static final String EXTRA_REMOTEADAPTER_APPWIDGET_ID = "remoteAdapterAppWidgetId";
90
91 /**
Svetoslav976e8bd2014-07-16 15:12:03 -070092 * Application that hosts the remote views.
93 *
94 * @hide
Jeff Sharkey6d515712012-09-20 16:06:08 -070095 */
Svetoslav976e8bd2014-07-16 15:12:03 -070096 private ApplicationInfo mApplication;
Jeff Sharkey6d515712012-09-20 16:06:08 -070097
98 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080099 * The resource ID of the layout file. (Added to the parcel)
100 */
Gilles Debunne30301932010-06-16 18:32:00 -0700101 private final int mLayoutId;
Romain Guya5475592009-07-01 17:20:08 -0700102
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800103 /**
104 * An array of actions to perform on the view tree once it has been
105 * inflated
106 */
107 private ArrayList<Action> mActions;
Jim Millere667a7a2012-08-09 19:22:32 -0700108
Winson Chung3ec9a452010-09-23 16:40:28 -0700109 /**
110 * A class to keep track of memory usage by this RemoteViews
111 */
112 private MemoryUsageCounter mMemoryUsageCounter;
113
Adam Cohen5d200642012-04-24 10:43:31 -0700114 /**
115 * Maps bitmaps to unique indicies to avoid Bitmap duplication.
116 */
117 private BitmapCache mBitmapCache;
118
119 /**
120 * Indicates whether or not this RemoteViews object is contained as a child of any other
121 * RemoteViews.
122 */
123 private boolean mIsRoot = true;
124
125 /**
126 * Constants to whether or not this RemoteViews is composed of a landscape and portrait
127 * RemoteViews.
128 */
129 private static final int MODE_NORMAL = 0;
130 private static final int MODE_HAS_LANDSCAPE_AND_PORTRAIT = 1;
131
132 /**
133 * Used in conjunction with the special constructor
134 * {@link #RemoteViews(RemoteViews, RemoteViews)} to keep track of the landscape and portrait
135 * RemoteViews.
136 */
137 private RemoteViews mLandscape = null;
138 private RemoteViews mPortrait = null;
139
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800140 /**
Adam Cohenca6fd842010-09-03 18:10:35 -0700141 * This flag indicates whether this RemoteViews object is being created from a
142 * RemoteViewsService for use as a child of a widget collection. This flag is used
143 * to determine whether or not certain features are available, in particular,
144 * setting on click extras and setting on click pending intents. The former is enabled,
145 * and the latter disabled when this flag is true.
146 */
Dianne Hackborn1927ae82012-06-22 15:21:36 -0700147 private boolean mIsWidgetCollectionChild = false;
148
149 private static final OnClickHandler DEFAULT_ON_CLICK_HANDLER = new OnClickHandler();
Adam Cohenca6fd842010-09-03 18:10:35 -0700150
Romain Guy484f4d62013-07-22 16:39:16 -0700151 private static final Object[] sMethodsLock = new Object[0];
152 private static final ArrayMap<Class<? extends View>, ArrayMap<MutablePair<String, Class<?>>, Method>> sMethods =
153 new ArrayMap<Class<? extends View>, ArrayMap<MutablePair<String, Class<?>>, Method>>();
Sunny Goyaldd292f42015-12-02 14:29:27 -0800154 private static final ArrayMap<Method, Method> sAsyncMethods = new ArrayMap<>();
155
Romain Guye4d4e202013-07-22 13:02:02 -0700156 private static final ThreadLocal<Object[]> sInvokeArgsTls = new ThreadLocal<Object[]>() {
157 @Override
158 protected Object[] initialValue() {
159 return new Object[1];
160 }
161 };
162
Adam Cohenca6fd842010-09-03 18:10:35 -0700163 /**
Adrian Roosfe84e1f2015-11-04 15:55:39 -0800164 * @hide
165 */
166 public void setRemoteInputs(int viewId, RemoteInput[] remoteInputs) {
167 mActions.add(new SetRemoteInputsAction(viewId, remoteInputs));
168 }
169
170 /**
Romain Guy484f4d62013-07-22 16:39:16 -0700171 * Handle with care!
172 */
173 static class MutablePair<F, S> {
174 F first;
175 S second;
176
177 MutablePair(F first, S second) {
178 this.first = first;
179 this.second = second;
180 }
181
182 @Override
183 public boolean equals(Object o) {
184 if (!(o instanceof MutablePair)) {
185 return false;
186 }
187 MutablePair<?, ?> p = (MutablePair<?, ?>) o;
188 return Objects.equal(p.first, first) && Objects.equal(p.second, second);
189 }
190
191 @Override
192 public int hashCode() {
193 return (first == null ? 0 : first.hashCode()) ^ (second == null ? 0 : second.hashCode());
194 }
195 }
196
197 /**
198 * This pair is used to perform lookups in sMethods without causing allocations.
199 */
200 private final MutablePair<String, Class<?>> mPair =
201 new MutablePair<String, Class<?>>(null, null);
202
203 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800204 * This annotation indicates that a subclass of View is alllowed to be used
Romain Guya5475592009-07-01 17:20:08 -0700205 * with the {@link RemoteViews} mechanism.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800206 */
207 @Target({ ElementType.TYPE })
208 @Retention(RetentionPolicy.RUNTIME)
209 public @interface RemoteView {
210 }
211
212 /**
213 * Exception to send when something goes wrong executing an action
214 *
215 */
216 public static class ActionException extends RuntimeException {
217 public ActionException(Exception ex) {
218 super(ex);
219 }
220 public ActionException(String message) {
221 super(message);
222 }
223 }
Adam Cohena32edd42010-10-26 10:35:01 -0700224
Dianne Hackborn1927ae82012-06-22 15:21:36 -0700225 /** @hide */
226 public static class OnClickHandler {
Gus Prevas1ed322b2015-09-17 17:34:46 -0400227
228 private int mEnterAnimationId;
229
Dianne Hackborn1927ae82012-06-22 15:21:36 -0700230 public boolean onClickHandler(View view, PendingIntent pendingIntent,
Winson Chungae615982010-11-01 12:14:49 -0700231 Intent fillInIntent) {
Jorim Jaggie6e108e2016-03-28 13:38:45 -0700232 return onClickHandler(view, pendingIntent, fillInIntent, StackId.INVALID_STACK_ID);
233 }
234
235 public boolean onClickHandler(View view, PendingIntent pendingIntent,
236 Intent fillInIntent, int launchStackId) {
Winson Chungae615982010-11-01 12:14:49 -0700237 try {
238 // TODO: Unregister this handler if PendingIntent.FLAG_ONE_SHOT?
Winson Chungdc6f79b2012-04-17 17:27:31 -0700239 Context context = view.getContext();
Gus Prevas1ed322b2015-09-17 17:34:46 -0400240 ActivityOptions opts;
241 if (mEnterAnimationId != 0) {
242 opts = ActivityOptions.makeCustomAnimation(context, mEnterAnimationId, 0);
243 } else {
244 opts = ActivityOptions.makeScaleUpAnimation(view,
245 0, 0,
246 view.getMeasuredWidth(), view.getMeasuredHeight());
247 }
Jorim Jaggie6e108e2016-03-28 13:38:45 -0700248
249 if (launchStackId != StackId.INVALID_STACK_ID) {
250 opts.setLaunchStackId(launchStackId);
251 }
Winson Chungae615982010-11-01 12:14:49 -0700252 context.startIntentSender(
253 pendingIntent.getIntentSender(), fillInIntent,
254 Intent.FLAG_ACTIVITY_NEW_TASK,
Winson Chungdc6f79b2012-04-17 17:27:31 -0700255 Intent.FLAG_ACTIVITY_NEW_TASK, 0, opts.toBundle());
Winson Chungae615982010-11-01 12:14:49 -0700256 } catch (IntentSender.SendIntentException e) {
257 android.util.Log.e(LOG_TAG, "Cannot send pending intent: ", e);
258 return false;
259 } catch (Exception e) {
260 android.util.Log.e(LOG_TAG, "Cannot send pending intent due to " +
261 "unknown exception: ", e);
262 return false;
263 }
264 return true;
265 }
Gus Prevas1ed322b2015-09-17 17:34:46 -0400266
267 public void setEnterAnimationId(int enterAnimationId) {
268 mEnterAnimationId = enterAnimationId;
269 }
Dianne Hackborn1927ae82012-06-22 15:21:36 -0700270 }
271
272 /**
273 * Base class for all actions that can be performed on an
274 * inflated view.
275 *
276 * SUBCLASSES MUST BE IMMUTABLE SO CLONE WORKS!!!!!
277 */
278 private abstract static class Action implements Parcelable {
Dianne Hackborna1940212012-06-28 16:07:22 -0700279 public abstract void apply(View root, ViewGroup rootParent,
280 OnClickHandler handler) throws ActionException;
Dianne Hackborn1927ae82012-06-22 15:21:36 -0700281
Adam Cohenfbe44b72012-09-19 20:36:23 -0700282 public static final int MERGE_REPLACE = 0;
283 public static final int MERGE_APPEND = 1;
284 public static final int MERGE_IGNORE = 2;
285
Dianne Hackborn1927ae82012-06-22 15:21:36 -0700286 public int describeContents() {
287 return 0;
288 }
289
290 /**
291 * Overridden by each class to report on it's own memory usage
292 */
293 public void updateMemoryUsageEstimate(MemoryUsageCounter counter) {
Romain Guye4d4e202013-07-22 13:02:02 -0700294 // We currently only calculate Bitmap memory usage, so by default,
295 // don't do anything here
Dianne Hackborn1927ae82012-06-22 15:21:36 -0700296 }
Adam Cohen5d200642012-04-24 10:43:31 -0700297
298 public void setBitmapCache(BitmapCache bitmapCache) {
299 // Do nothing
300 }
Adam Cohenfbe44b72012-09-19 20:36:23 -0700301
302 public int mergeBehavior() {
303 return MERGE_REPLACE;
304 }
305
306 public abstract String getActionName();
307
308 public String getUniqueKey() {
309 return (getActionName() + viewId);
310 }
311
Sunny Goyaldd292f42015-12-02 14:29:27 -0800312 /**
313 * This is called on the background thread. It should perform any non-ui computations
314 * and return the final action which will run on the UI thread.
315 * Override this if some of the tasks can be performed async.
316 */
317 public Action initActionAsync(ViewTree root, ViewGroup rootParent, OnClickHandler handler) {
318 return this;
319 }
320
Adam Cohenfbe44b72012-09-19 20:36:23 -0700321 int viewId;
322 }
323
Adam Cohenbd1e0072012-09-21 16:51:50 -0700324 /**
Sunny Goyaldd292f42015-12-02 14:29:27 -0800325 * Action class used during async inflation of RemoteViews. Subclasses are not parcelable.
326 */
327 private static abstract class RuntimeAction extends Action {
328 @Override
329 public final String getActionName() {
330 return "RuntimeAction";
331 }
332
333 @Override
334 public final void writeToParcel(Parcel dest, int flags) {
335 throw new UnsupportedOperationException();
336 }
337 }
338
339 // Constant used during async execution. It is not parcelable.
340 private static final Action ACTION_NOOP = new RuntimeAction() {
341 @Override
342 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) { }
343 };
344
345 /**
Adam Cohenbd1e0072012-09-21 16:51:50 -0700346 * Merges the passed RemoteViews actions with this RemoteViews actions according to
347 * action-specific merge rules.
Svetoslav976e8bd2014-07-16 15:12:03 -0700348 *
Adam Cohenbd1e0072012-09-21 16:51:50 -0700349 * @param newRv
Svetoslav976e8bd2014-07-16 15:12:03 -0700350 *
Adam Cohenbd1e0072012-09-21 16:51:50 -0700351 * @hide
352 */
Adam Cohenfbe44b72012-09-19 20:36:23 -0700353 public void mergeRemoteViews(RemoteViews newRv) {
Adam Cohen3ff2d862012-09-26 14:07:57 -0700354 if (newRv == null) return;
Adam Cohenfbe44b72012-09-19 20:36:23 -0700355 // We first copy the new RemoteViews, as the process of merging modifies the way the actions
356 // reference the bitmap cache. We don't want to modify the object as it may need to
357 // be merged and applied multiple times.
Adam Cohen3ff2d862012-09-26 14:07:57 -0700358 RemoteViews copy = newRv.clone();
Adam Cohenfbe44b72012-09-19 20:36:23 -0700359
360 HashMap<String, Action> map = new HashMap<String, Action>();
361 if (mActions == null) {
362 mActions = new ArrayList<Action>();
363 }
364
365 int count = mActions.size();
366 for (int i = 0; i < count; i++) {
367 Action a = mActions.get(i);
368 map.put(a.getUniqueKey(), a);
369 }
370
371 ArrayList<Action> newActions = copy.mActions;
372 if (newActions == null) return;
373 count = newActions.size();
374 for (int i = 0; i < count; i++) {
375 Action a = newActions.get(i);
376 String key = newActions.get(i).getUniqueKey();
Adam Cohen3ff2d862012-09-26 14:07:57 -0700377 int mergeBehavior = newActions.get(i).mergeBehavior();
Adam Cohenfbe44b72012-09-19 20:36:23 -0700378 if (map.containsKey(key) && mergeBehavior == Action.MERGE_REPLACE) {
379 mActions.remove(map.get(key));
380 map.remove(key);
381 }
382
383 // If the merge behavior is ignore, we don't bother keeping the extra action
384 if (mergeBehavior == Action.MERGE_REPLACE || mergeBehavior == Action.MERGE_APPEND) {
385 mActions.add(a);
386 }
387 }
388
389 // Because pruning can remove the need for bitmaps, we reconstruct the bitmap cache
390 mBitmapCache = new BitmapCache();
391 setBitmapCache(mBitmapCache);
Romain Guya5475592009-07-01 17:20:08 -0700392 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800393
Adam Cohen1480fdd2010-08-25 17:24:53 -0700394 private class SetEmptyView extends Action {
395 int viewId;
396 int emptyViewId;
397
398 public final static int TAG = 6;
399
400 SetEmptyView(int viewId, int emptyViewId) {
401 this.viewId = viewId;
402 this.emptyViewId = emptyViewId;
403 }
404
405 SetEmptyView(Parcel in) {
406 this.viewId = in.readInt();
407 this.emptyViewId = in.readInt();
408 }
409
410 public void writeToParcel(Parcel out, int flags) {
411 out.writeInt(TAG);
412 out.writeInt(this.viewId);
413 out.writeInt(this.emptyViewId);
414 }
415
416 @Override
Dianne Hackborna1940212012-06-28 16:07:22 -0700417 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
Adam Cohen1480fdd2010-08-25 17:24:53 -0700418 final View view = root.findViewById(viewId);
419 if (!(view instanceof AdapterView<?>)) return;
420
421 AdapterView<?> adapterView = (AdapterView<?>) view;
422
423 final View emptyView = root.findViewById(emptyViewId);
424 if (emptyView == null) return;
425
426 adapterView.setEmptyView(emptyView);
427 }
Adam Cohenfbe44b72012-09-19 20:36:23 -0700428
429 public String getActionName() {
430 return "SetEmptyView";
431 }
Adam Cohen1480fdd2010-08-25 17:24:53 -0700432 }
433
Adam Cohen35ae9ca2010-09-20 15:20:41 -0700434 private class SetOnClickFillInIntent extends Action {
435 public SetOnClickFillInIntent(int id, Intent fillInIntent) {
436 this.viewId = id;
437 this.fillInIntent = fillInIntent;
438 }
439
440 public SetOnClickFillInIntent(Parcel parcel) {
441 viewId = parcel.readInt();
442 fillInIntent = Intent.CREATOR.createFromParcel(parcel);
443 }
444
445 public void writeToParcel(Parcel dest, int flags) {
446 dest.writeInt(TAG);
447 dest.writeInt(viewId);
448 fillInIntent.writeToParcel(dest, 0 /* no flags */);
449 }
450
451 @Override
Dianne Hackborna1940212012-06-28 16:07:22 -0700452 public void apply(View root, ViewGroup rootParent, final OnClickHandler handler) {
Adam Cohen35ae9ca2010-09-20 15:20:41 -0700453 final View target = root.findViewById(viewId);
Joe Onorato2b69ce42010-10-31 11:35:41 -0700454 if (target == null) return;
Adam Cohen35ae9ca2010-09-20 15:20:41 -0700455
456 if (!mIsWidgetCollectionChild) {
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800457 Log.e(LOG_TAG, "The method setOnClickFillInIntent is available " +
Adam Cohen35ae9ca2010-09-20 15:20:41 -0700458 "only from RemoteViewsFactory (ie. on collection items).");
459 return;
460 }
Adam Cohena32edd42010-10-26 10:35:01 -0700461 if (target == root) {
462 target.setTagInternal(com.android.internal.R.id.fillInIntent, fillInIntent);
Romain Guye4d4e202013-07-22 13:02:02 -0700463 } else if (fillInIntent != null) {
Adam Cohen35ae9ca2010-09-20 15:20:41 -0700464 OnClickListener listener = new OnClickListener() {
465 public void onClick(View v) {
466 // Insure that this view is a child of an AdapterView
467 View parent = (View) v.getParent();
Sunny Goyalb880d162016-02-24 14:38:59 -0800468 // Break the for loop on the first encounter of:
469 // 1) an AdapterView,
470 // 2) an AppWidgetHostView that is not a RemoteViewsFrameLayout, or
471 // 3) a null parent.
472 // 2) and 3) are unexpected and catch the case where a child is not
473 // correctly parented in an AdapterView.
Adam Cohen85a08f12012-11-06 11:24:23 -0800474 while (parent != null && !(parent instanceof AdapterView<?>)
Sunny Goyalb880d162016-02-24 14:38:59 -0800475 && !((parent instanceof AppWidgetHostView) &&
476 !(parent instanceof RemoteViewsAdapter.RemoteViewsFrameLayout))) {
Adam Cohen35ae9ca2010-09-20 15:20:41 -0700477 parent = (View) parent.getParent();
478 }
479
Sunny Goyalb880d162016-02-24 14:38:59 -0800480 if (!(parent instanceof AdapterView<?>)) {
Adam Cohen35ae9ca2010-09-20 15:20:41 -0700481 // Somehow they've managed to get this far without having
482 // and AdapterView as a parent.
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800483 Log.e(LOG_TAG, "Collection item doesn't have AdapterView parent");
Adam Cohen35ae9ca2010-09-20 15:20:41 -0700484 return;
485 }
486
487 // Insure that a template pending intent has been set on an ancestor
488 if (!(parent.getTag() instanceof PendingIntent)) {
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800489 Log.e(LOG_TAG, "Attempting setOnClickFillInIntent without" +
Adam Cohena32edd42010-10-26 10:35:01 -0700490 " calling setPendingIntentTemplate on parent.");
Adam Cohen35ae9ca2010-09-20 15:20:41 -0700491 return;
492 }
493
494 PendingIntent pendingIntent = (PendingIntent) parent.getTag();
495
Romain Guye4d4e202013-07-22 13:02:02 -0700496 final Rect rect = getSourceBounds(v);
Adam Cohen35ae9ca2010-09-20 15:20:41 -0700497
498 fillInIntent.setSourceBounds(rect);
Dianne Hackborna1940212012-06-28 16:07:22 -0700499 handler.onClickHandler(v, pendingIntent, fillInIntent);
Adam Cohen35ae9ca2010-09-20 15:20:41 -0700500 }
501
502 };
503 target.setOnClickListener(listener);
504 }
505 }
506
Adam Cohenfbe44b72012-09-19 20:36:23 -0700507 public String getActionName() {
508 return "SetOnClickFillInIntent";
509 }
510
Adam Cohen35ae9ca2010-09-20 15:20:41 -0700511 Intent fillInIntent;
512
513 public final static int TAG = 9;
514 }
515
Adam Cohenca6fd842010-09-03 18:10:35 -0700516 private class SetPendingIntentTemplate extends Action {
517 public SetPendingIntentTemplate(int id, PendingIntent pendingIntentTemplate) {
518 this.viewId = id;
519 this.pendingIntentTemplate = pendingIntentTemplate;
520 }
521
522 public SetPendingIntentTemplate(Parcel parcel) {
523 viewId = parcel.readInt();
524 pendingIntentTemplate = PendingIntent.readPendingIntentOrNullFromParcel(parcel);
525 }
526
527 public void writeToParcel(Parcel dest, int flags) {
528 dest.writeInt(TAG);
529 dest.writeInt(viewId);
530 pendingIntentTemplate.writeToParcel(dest, 0 /* no flags */);
531 }
532
533 @Override
Dianne Hackborna1940212012-06-28 16:07:22 -0700534 public void apply(View root, ViewGroup rootParent, final OnClickHandler handler) {
Adam Cohenca6fd842010-09-03 18:10:35 -0700535 final View target = root.findViewById(viewId);
Joe Onorato2b69ce42010-10-31 11:35:41 -0700536 if (target == null) return;
Adam Cohenca6fd842010-09-03 18:10:35 -0700537
538 // If the view isn't an AdapterView, setting a PendingIntent template doesn't make sense
539 if (target instanceof AdapterView<?>) {
Adam Cohena32edd42010-10-26 10:35:01 -0700540 AdapterView<?> av = (AdapterView<?>) target;
Adam Cohenca6fd842010-09-03 18:10:35 -0700541 // The PendingIntent template is stored in the view's tag.
Adam Cohena32edd42010-10-26 10:35:01 -0700542 OnItemClickListener listener = new OnItemClickListener() {
543 public void onItemClick(AdapterView<?> parent, View view,
544 int position, long id) {
545 // The view should be a frame layout
546 if (view instanceof ViewGroup) {
547 ViewGroup vg = (ViewGroup) view;
548
549 // AdapterViews contain their children in a frame
550 // so we need to go one layer deeper here.
551 if (parent instanceof AdapterViewAnimator) {
552 vg = (ViewGroup) vg.getChildAt(0);
553 }
554 if (vg == null) return;
555
556 Intent fillInIntent = null;
557 int childCount = vg.getChildCount();
558 for (int i = 0; i < childCount; i++) {
559 Object tag = vg.getChildAt(i).getTag(com.android.internal.R.id.fillInIntent);
560 if (tag instanceof Intent) {
561 fillInIntent = (Intent) tag;
562 break;
563 }
564 }
565 if (fillInIntent == null) return;
566
Romain Guye4d4e202013-07-22 13:02:02 -0700567 final Rect rect = getSourceBounds(view);
Adam Cohena32edd42010-10-26 10:35:01 -0700568
569 final Intent intent = new Intent();
570 intent.setSourceBounds(rect);
Dianne Hackborna1940212012-06-28 16:07:22 -0700571 handler.onClickHandler(view, pendingIntentTemplate, fillInIntent);
Adam Cohena32edd42010-10-26 10:35:01 -0700572 }
573 }
574 };
575 av.setOnItemClickListener(listener);
576 av.setTag(pendingIntentTemplate);
Adam Cohenca6fd842010-09-03 18:10:35 -0700577 } else {
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800578 Log.e(LOG_TAG, "Cannot setPendingIntentTemplate on a view which is not" +
Adam Cohen35ae9ca2010-09-20 15:20:41 -0700579 "an AdapterView (id: " + viewId + ")");
Adam Cohenca6fd842010-09-03 18:10:35 -0700580 return;
581 }
582 }
583
Adam Cohenfbe44b72012-09-19 20:36:23 -0700584 public String getActionName() {
585 return "SetPendingIntentTemplate";
586 }
587
Adam Cohenca6fd842010-09-03 18:10:35 -0700588 PendingIntent pendingIntentTemplate;
589
590 public final static int TAG = 8;
591 }
592
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800593 private class SetRemoteViewsAdapterList extends Action {
Adam Cohenb00d9f02013-01-10 14:12:52 -0800594 public SetRemoteViewsAdapterList(int id, ArrayList<RemoteViews> list, int viewTypeCount) {
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800595 this.viewId = id;
596 this.list = list;
Adam Cohenb00d9f02013-01-10 14:12:52 -0800597 this.viewTypeCount = viewTypeCount;
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800598 }
599
600 public SetRemoteViewsAdapterList(Parcel parcel) {
601 viewId = parcel.readInt();
Adam Cohenb00d9f02013-01-10 14:12:52 -0800602 viewTypeCount = parcel.readInt();
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800603 int count = parcel.readInt();
604 list = new ArrayList<RemoteViews>();
605
606 for (int i = 0; i < count; i++) {
607 RemoteViews rv = RemoteViews.CREATOR.createFromParcel(parcel);
608 list.add(rv);
609 }
610 }
611
612 public void writeToParcel(Parcel dest, int flags) {
613 dest.writeInt(TAG);
614 dest.writeInt(viewId);
Adam Cohenb00d9f02013-01-10 14:12:52 -0800615 dest.writeInt(viewTypeCount);
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800616
617 if (list == null || list.size() == 0) {
618 dest.writeInt(0);
619 } else {
620 int count = list.size();
621 dest.writeInt(count);
622 for (int i = 0; i < count; i++) {
623 RemoteViews rv = list.get(i);
624 rv.writeToParcel(dest, flags);
625 }
626 }
627 }
628
629 @Override
630 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
631 final View target = root.findViewById(viewId);
632 if (target == null) return;
633
634 // Ensure that we are applying to an AppWidget root
635 if (!(rootParent instanceof AppWidgetHostView)) {
636 Log.e(LOG_TAG, "SetRemoteViewsAdapterIntent action can only be used for " +
637 "AppWidgets (root id: " + viewId + ")");
638 return;
639 }
640 // Ensure that we are calling setRemoteAdapter on an AdapterView that supports it
641 if (!(target instanceof AbsListView) && !(target instanceof AdapterViewAnimator)) {
642 Log.e(LOG_TAG, "Cannot setRemoteViewsAdapter on a view which is not " +
643 "an AbsListView or AdapterViewAnimator (id: " + viewId + ")");
644 return;
645 }
646
647 if (target instanceof AbsListView) {
648 AbsListView v = (AbsListView) target;
649 Adapter a = v.getAdapter();
Adam Cohenb00d9f02013-01-10 14:12:52 -0800650 if (a instanceof RemoteViewsListAdapter && viewTypeCount <= a.getViewTypeCount()) {
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800651 ((RemoteViewsListAdapter) a).setViewsList(list);
652 } else {
Adam Cohenb00d9f02013-01-10 14:12:52 -0800653 v.setAdapter(new RemoteViewsListAdapter(v.getContext(), list, viewTypeCount));
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800654 }
655 } else if (target instanceof AdapterViewAnimator) {
656 AdapterViewAnimator v = (AdapterViewAnimator) target;
657 Adapter a = v.getAdapter();
Adam Cohenb00d9f02013-01-10 14:12:52 -0800658 if (a instanceof RemoteViewsListAdapter && viewTypeCount <= a.getViewTypeCount()) {
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800659 ((RemoteViewsListAdapter) a).setViewsList(list);
660 } else {
Adam Cohenb00d9f02013-01-10 14:12:52 -0800661 v.setAdapter(new RemoteViewsListAdapter(v.getContext(), list, viewTypeCount));
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800662 }
663 }
664 }
665
666 public String getActionName() {
667 return "SetRemoteViewsAdapterList";
668 }
669
Adam Cohenb00d9f02013-01-10 14:12:52 -0800670 int viewTypeCount;
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800671 ArrayList<RemoteViews> list;
672 public final static int TAG = 15;
673 }
674
Winson Chung037300b2011-03-29 15:40:16 -0700675 private class SetRemoteViewsAdapterIntent extends Action {
676 public SetRemoteViewsAdapterIntent(int id, Intent intent) {
677 this.viewId = id;
678 this.intent = intent;
679 }
680
681 public SetRemoteViewsAdapterIntent(Parcel parcel) {
682 viewId = parcel.readInt();
683 intent = Intent.CREATOR.createFromParcel(parcel);
684 }
685
686 public void writeToParcel(Parcel dest, int flags) {
687 dest.writeInt(TAG);
688 dest.writeInt(viewId);
689 intent.writeToParcel(dest, flags);
690 }
691
692 @Override
Dianne Hackborna1940212012-06-28 16:07:22 -0700693 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
Winson Chung037300b2011-03-29 15:40:16 -0700694 final View target = root.findViewById(viewId);
695 if (target == null) return;
696
697 // Ensure that we are applying to an AppWidget root
698 if (!(rootParent instanceof AppWidgetHostView)) {
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800699 Log.e(LOG_TAG, "SetRemoteViewsAdapterIntent action can only be used for " +
Winson Chung037300b2011-03-29 15:40:16 -0700700 "AppWidgets (root id: " + viewId + ")");
701 return;
702 }
703 // Ensure that we are calling setRemoteAdapter on an AdapterView that supports it
704 if (!(target instanceof AbsListView) && !(target instanceof AdapterViewAnimator)) {
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800705 Log.e(LOG_TAG, "Cannot setRemoteViewsAdapter on a view which is not " +
Winson Chung037300b2011-03-29 15:40:16 -0700706 "an AbsListView or AdapterViewAnimator (id: " + viewId + ")");
707 return;
708 }
709
710 // Embed the AppWidget Id for use in RemoteViewsAdapter when connecting to the intent
711 // RemoteViewsService
712 AppWidgetHostView host = (AppWidgetHostView) rootParent;
713 intent.putExtra(EXTRA_REMOTEADAPTER_APPWIDGET_ID, host.getAppWidgetId());
714 if (target instanceof AbsListView) {
715 AbsListView v = (AbsListView) target;
716 v.setRemoteViewsAdapter(intent);
Adam Cohena6a4cbc2012-09-26 17:36:40 -0700717 v.setRemoteViewsOnClickHandler(handler);
Winson Chung037300b2011-03-29 15:40:16 -0700718 } else if (target instanceof AdapterViewAnimator) {
719 AdapterViewAnimator v = (AdapterViewAnimator) target;
720 v.setRemoteViewsAdapter(intent);
Adam Cohena6a4cbc2012-09-26 17:36:40 -0700721 v.setRemoteViewsOnClickHandler(handler);
Winson Chung037300b2011-03-29 15:40:16 -0700722 }
723 }
724
Adam Cohenfbe44b72012-09-19 20:36:23 -0700725 public String getActionName() {
726 return "SetRemoteViewsAdapterIntent";
727 }
728
Winson Chung037300b2011-03-29 15:40:16 -0700729 Intent intent;
730
731 public final static int TAG = 10;
732 }
733
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800734 /**
735 * Equivalent to calling
736 * {@link android.view.View#setOnClickListener(android.view.View.OnClickListener)}
737 * to launch the provided {@link PendingIntent}.
738 */
739 private class SetOnClickPendingIntent extends Action {
740 public SetOnClickPendingIntent(int id, PendingIntent pendingIntent) {
741 this.viewId = id;
742 this.pendingIntent = pendingIntent;
743 }
Adam Cohenca6fd842010-09-03 18:10:35 -0700744
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800745 public SetOnClickPendingIntent(Parcel parcel) {
746 viewId = parcel.readInt();
Adam Cohenc6151f22012-02-02 21:02:31 -0800747
748 // We check a flag to determine if the parcel contains a PendingIntent.
749 if (parcel.readInt() != 0) {
750 pendingIntent = PendingIntent.readPendingIntentOrNullFromParcel(parcel);
751 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800752 }
Adam Cohenca6fd842010-09-03 18:10:35 -0700753
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800754 public void writeToParcel(Parcel dest, int flags) {
755 dest.writeInt(TAG);
756 dest.writeInt(viewId);
Adam Cohenc6151f22012-02-02 21:02:31 -0800757
758 // We use a flag to indicate whether the parcel contains a valid object.
759 dest.writeInt(pendingIntent != null ? 1 : 0);
760 if (pendingIntent != null) {
761 pendingIntent.writeToParcel(dest, 0 /* no flags */);
762 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800763 }
Adam Cohenca6fd842010-09-03 18:10:35 -0700764
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800765 @Override
Dianne Hackborna1940212012-06-28 16:07:22 -0700766 public void apply(View root, ViewGroup rootParent, final OnClickHandler handler) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800767 final View target = root.findViewById(viewId);
Joe Onorato2b69ce42010-10-31 11:35:41 -0700768 if (target == null) return;
Adam Cohenca6fd842010-09-03 18:10:35 -0700769
770 // If the view is an AdapterView, setting a PendingIntent on click doesn't make much
771 // sense, do they mean to set a PendingIntent template for the AdapterView's children?
772 if (mIsWidgetCollectionChild) {
Adam Cohen50f3d1b2012-12-11 18:36:07 -0800773 Log.w(LOG_TAG, "Cannot setOnClickPendingIntent for collection item " +
Adam Cohenc6151f22012-02-02 21:02:31 -0800774 "(id: " + viewId + ")");
Adam Cohenffc46a52012-04-30 19:54:39 -0700775 ApplicationInfo appInfo = root.getContext().getApplicationInfo();
776
777 // We let this slide for HC and ICS so as to not break compatibility. It should have
778 // been disabled from the outset, but was left open by accident.
779 if (appInfo != null &&
780 appInfo.targetSdkVersion >= Build.VERSION_CODES.JELLY_BEAN) {
781 return;
782 }
Adam Cohenca6fd842010-09-03 18:10:35 -0700783 }
784
Romain Guye4d4e202013-07-22 13:02:02 -0700785 // If the pendingIntent is null, we clear the onClickListener
786 OnClickListener listener = null;
787 if (pendingIntent != null) {
788 listener = new OnClickListener() {
789 public void onClick(View v) {
790 // Find target view location in screen coordinates and
791 // fill into PendingIntent before sending.
792 final Rect rect = getSourceBounds(v);
Jim Millere667a7a2012-08-09 19:22:32 -0700793
Romain Guye4d4e202013-07-22 13:02:02 -0700794 final Intent intent = new Intent();
795 intent.setSourceBounds(rect);
796 handler.onClickHandler(v, pendingIntent, intent);
797 }
798 };
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800799 }
Romain Guye4d4e202013-07-22 13:02:02 -0700800 target.setOnClickListener(listener);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800801 }
Adam Cohenc6151f22012-02-02 21:02:31 -0800802
Adam Cohenfbe44b72012-09-19 20:36:23 -0700803 public String getActionName() {
804 return "SetOnClickPendingIntent";
805 }
806
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800807 PendingIntent pendingIntent;
808
809 public final static int TAG = 1;
810 }
811
Romain Guye4d4e202013-07-22 13:02:02 -0700812 private static Rect getSourceBounds(View v) {
813 final float appScale = v.getContext().getResources()
814 .getCompatibilityInfo().applicationScale;
815 final int[] pos = new int[2];
816 v.getLocationOnScreen(pos);
817
818 final Rect rect = new Rect();
819 rect.left = (int) (pos[0] * appScale + 0.5f);
820 rect.top = (int) (pos[1] * appScale + 0.5f);
821 rect.right = (int) ((pos[0] + v.getWidth()) * appScale + 0.5f);
822 rect.bottom = (int) ((pos[1] + v.getHeight()) * appScale + 0.5f);
823 return rect;
824 }
825
Romain Guy484f4d62013-07-22 16:39:16 -0700826 private Method getMethod(View view, String methodName, Class<?> paramType) {
Romain Guye4d4e202013-07-22 13:02:02 -0700827 Method method;
828 Class<? extends View> klass = view.getClass();
829
830 synchronized (sMethodsLock) {
Romain Guy484f4d62013-07-22 16:39:16 -0700831 ArrayMap<MutablePair<String, Class<?>>, Method> methods = sMethods.get(klass);
Romain Guye4d4e202013-07-22 13:02:02 -0700832 if (methods == null) {
Romain Guy484f4d62013-07-22 16:39:16 -0700833 methods = new ArrayMap<MutablePair<String, Class<?>>, Method>();
Romain Guye4d4e202013-07-22 13:02:02 -0700834 sMethods.put(klass, methods);
835 }
836
Romain Guy484f4d62013-07-22 16:39:16 -0700837 mPair.first = methodName;
838 mPair.second = paramType;
839
840 method = methods.get(mPair);
Romain Guye4d4e202013-07-22 13:02:02 -0700841 if (method == null) {
842 try {
Romain Guy9870e5c2013-07-23 13:09:51 -0700843 if (paramType == null) {
844 method = klass.getMethod(methodName);
845 } else {
846 method = klass.getMethod(methodName, paramType);
847 }
Romain Guye4d4e202013-07-22 13:02:02 -0700848 } catch (NoSuchMethodException ex) {
849 throw new ActionException("view: " + klass.getName() + " doesn't have method: "
850 + methodName + getParameters(paramType));
851 }
852
853 if (!method.isAnnotationPresent(RemotableViewMethod.class)) {
854 throw new ActionException("view: " + klass.getName()
855 + " can't use method with RemoteViews: "
856 + methodName + getParameters(paramType));
857 }
858
Romain Guy484f4d62013-07-22 16:39:16 -0700859 methods.put(new MutablePair<String, Class<?>>(methodName, paramType), method);
Romain Guye4d4e202013-07-22 13:02:02 -0700860 }
861 }
862
863 return method;
864 }
865
Sunny Goyaldd292f42015-12-02 14:29:27 -0800866 /**
867 * @return the async implementation of the provided method.
868 */
869 private Method getAsyncMethod(Method method) {
870 synchronized (sAsyncMethods) {
871 int valueIndex = sAsyncMethods.indexOfKey(method);
872 if (valueIndex >= 0) {
873 return sAsyncMethods.valueAt(valueIndex);
874 }
875
876 RemotableViewMethod annotation = method.getAnnotation(RemotableViewMethod.class);
877 Method asyncMethod = null;
878 if (!annotation.asyncImpl().isEmpty()) {
879 try {
880 asyncMethod = method.getDeclaringClass()
881 .getMethod(annotation.asyncImpl(), method.getParameterTypes());
882 if (!asyncMethod.getReturnType().equals(Runnable.class)) {
883 throw new ActionException("Async implementation for " + method.getName() +
884 " does not return a Runnable");
885 }
886 } catch (NoSuchMethodException ex) {
887 throw new ActionException("Async implementation declared but not defined for " +
888 method.getName());
889 }
890 }
891 sAsyncMethods.put(method, asyncMethod);
892 return asyncMethod;
893 }
894 }
895
Romain Guye4d4e202013-07-22 13:02:02 -0700896 private static String getParameters(Class<?> paramType) {
897 if (paramType == null) return "()";
898 return "(" + paramType + ")";
899 }
900
901 private static Object[] wrapArg(Object value) {
902 Object[] args = sInvokeArgsTls.get();
903 args[0] = value;
904 return args;
905 }
906
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800907 /**
908 * Equivalent to calling a combination of {@link Drawable#setAlpha(int)},
909 * {@link Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)},
910 * and/or {@link Drawable#setLevel(int)} on the {@link Drawable} of a given view.
911 * <p>
912 * These operations will be performed on the {@link Drawable} returned by the
913 * target {@link View#getBackground()} by default. If targetBackground is false,
914 * we assume the target is an {@link ImageView} and try applying the operations
915 * to {@link ImageView#getDrawable()}.
916 * <p>
917 * You can omit specific calls by marking their values with null or -1.
918 */
919 private class SetDrawableParameters extends Action {
920 public SetDrawableParameters(int id, boolean targetBackground, int alpha,
921 int colorFilter, PorterDuff.Mode mode, int level) {
922 this.viewId = id;
923 this.targetBackground = targetBackground;
924 this.alpha = alpha;
925 this.colorFilter = colorFilter;
926 this.filterMode = mode;
927 this.level = level;
928 }
Jim Millere667a7a2012-08-09 19:22:32 -0700929
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800930 public SetDrawableParameters(Parcel parcel) {
931 viewId = parcel.readInt();
932 targetBackground = parcel.readInt() != 0;
933 alpha = parcel.readInt();
934 colorFilter = parcel.readInt();
935 boolean hasMode = parcel.readInt() != 0;
936 if (hasMode) {
937 filterMode = PorterDuff.Mode.valueOf(parcel.readString());
938 } else {
939 filterMode = null;
940 }
941 level = parcel.readInt();
942 }
Jim Millere667a7a2012-08-09 19:22:32 -0700943
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800944 public void writeToParcel(Parcel dest, int flags) {
945 dest.writeInt(TAG);
946 dest.writeInt(viewId);
947 dest.writeInt(targetBackground ? 1 : 0);
948 dest.writeInt(alpha);
949 dest.writeInt(colorFilter);
950 if (filterMode != null) {
951 dest.writeInt(1);
952 dest.writeString(filterMode.toString());
953 } else {
954 dest.writeInt(0);
955 }
956 dest.writeInt(level);
957 }
Jim Millere667a7a2012-08-09 19:22:32 -0700958
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800959 @Override
Dianne Hackborna1940212012-06-28 16:07:22 -0700960 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800961 final View target = root.findViewById(viewId);
Joe Onorato2b69ce42010-10-31 11:35:41 -0700962 if (target == null) return;
Jim Millere667a7a2012-08-09 19:22:32 -0700963
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800964 // Pick the correct drawable to modify for this view
965 Drawable targetDrawable = null;
966 if (targetBackground) {
967 targetDrawable = target.getBackground();
968 } else if (target instanceof ImageView) {
969 ImageView imageView = (ImageView) target;
970 targetDrawable = imageView.getDrawable();
971 }
Jim Millere667a7a2012-08-09 19:22:32 -0700972
Romain Guya5475592009-07-01 17:20:08 -0700973 if (targetDrawable != null) {
974 // Perform modifications only if values are set correctly
975 if (alpha != -1) {
Jorim Jaggi03a7c4c2015-06-01 16:25:39 -0700976 targetDrawable.mutate().setAlpha(alpha);
Romain Guya5475592009-07-01 17:20:08 -0700977 }
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +0200978 if (filterMode != null) {
Jorim Jaggi03a7c4c2015-06-01 16:25:39 -0700979 targetDrawable.mutate().setColorFilter(colorFilter, filterMode);
Romain Guya5475592009-07-01 17:20:08 -0700980 }
981 if (level != -1) {
Jorim Jaggi03a7c4c2015-06-01 16:25:39 -0700982 targetDrawable.mutate().setLevel(level);
Romain Guya5475592009-07-01 17:20:08 -0700983 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800984 }
985 }
Winson Chung3ec9a452010-09-23 16:40:28 -0700986
Adam Cohenfbe44b72012-09-19 20:36:23 -0700987 public String getActionName() {
988 return "SetDrawableParameters";
989 }
990
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800991 boolean targetBackground;
992 int alpha;
993 int colorFilter;
994 PorterDuff.Mode filterMode;
995 int level;
996
997 public final static int TAG = 3;
998 }
Jim Millere667a7a2012-08-09 19:22:32 -0700999
Romain Guy484f4d62013-07-22 16:39:16 -07001000 private final class ReflectionActionWithoutParams extends Action {
1001 final String methodName;
Adam Cohen2dd21972010-08-15 18:20:04 -07001002
1003 public final static int TAG = 5;
1004
1005 ReflectionActionWithoutParams(int viewId, String methodName) {
1006 this.viewId = viewId;
1007 this.methodName = methodName;
1008 }
1009
1010 ReflectionActionWithoutParams(Parcel in) {
1011 this.viewId = in.readInt();
1012 this.methodName = in.readString();
1013 }
1014
1015 public void writeToParcel(Parcel out, int flags) {
1016 out.writeInt(TAG);
1017 out.writeInt(this.viewId);
1018 out.writeString(this.methodName);
1019 }
1020
1021 @Override
Dianne Hackborna1940212012-06-28 16:07:22 -07001022 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
Adam Cohen2dd21972010-08-15 18:20:04 -07001023 final View view = root.findViewById(viewId);
Joe Onorato2b69ce42010-10-31 11:35:41 -07001024 if (view == null) return;
Adam Cohen2dd21972010-08-15 18:20:04 -07001025
Adam Cohen2dd21972010-08-15 18:20:04 -07001026 try {
Romain Guye4d4e202013-07-22 13:02:02 -07001027 getMethod(view, this.methodName, null).invoke(view);
1028 } catch (ActionException e) {
1029 throw e;
Adam Cohen2dd21972010-08-15 18:20:04 -07001030 } catch (Exception ex) {
1031 throw new ActionException(ex);
1032 }
1033 }
Adam Cohenfbe44b72012-09-19 20:36:23 -07001034
1035 public int mergeBehavior() {
1036 // we don't need to build up showNext or showPrevious calls
1037 if (methodName.equals("showNext") || methodName.equals("showPrevious")) {
1038 return MERGE_IGNORE;
1039 } else {
1040 return MERGE_REPLACE;
1041 }
1042 }
1043
1044 public String getActionName() {
1045 return "ReflectionActionWithoutParams";
1046 }
Adam Cohen2dd21972010-08-15 18:20:04 -07001047 }
1048
Adam Cohen5d200642012-04-24 10:43:31 -07001049 private static class BitmapCache {
1050 ArrayList<Bitmap> mBitmaps;
1051
1052 public BitmapCache() {
1053 mBitmaps = new ArrayList<Bitmap>();
1054 }
1055
1056 public BitmapCache(Parcel source) {
1057 int count = source.readInt();
1058 mBitmaps = new ArrayList<Bitmap>();
1059 for (int i = 0; i < count; i++) {
1060 Bitmap b = Bitmap.CREATOR.createFromParcel(source);
1061 mBitmaps.add(b);
1062 }
1063 }
1064
1065 public int getBitmapId(Bitmap b) {
1066 if (b == null) {
1067 return -1;
1068 } else {
1069 if (mBitmaps.contains(b)) {
1070 return mBitmaps.indexOf(b);
1071 } else {
1072 mBitmaps.add(b);
1073 return (mBitmaps.size() - 1);
1074 }
1075 }
1076 }
1077
1078 public Bitmap getBitmapForId(int id) {
1079 if (id == -1 || id >= mBitmaps.size()) {
1080 return null;
1081 } else {
1082 return mBitmaps.get(id);
1083 }
1084 }
1085
1086 public void writeBitmapsToParcel(Parcel dest, int flags) {
1087 int count = mBitmaps.size();
1088 dest.writeInt(count);
1089 for (int i = 0; i < count; i++) {
1090 mBitmaps.get(i).writeToParcel(dest, flags);
1091 }
1092 }
1093
1094 public void assimilate(BitmapCache bitmapCache) {
1095 ArrayList<Bitmap> bitmapsToBeAdded = bitmapCache.mBitmaps;
1096 int count = bitmapsToBeAdded.size();
1097 for (int i = 0; i < count; i++) {
1098 Bitmap b = bitmapsToBeAdded.get(i);
1099 if (!mBitmaps.contains(b)) {
1100 mBitmaps.add(b);
1101 }
1102 }
1103 }
1104
1105 public void addBitmapMemory(MemoryUsageCounter memoryCounter) {
1106 for (int i = 0; i < mBitmaps.size(); i++) {
1107 memoryCounter.addBitmapMemory(mBitmaps.get(i));
1108 }
1109 }
Adrian Roos7da889d2016-03-16 18:38:58 -07001110
1111 @Override
1112 protected BitmapCache clone() {
1113 BitmapCache bitmapCache = new BitmapCache();
1114 bitmapCache.mBitmaps.addAll(mBitmaps);
1115 return bitmapCache;
1116 }
Adam Cohen5d200642012-04-24 10:43:31 -07001117 }
1118
1119 private class BitmapReflectionAction extends Action {
1120 int bitmapId;
Adam Cohen5d200642012-04-24 10:43:31 -07001121 Bitmap bitmap;
1122 String methodName;
1123
1124 BitmapReflectionAction(int viewId, String methodName, Bitmap bitmap) {
1125 this.bitmap = bitmap;
1126 this.viewId = viewId;
1127 this.methodName = methodName;
1128 bitmapId = mBitmapCache.getBitmapId(bitmap);
1129 }
1130
1131 BitmapReflectionAction(Parcel in) {
1132 viewId = in.readInt();
1133 methodName = in.readString();
1134 bitmapId = in.readInt();
1135 bitmap = mBitmapCache.getBitmapForId(bitmapId);
1136 }
1137
1138 @Override
1139 public void writeToParcel(Parcel dest, int flags) {
1140 dest.writeInt(TAG);
1141 dest.writeInt(viewId);
1142 dest.writeString(methodName);
1143 dest.writeInt(bitmapId);
1144 }
1145
1146 @Override
Dianne Hackborna1940212012-06-28 16:07:22 -07001147 public void apply(View root, ViewGroup rootParent,
1148 OnClickHandler handler) throws ActionException {
Adam Cohen5d200642012-04-24 10:43:31 -07001149 ReflectionAction ra = new ReflectionAction(viewId, methodName, ReflectionAction.BITMAP,
1150 bitmap);
Dianne Hackborna1940212012-06-28 16:07:22 -07001151 ra.apply(root, rootParent, handler);
Adam Cohen5d200642012-04-24 10:43:31 -07001152 }
1153
1154 @Override
1155 public void setBitmapCache(BitmapCache bitmapCache) {
1156 bitmapId = bitmapCache.getBitmapId(bitmap);
1157 }
1158
Adam Cohenfbe44b72012-09-19 20:36:23 -07001159 public String getActionName() {
1160 return "BitmapReflectionAction";
1161 }
1162
Adam Cohen5d200642012-04-24 10:43:31 -07001163 public final static int TAG = 12;
1164 }
1165
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001166 /**
1167 * Base class for the reflection actions.
1168 */
Romain Guy484f4d62013-07-22 16:39:16 -07001169 private final class ReflectionAction extends Action {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001170 static final int TAG = 2;
1171
1172 static final int BOOLEAN = 1;
1173 static final int BYTE = 2;
1174 static final int SHORT = 3;
1175 static final int INT = 4;
1176 static final int LONG = 5;
1177 static final int FLOAT = 6;
1178 static final int DOUBLE = 7;
1179 static final int CHAR = 8;
1180 static final int STRING = 9;
1181 static final int CHAR_SEQUENCE = 10;
1182 static final int URI = 11;
Adam Cohenfbe44b72012-09-19 20:36:23 -07001183 // BITMAP actions are never stored in the list of actions. They are only used locally
1184 // to implement BitmapReflectionAction, which eliminates duplicates using BitmapCache.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001185 static final int BITMAP = 12;
Bjorn Bringertd755b062010-01-06 17:15:37 +00001186 static final int BUNDLE = 13;
Winson Chung499cb9f2010-07-16 11:18:17 -07001187 static final int INTENT = 14;
Jorim Jaggief72a192014-08-26 21:57:46 +02001188 static final int COLOR_STATE_LIST = 15;
Dan Sandlera22a3802015-05-13 00:12:47 -04001189 static final int ICON = 16;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001190
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001191 String methodName;
1192 int type;
1193 Object value;
1194
1195 ReflectionAction(int viewId, String methodName, int type, Object value) {
1196 this.viewId = viewId;
1197 this.methodName = methodName;
1198 this.type = type;
1199 this.value = value;
1200 }
1201
1202 ReflectionAction(Parcel in) {
1203 this.viewId = in.readInt();
1204 this.methodName = in.readString();
1205 this.type = in.readInt();
Romain Guya5475592009-07-01 17:20:08 -07001206 //noinspection ConstantIfStatement
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001207 if (false) {
Adam Cohen50f3d1b2012-12-11 18:36:07 -08001208 Log.d(LOG_TAG, "read viewId=0x" + Integer.toHexString(this.viewId)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001209 + " methodName=" + this.methodName + " type=" + this.type);
1210 }
Adam Cohenc6151f22012-02-02 21:02:31 -08001211
1212 // For some values that may have been null, we first check a flag to see if they were
1213 // written to the parcel.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001214 switch (this.type) {
1215 case BOOLEAN:
1216 this.value = in.readInt() != 0;
1217 break;
1218 case BYTE:
1219 this.value = in.readByte();
1220 break;
1221 case SHORT:
1222 this.value = (short)in.readInt();
1223 break;
1224 case INT:
1225 this.value = in.readInt();
1226 break;
1227 case LONG:
1228 this.value = in.readLong();
1229 break;
1230 case FLOAT:
1231 this.value = in.readFloat();
1232 break;
1233 case DOUBLE:
1234 this.value = in.readDouble();
1235 break;
1236 case CHAR:
1237 this.value = (char)in.readInt();
1238 break;
1239 case STRING:
1240 this.value = in.readString();
1241 break;
1242 case CHAR_SEQUENCE:
1243 this.value = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
1244 break;
1245 case URI:
Adam Cohenc6151f22012-02-02 21:02:31 -08001246 if (in.readInt() != 0) {
1247 this.value = Uri.CREATOR.createFromParcel(in);
1248 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001249 break;
1250 case BITMAP:
Adam Cohenc6151f22012-02-02 21:02:31 -08001251 if (in.readInt() != 0) {
1252 this.value = Bitmap.CREATOR.createFromParcel(in);
1253 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001254 break;
Bjorn Bringertd755b062010-01-06 17:15:37 +00001255 case BUNDLE:
1256 this.value = in.readBundle();
1257 break;
Winson Chung499cb9f2010-07-16 11:18:17 -07001258 case INTENT:
Adam Cohenc6151f22012-02-02 21:02:31 -08001259 if (in.readInt() != 0) {
1260 this.value = Intent.CREATOR.createFromParcel(in);
1261 }
Winson Chung499cb9f2010-07-16 11:18:17 -07001262 break;
Jorim Jaggief72a192014-08-26 21:57:46 +02001263 case COLOR_STATE_LIST:
1264 if (in.readInt() != 0) {
1265 this.value = ColorStateList.CREATOR.createFromParcel(in);
1266 }
1267 break;
Dan Sandlera22a3802015-05-13 00:12:47 -04001268 case ICON:
1269 if (in.readInt() != 0) {
1270 this.value = Icon.CREATOR.createFromParcel(in);
1271 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001272 default:
1273 break;
1274 }
1275 }
1276
1277 public void writeToParcel(Parcel out, int flags) {
1278 out.writeInt(TAG);
1279 out.writeInt(this.viewId);
1280 out.writeString(this.methodName);
1281 out.writeInt(this.type);
Romain Guya5475592009-07-01 17:20:08 -07001282 //noinspection ConstantIfStatement
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001283 if (false) {
Adam Cohen50f3d1b2012-12-11 18:36:07 -08001284 Log.d(LOG_TAG, "write viewId=0x" + Integer.toHexString(this.viewId)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001285 + " methodName=" + this.methodName + " type=" + this.type);
1286 }
Adam Cohenc6151f22012-02-02 21:02:31 -08001287
1288 // For some values which are null, we record an integer flag to indicate whether
1289 // we have written a valid value to the parcel.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001290 switch (this.type) {
1291 case BOOLEAN:
Romain Guya5475592009-07-01 17:20:08 -07001292 out.writeInt((Boolean) this.value ? 1 : 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001293 break;
1294 case BYTE:
Romain Guya5475592009-07-01 17:20:08 -07001295 out.writeByte((Byte) this.value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001296 break;
1297 case SHORT:
Romain Guya5475592009-07-01 17:20:08 -07001298 out.writeInt((Short) this.value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001299 break;
1300 case INT:
Romain Guya5475592009-07-01 17:20:08 -07001301 out.writeInt((Integer) this.value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001302 break;
1303 case LONG:
Romain Guya5475592009-07-01 17:20:08 -07001304 out.writeLong((Long) this.value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001305 break;
1306 case FLOAT:
Romain Guya5475592009-07-01 17:20:08 -07001307 out.writeFloat((Float) this.value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001308 break;
1309 case DOUBLE:
Romain Guya5475592009-07-01 17:20:08 -07001310 out.writeDouble((Double) this.value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001311 break;
1312 case CHAR:
1313 out.writeInt((int)((Character)this.value).charValue());
1314 break;
1315 case STRING:
1316 out.writeString((String)this.value);
1317 break;
1318 case CHAR_SEQUENCE:
Jim Millere667a7a2012-08-09 19:22:32 -07001319 TextUtils.writeToParcel((CharSequence)this.value, out, flags);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001320 break;
1321 case URI:
Adam Cohenc6151f22012-02-02 21:02:31 -08001322 out.writeInt(this.value != null ? 1 : 0);
1323 if (this.value != null) {
1324 ((Uri)this.value).writeToParcel(out, flags);
1325 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001326 break;
1327 case BITMAP:
Adam Cohenc6151f22012-02-02 21:02:31 -08001328 out.writeInt(this.value != null ? 1 : 0);
1329 if (this.value != null) {
1330 ((Bitmap)this.value).writeToParcel(out, flags);
1331 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001332 break;
Bjorn Bringertd755b062010-01-06 17:15:37 +00001333 case BUNDLE:
1334 out.writeBundle((Bundle) this.value);
1335 break;
Winson Chung499cb9f2010-07-16 11:18:17 -07001336 case INTENT:
Adam Cohenc6151f22012-02-02 21:02:31 -08001337 out.writeInt(this.value != null ? 1 : 0);
1338 if (this.value != null) {
1339 ((Intent)this.value).writeToParcel(out, flags);
1340 }
Winson Chung499cb9f2010-07-16 11:18:17 -07001341 break;
Jorim Jaggief72a192014-08-26 21:57:46 +02001342 case COLOR_STATE_LIST:
1343 out.writeInt(this.value != null ? 1 : 0);
1344 if (this.value != null) {
1345 ((ColorStateList)this.value).writeToParcel(out, flags);
1346 }
Dan Sandlera22a3802015-05-13 00:12:47 -04001347 break;
1348 case ICON:
1349 out.writeInt(this.value != null ? 1 : 0);
1350 if (this.value != null) {
1351 ((Icon)this.value).writeToParcel(out, flags);
1352 }
1353 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001354 default:
1355 break;
1356 }
1357 }
1358
Romain Guye4d4e202013-07-22 13:02:02 -07001359 private Class<?> getParameterType() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001360 switch (this.type) {
1361 case BOOLEAN:
1362 return boolean.class;
1363 case BYTE:
1364 return byte.class;
1365 case SHORT:
1366 return short.class;
1367 case INT:
1368 return int.class;
1369 case LONG:
1370 return long.class;
1371 case FLOAT:
1372 return float.class;
1373 case DOUBLE:
1374 return double.class;
1375 case CHAR:
1376 return char.class;
1377 case STRING:
1378 return String.class;
1379 case CHAR_SEQUENCE:
1380 return CharSequence.class;
1381 case URI:
1382 return Uri.class;
1383 case BITMAP:
1384 return Bitmap.class;
Bjorn Bringertd755b062010-01-06 17:15:37 +00001385 case BUNDLE:
1386 return Bundle.class;
Winson Chung499cb9f2010-07-16 11:18:17 -07001387 case INTENT:
1388 return Intent.class;
Jorim Jaggief72a192014-08-26 21:57:46 +02001389 case COLOR_STATE_LIST:
1390 return ColorStateList.class;
Dan Sandlera22a3802015-05-13 00:12:47 -04001391 case ICON:
1392 return Icon.class;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001393 default:
1394 return null;
1395 }
1396 }
1397
1398 @Override
Dianne Hackborna1940212012-06-28 16:07:22 -07001399 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001400 final View view = root.findViewById(viewId);
Joe Onorato2b69ce42010-10-31 11:35:41 -07001401 if (view == null) return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001402
Romain Guye4d4e202013-07-22 13:02:02 -07001403 Class<?> param = getParameterType();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001404 if (param == null) {
1405 throw new ActionException("bad type: " + this.type);
1406 }
1407
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001408 try {
Romain Guye4d4e202013-07-22 13:02:02 -07001409 getMethod(view, this.methodName, param).invoke(view, wrapArg(this.value));
1410 } catch (ActionException e) {
1411 throw e;
1412 } catch (Exception ex) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001413 throw new ActionException(ex);
1414 }
1415 }
Winson Chung3ec9a452010-09-23 16:40:28 -07001416
Sunny Goyaldd292f42015-12-02 14:29:27 -08001417 @Override
1418 public Action initActionAsync(ViewTree root, ViewGroup rootParent, OnClickHandler handler) {
1419 final View view = root.findViewById(viewId);
1420 if (view == null) return ACTION_NOOP;
1421
1422 Class<?> param = getParameterType();
1423 if (param == null) {
1424 throw new ActionException("bad type: " + this.type);
1425 }
1426
1427 try {
1428 Method method = getMethod(view, this.methodName, param);
1429 Method asyncMethod = getAsyncMethod(method);
1430
1431 if (asyncMethod != null) {
1432 Runnable endAction = (Runnable) asyncMethod.invoke(view, wrapArg(this.value));
1433 if (endAction == null) {
1434 return ACTION_NOOP;
1435 } else {
1436 return new RunnableAction(endAction);
1437 }
1438 }
1439 } catch (ActionException e) {
1440 throw e;
1441 } catch (Exception ex) {
1442 throw new ActionException(ex);
1443 }
1444
1445 return this;
1446 }
1447
Adam Cohenfbe44b72012-09-19 20:36:23 -07001448 public int mergeBehavior() {
1449 // smoothScrollBy is cumulative, everything else overwites.
1450 if (methodName.equals("smoothScrollBy")) {
1451 return MERGE_APPEND;
1452 } else {
1453 return MERGE_REPLACE;
Winson Chung3ec9a452010-09-23 16:40:28 -07001454 }
1455 }
Adam Cohenfbe44b72012-09-19 20:36:23 -07001456
1457 public String getActionName() {
1458 // Each type of reflection action corresponds to a setter, so each should be seen as
1459 // unique from the standpoint of merging.
1460 return "ReflectionAction" + this.methodName + this.type;
1461 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001462 }
1463
Sunny Goyaldd292f42015-12-02 14:29:27 -08001464 /**
1465 * This is only used for async execution of actions and it not parcelable.
1466 */
1467 private static final class RunnableAction extends RuntimeAction {
1468 private final Runnable mRunnable;
1469
1470 RunnableAction(Runnable r) {
1471 mRunnable = r;
1472 }
1473
1474 @Override
1475 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
1476 mRunnable.run();
1477 }
1478 }
1479
Adam Cohen5d200642012-04-24 10:43:31 -07001480 private void configureRemoteViewsAsChild(RemoteViews rv) {
1481 mBitmapCache.assimilate(rv.mBitmapCache);
1482 rv.setBitmapCache(mBitmapCache);
1483 rv.setNotRoot();
1484 }
1485
1486 void setNotRoot() {
1487 mIsRoot = false;
1488 }
1489
Jeff Sharkey1162fd72009-11-04 17:58:08 -08001490 /**
1491 * Equivalent to calling {@link ViewGroup#addView(View)} after inflating the
1492 * given {@link RemoteViews}, or calling {@link ViewGroup#removeAllViews()}
1493 * when null. This allows users to build "nested" {@link RemoteViews}.
1494 */
1495 private class ViewGroupAction extends Action {
1496 public ViewGroupAction(int viewId, RemoteViews nestedViews) {
1497 this.viewId = viewId;
1498 this.nestedViews = nestedViews;
Adam Cohenc431c152012-04-26 18:42:17 -07001499 if (nestedViews != null) {
1500 configureRemoteViewsAsChild(nestedViews);
1501 }
Jeff Sharkey1162fd72009-11-04 17:58:08 -08001502 }
1503
Adam Cohen5d200642012-04-24 10:43:31 -07001504 public ViewGroupAction(Parcel parcel, BitmapCache bitmapCache) {
Jeff Sharkey1162fd72009-11-04 17:58:08 -08001505 viewId = parcel.readInt();
Adam Cohen5d200642012-04-24 10:43:31 -07001506 boolean nestedViewsNull = parcel.readInt() == 0;
1507 if (!nestedViewsNull) {
1508 nestedViews = new RemoteViews(parcel, bitmapCache);
1509 } else {
1510 nestedViews = null;
1511 }
Jeff Sharkey1162fd72009-11-04 17:58:08 -08001512 }
1513
1514 public void writeToParcel(Parcel dest, int flags) {
1515 dest.writeInt(TAG);
1516 dest.writeInt(viewId);
Adam Cohen5d200642012-04-24 10:43:31 -07001517 if (nestedViews != null) {
1518 dest.writeInt(1);
1519 nestedViews.writeToParcel(dest, flags);
1520 } else {
1521 // signifies null
1522 dest.writeInt(0);
1523 }
Jeff Sharkey1162fd72009-11-04 17:58:08 -08001524 }
1525
1526 @Override
Dianne Hackborna1940212012-06-28 16:07:22 -07001527 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
Jeff Sharkey1162fd72009-11-04 17:58:08 -08001528 final Context context = root.getContext();
1529 final ViewGroup target = (ViewGroup) root.findViewById(viewId);
Joe Onorato2b69ce42010-10-31 11:35:41 -07001530 if (target == null) return;
Jeff Sharkey1162fd72009-11-04 17:58:08 -08001531 if (nestedViews != null) {
1532 // Inflate nested views and add as children
Dianne Hackborna1940212012-06-28 16:07:22 -07001533 target.addView(nestedViews.apply(context, target, handler));
Joe Onorato2b69ce42010-10-31 11:35:41 -07001534 } else {
Jeff Sharkey1162fd72009-11-04 17:58:08 -08001535 // Clear all children when nested views omitted
1536 target.removeAllViews();
1537 }
1538 }
1539
Winson Chung3ec9a452010-09-23 16:40:28 -07001540 @Override
Sunny Goyaldd292f42015-12-02 14:29:27 -08001541 public Action initActionAsync(ViewTree root, ViewGroup rootParent, OnClickHandler handler) {
1542 // In the async implementation, update the view tree so that subsequent calls to
1543 // findViewById return the currect view.
1544 root.createTree();
1545 ViewTree target = root.findViewTreeById(viewId);
1546 if ((target == null) || !(target.mRoot instanceof ViewGroup)) {
1547 return ACTION_NOOP;
1548 }
1549 if (nestedViews == null) {
1550 // Clear all children when nested views omitted
1551 target.mChildren = null;
1552 return this;
1553 } else {
1554 // Inflate nested views and perform all the async tasks for the child remoteView.
1555 final Context context = root.mRoot.getContext();
1556 final AsyncApplyTask task = nestedViews.getAsyncApplyTask(
1557 context, (ViewGroup) target.mRoot, null, handler);
1558 final ViewTree tree = task.doInBackground();
1559
1560 // Update the global view tree, so that next call to findViewTreeById
1561 // goes through the subtree as well.
1562 target.addChild(tree);
1563
1564 return new RuntimeAction() {
1565
1566 @Override
1567 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) throws ActionException {
1568 // This view will exist as we have already made sure
1569 final ViewGroup target = (ViewGroup) root.findViewById(viewId);
1570 task.onPostExecute(tree);
1571 target.addView(task.mResult);
1572 }
1573 };
1574 }
1575 }
1576
1577 @Override
Winson Chung3ec9a452010-09-23 16:40:28 -07001578 public void updateMemoryUsageEstimate(MemoryUsageCounter counter) {
1579 if (nestedViews != null) {
Adam Cohen5d200642012-04-24 10:43:31 -07001580 counter.increment(nestedViews.estimateMemoryUsage());
1581 }
1582 }
1583
1584 @Override
1585 public void setBitmapCache(BitmapCache bitmapCache) {
1586 if (nestedViews != null) {
1587 nestedViews.setBitmapCache(bitmapCache);
Winson Chung3ec9a452010-09-23 16:40:28 -07001588 }
1589 }
1590
Adam Cohenfbe44b72012-09-19 20:36:23 -07001591 public String getActionName() {
Romain Guye4d4e202013-07-22 13:02:02 -07001592 return "ViewGroupAction" + (nestedViews == null ? "Remove" : "Add");
Adam Cohenfbe44b72012-09-19 20:36:23 -07001593 }
1594
1595 public int mergeBehavior() {
1596 return MERGE_APPEND;
1597 }
1598
Jeff Sharkey1162fd72009-11-04 17:58:08 -08001599 RemoteViews nestedViews;
1600
1601 public final static int TAG = 4;
1602 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001603
1604 /**
Daniel Sandler820ba322012-03-23 16:36:00 -05001605 * Helper action to set compound drawables on a TextView. Supports relative
1606 * (s/t/e/b) or cardinal (l/t/r/b) arrangement.
1607 */
1608 private class TextViewDrawableAction extends Action {
1609 public TextViewDrawableAction(int viewId, boolean isRelative, int d1, int d2, int d3, int d4) {
1610 this.viewId = viewId;
1611 this.isRelative = isRelative;
Dan Sandler912282e2015-07-28 22:49:30 -04001612 this.useIcons = false;
Daniel Sandler820ba322012-03-23 16:36:00 -05001613 this.d1 = d1;
1614 this.d2 = d2;
1615 this.d3 = d3;
1616 this.d4 = d4;
1617 }
1618
Dan Sandler912282e2015-07-28 22:49:30 -04001619 public TextViewDrawableAction(int viewId, boolean isRelative,
1620 Icon i1, Icon i2, Icon i3, Icon i4) {
1621 this.viewId = viewId;
1622 this.isRelative = isRelative;
1623 this.useIcons = true;
1624 this.i1 = i1;
1625 this.i2 = i2;
1626 this.i3 = i3;
1627 this.i4 = i4;
1628 }
1629
Daniel Sandler820ba322012-03-23 16:36:00 -05001630 public TextViewDrawableAction(Parcel parcel) {
1631 viewId = parcel.readInt();
1632 isRelative = (parcel.readInt() != 0);
Dan Sandler912282e2015-07-28 22:49:30 -04001633 useIcons = (parcel.readInt() != 0);
1634 if (useIcons) {
1635 if (parcel.readInt() != 0) {
1636 i1 = Icon.CREATOR.createFromParcel(parcel);
1637 }
1638 if (parcel.readInt() != 0) {
1639 i2 = Icon.CREATOR.createFromParcel(parcel);
1640 }
1641 if (parcel.readInt() != 0) {
1642 i3 = Icon.CREATOR.createFromParcel(parcel);
1643 }
1644 if (parcel.readInt() != 0) {
1645 i4 = Icon.CREATOR.createFromParcel(parcel);
1646 }
1647 } else {
1648 d1 = parcel.readInt();
1649 d2 = parcel.readInt();
1650 d3 = parcel.readInt();
1651 d4 = parcel.readInt();
1652 }
Daniel Sandler820ba322012-03-23 16:36:00 -05001653 }
1654
1655 public void writeToParcel(Parcel dest, int flags) {
1656 dest.writeInt(TAG);
1657 dest.writeInt(viewId);
1658 dest.writeInt(isRelative ? 1 : 0);
Dan Sandler912282e2015-07-28 22:49:30 -04001659 dest.writeInt(useIcons ? 1 : 0);
1660 if (useIcons) {
1661 if (i1 != null) {
1662 dest.writeInt(1);
1663 i1.writeToParcel(dest, 0);
1664 } else {
1665 dest.writeInt(0);
1666 }
1667 if (i2 != null) {
1668 dest.writeInt(1);
1669 i2.writeToParcel(dest, 0);
1670 } else {
1671 dest.writeInt(0);
1672 }
1673 if (i3 != null) {
1674 dest.writeInt(1);
1675 i3.writeToParcel(dest, 0);
1676 } else {
1677 dest.writeInt(0);
1678 }
1679 if (i4 != null) {
1680 dest.writeInt(1);
1681 i4.writeToParcel(dest, 0);
1682 } else {
1683 dest.writeInt(0);
1684 }
1685 } else {
1686 dest.writeInt(d1);
1687 dest.writeInt(d2);
1688 dest.writeInt(d3);
1689 dest.writeInt(d4);
1690 }
Daniel Sandler820ba322012-03-23 16:36:00 -05001691 }
1692
1693 @Override
Dianne Hackborna1940212012-06-28 16:07:22 -07001694 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
Daniel Sandler820ba322012-03-23 16:36:00 -05001695 final TextView target = (TextView) root.findViewById(viewId);
1696 if (target == null) return;
Sunny Goyaldd292f42015-12-02 14:29:27 -08001697 if (drawablesLoaded) {
1698 if (isRelative) {
1699 target.setCompoundDrawablesRelativeWithIntrinsicBounds(id1, id2, id3, id4);
1700 } else {
1701 target.setCompoundDrawablesWithIntrinsicBounds(id1, id2, id3, id4);
1702 }
1703 } else if (useIcons) {
Dan Sandler912282e2015-07-28 22:49:30 -04001704 final Context ctx = target.getContext();
1705 final Drawable id1 = i1 == null ? null : i1.loadDrawable(ctx);
1706 final Drawable id2 = i2 == null ? null : i2.loadDrawable(ctx);
1707 final Drawable id3 = i3 == null ? null : i3.loadDrawable(ctx);
1708 final Drawable id4 = i4 == null ? null : i4.loadDrawable(ctx);
1709 if (isRelative) {
1710 target.setCompoundDrawablesRelativeWithIntrinsicBounds(id1, id2, id3, id4);
1711 } else {
1712 target.setCompoundDrawablesWithIntrinsicBounds(id1, id2, id3, id4);
1713 }
Daniel Sandler820ba322012-03-23 16:36:00 -05001714 } else {
Dan Sandler912282e2015-07-28 22:49:30 -04001715 if (isRelative) {
1716 target.setCompoundDrawablesRelativeWithIntrinsicBounds(d1, d2, d3, d4);
1717 } else {
1718 target.setCompoundDrawablesWithIntrinsicBounds(d1, d2, d3, d4);
1719 }
Daniel Sandler820ba322012-03-23 16:36:00 -05001720 }
1721 }
1722
Sunny Goyaldd292f42015-12-02 14:29:27 -08001723 @Override
1724 public Action initActionAsync(ViewTree root, ViewGroup rootParent, OnClickHandler handler) {
1725 final TextView target = (TextView) root.findViewById(viewId);
1726 if (target == null) return ACTION_NOOP;
1727
1728 TextViewDrawableAction copy = useIcons ?
1729 new TextViewDrawableAction(viewId, isRelative, i1, i2, i3, i4) :
1730 new TextViewDrawableAction(viewId, isRelative, d1, d2, d3, d4);
1731
1732 // Load the drawables on the background thread.
1733 copy.drawablesLoaded = true;
1734 final Context ctx = target.getContext();
1735
1736 if (useIcons) {
1737 copy.id1 = i1 == null ? null : i1.loadDrawable(ctx);
1738 copy.id2 = i2 == null ? null : i2.loadDrawable(ctx);
1739 copy.id3 = i3 == null ? null : i3.loadDrawable(ctx);
1740 copy.id4 = i4 == null ? null : i4.loadDrawable(ctx);
1741 } else {
1742 copy.id1 = d1 == 0 ? null : ctx.getDrawable(d1);
1743 copy.id2 = d2 == 0 ? null : ctx.getDrawable(d2);
1744 copy.id3 = d3 == 0 ? null : ctx.getDrawable(d3);
1745 copy.id4 = d4 == 0 ? null : ctx.getDrawable(d4);
1746 }
1747 return copy;
1748 }
1749
Adam Cohenfbe44b72012-09-19 20:36:23 -07001750 public String getActionName() {
1751 return "TextViewDrawableAction";
1752 }
1753
Daniel Sandler820ba322012-03-23 16:36:00 -05001754 boolean isRelative = false;
Dan Sandler912282e2015-07-28 22:49:30 -04001755 boolean useIcons = false;
Daniel Sandler820ba322012-03-23 16:36:00 -05001756 int d1, d2, d3, d4;
Dan Sandler912282e2015-07-28 22:49:30 -04001757 Icon i1, i2, i3, i4;
Daniel Sandler820ba322012-03-23 16:36:00 -05001758
Sunny Goyaldd292f42015-12-02 14:29:27 -08001759 boolean drawablesLoaded = false;
1760 Drawable id1, id2, id3, id4;
1761
Daniel Sandler820ba322012-03-23 16:36:00 -05001762 public final static int TAG = 11;
1763 }
1764
1765 /**
Daniel Sandler99d1f742012-05-21 16:14:14 -04001766 * Helper action to set text size on a TextView in any supported units.
Daniel Sandler7264f712012-05-21 14:48:23 -04001767 */
1768 private class TextViewSizeAction extends Action {
1769 public TextViewSizeAction(int viewId, int units, float size) {
1770 this.viewId = viewId;
1771 this.units = units;
1772 this.size = size;
1773 }
1774
1775 public TextViewSizeAction(Parcel parcel) {
1776 viewId = parcel.readInt();
1777 units = parcel.readInt();
1778 size = parcel.readFloat();
1779 }
1780
1781 public void writeToParcel(Parcel dest, int flags) {
1782 dest.writeInt(TAG);
1783 dest.writeInt(viewId);
1784 dest.writeInt(units);
1785 dest.writeFloat(size);
1786 }
1787
1788 @Override
Dianne Hackborna1940212012-06-28 16:07:22 -07001789 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
Daniel Sandler7264f712012-05-21 14:48:23 -04001790 final TextView target = (TextView) root.findViewById(viewId);
1791 if (target == null) return;
1792 target.setTextSize(units, size);
1793 }
1794
Adam Cohenfbe44b72012-09-19 20:36:23 -07001795 public String getActionName() {
1796 return "TextViewSizeAction";
1797 }
1798
Daniel Sandler7264f712012-05-21 14:48:23 -04001799 int units;
1800 float size;
1801
1802 public final static int TAG = 13;
1803 }
1804
1805 /**
Daniel Sandler99d1f742012-05-21 16:14:14 -04001806 * Helper action to set padding on a View.
1807 */
1808 private class ViewPaddingAction extends Action {
1809 public ViewPaddingAction(int viewId, int left, int top, int right, int bottom) {
1810 this.viewId = viewId;
1811 this.left = left;
1812 this.top = top;
1813 this.right = right;
1814 this.bottom = bottom;
1815 }
1816
1817 public ViewPaddingAction(Parcel parcel) {
1818 viewId = parcel.readInt();
1819 left = parcel.readInt();
1820 top = parcel.readInt();
1821 right = parcel.readInt();
1822 bottom = parcel.readInt();
1823 }
1824
1825 public void writeToParcel(Parcel dest, int flags) {
1826 dest.writeInt(TAG);
1827 dest.writeInt(viewId);
1828 dest.writeInt(left);
1829 dest.writeInt(top);
1830 dest.writeInt(right);
1831 dest.writeInt(bottom);
1832 }
1833
1834 @Override
Dianne Hackborna1940212012-06-28 16:07:22 -07001835 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
Daniel Sandler99d1f742012-05-21 16:14:14 -04001836 final View target = root.findViewById(viewId);
1837 if (target == null) return;
1838 target.setPadding(left, top, right, bottom);
1839 }
1840
Adam Cohenfbe44b72012-09-19 20:36:23 -07001841 public String getActionName() {
1842 return "ViewPaddingAction";
1843 }
1844
Daniel Sandler99d1f742012-05-21 16:14:14 -04001845 int left, top, right, bottom;
1846
1847 public final static int TAG = 14;
1848 }
1849
1850 /**
Adrian Roos9b123cf2016-02-04 14:55:57 -08001851 * Helper action to set layout params on a View.
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001852 */
Adrian Roos9b123cf2016-02-04 14:55:57 -08001853 private class LayoutParamAction extends Action {
1854
1855 /** Set marginEnd */
1856 public static final int LAYOUT_MARGIN_END = 1;
1857 /** Set width */
1858 public static final int LAYOUT_WIDTH = 2;
1859
1860 /**
1861 * @param viewId ID of the view alter
1862 * @param property which layout parameter to alter
1863 * @param value new value of the layout parameter
1864 */
1865 public LayoutParamAction(int viewId, int property, int value) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001866 this.viewId = viewId;
Adrian Roos9b123cf2016-02-04 14:55:57 -08001867 this.property = property;
1868 this.value = value;
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001869 }
1870
Adrian Roos9b123cf2016-02-04 14:55:57 -08001871 public LayoutParamAction(Parcel parcel) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001872 viewId = parcel.readInt();
Adrian Roos9b123cf2016-02-04 14:55:57 -08001873 property = parcel.readInt();
1874 value = parcel.readInt();
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001875 }
1876
1877 public void writeToParcel(Parcel dest, int flags) {
1878 dest.writeInt(TAG);
1879 dest.writeInt(viewId);
Adrian Roos9b123cf2016-02-04 14:55:57 -08001880 dest.writeInt(property);
1881 dest.writeInt(value);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001882 }
1883
1884 @Override
1885 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
1886 final View target = root.findViewById(viewId);
1887 if (target == null) {
1888 return;
1889 }
1890 ViewGroup.LayoutParams layoutParams = target.getLayoutParams();
Adrian Roos9b123cf2016-02-04 14:55:57 -08001891 if (layoutParams == null) {
1892 return;
1893 }
1894 switch (property) {
1895 case LAYOUT_MARGIN_END:
1896 if (layoutParams instanceof ViewGroup.MarginLayoutParams) {
1897 ((ViewGroup.MarginLayoutParams) layoutParams).setMarginEnd(value);
1898 target.setLayoutParams(layoutParams);
1899 }
1900 break;
1901 case LAYOUT_WIDTH:
1902 layoutParams.width = value;
1903 target.setLayoutParams(layoutParams);
1904 break;
1905 default:
1906 throw new IllegalArgumentException("Unknown property " + property);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001907 }
1908 }
1909
1910 public String getActionName() {
Adrian Roos9b123cf2016-02-04 14:55:57 -08001911 return "LayoutParamAction" + property + ".";
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001912 }
1913
Adrian Roos9b123cf2016-02-04 14:55:57 -08001914 int property;
1915 int value;
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001916
1917 public final static int TAG = 19;
1918 }
1919
1920 /**
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01001921 * Helper action to set a color filter on a compound drawable on a TextView. Supports relative
1922 * (s/t/e/b) or cardinal (l/t/r/b) arrangement.
1923 */
1924 private class TextViewDrawableColorFilterAction extends Action {
1925 public TextViewDrawableColorFilterAction(int viewId, boolean isRelative, int index,
1926 int color, PorterDuff.Mode mode) {
1927 this.viewId = viewId;
1928 this.isRelative = isRelative;
1929 this.index = index;
1930 this.color = color;
1931 this.mode = mode;
1932 }
1933
1934 public TextViewDrawableColorFilterAction(Parcel parcel) {
1935 viewId = parcel.readInt();
1936 isRelative = (parcel.readInt() != 0);
1937 index = parcel.readInt();
1938 color = parcel.readInt();
1939 mode = readPorterDuffMode(parcel);
1940 }
1941
1942 private PorterDuff.Mode readPorterDuffMode(Parcel parcel) {
1943 int mode = parcel.readInt();
1944 if (mode >= 0 && mode < PorterDuff.Mode.values().length) {
1945 return PorterDuff.Mode.values()[mode];
1946 } else {
1947 return PorterDuff.Mode.CLEAR;
1948 }
1949 }
1950
1951 public void writeToParcel(Parcel dest, int flags) {
1952 dest.writeInt(TAG);
1953 dest.writeInt(viewId);
1954 dest.writeInt(isRelative ? 1 : 0);
1955 dest.writeInt(index);
1956 dest.writeInt(color);
1957 dest.writeInt(mode.ordinal());
1958 }
1959
1960 @Override
1961 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
1962 final TextView target = (TextView) root.findViewById(viewId);
1963 if (target == null) return;
1964 Drawable[] drawables = isRelative
1965 ? target.getCompoundDrawablesRelative()
1966 : target.getCompoundDrawables();
1967 if (index < 0 || index >= 4) {
1968 throw new IllegalStateException("index must be in range [0, 3].");
1969 }
1970 Drawable d = drawables[index];
1971 if (d != null) {
1972 d.mutate();
1973 d.setColorFilter(color, mode);
1974 }
1975 }
1976
1977 public String getActionName() {
1978 return "TextViewDrawableColorFilterAction";
1979 }
1980
1981 final boolean isRelative;
1982 final int index;
1983 final int color;
1984 final PorterDuff.Mode mode;
1985
1986 public final static int TAG = 17;
1987 }
1988
1989 /**
Adrian Roosfe84e1f2015-11-04 15:55:39 -08001990 * Helper action to add a view tag with RemoteInputs.
1991 */
1992 private class SetRemoteInputsAction extends Action {
1993
1994 public SetRemoteInputsAction(int viewId, RemoteInput[] remoteInputs) {
1995 this.viewId = viewId;
1996 this.remoteInputs = remoteInputs;
1997 }
1998
1999 public SetRemoteInputsAction(Parcel parcel) {
2000 viewId = parcel.readInt();
Adrian Roos5dd685f2016-02-24 12:05:51 -08002001 remoteInputs = parcel.createTypedArray(RemoteInput.CREATOR);
Adrian Roosfe84e1f2015-11-04 15:55:39 -08002002 }
2003
2004 public void writeToParcel(Parcel dest, int flags) {
2005 dest.writeInt(TAG);
2006 dest.writeInt(viewId);
Adrian Roos5dd685f2016-02-24 12:05:51 -08002007 dest.writeTypedArray(remoteInputs, flags);
Adrian Roosfe84e1f2015-11-04 15:55:39 -08002008 }
2009
2010 @Override
2011 public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
2012 final TextView target = (TextView) root.findViewById(viewId);
2013 if (target == null) return;
2014
2015 target.setTagInternal(R.id.remote_input_tag, remoteInputs);
2016 }
2017
2018 public String getActionName() {
2019 return "SetRemoteInputsAction";
2020 }
2021
2022 final Parcelable[] remoteInputs;
2023 public final static int TAG = 18;
2024 }
2025
2026 /**
Winson Chung3ec9a452010-09-23 16:40:28 -07002027 * Simple class used to keep track of memory usage in a RemoteViews.
2028 *
2029 */
2030 private class MemoryUsageCounter {
2031 public void clear() {
Adam Cohen5d200642012-04-24 10:43:31 -07002032 mMemoryUsage = 0;
Winson Chung3ec9a452010-09-23 16:40:28 -07002033 }
2034
Adam Cohen5d200642012-04-24 10:43:31 -07002035 public void increment(int numBytes) {
2036 mMemoryUsage += numBytes;
Winson Chung3ec9a452010-09-23 16:40:28 -07002037 }
2038
Adam Cohen5d200642012-04-24 10:43:31 -07002039 public int getMemoryUsage() {
2040 return mMemoryUsage;
Winson Chung3ec9a452010-09-23 16:40:28 -07002041 }
2042
Romain Guye4d4e202013-07-22 13:02:02 -07002043 @SuppressWarnings("deprecation")
Adam Cohen5d200642012-04-24 10:43:31 -07002044 public void addBitmapMemory(Bitmap b) {
2045 final Bitmap.Config c = b.getConfig();
2046 // If we don't know, be pessimistic and assume 4
2047 int bpp = 4;
2048 if (c != null) {
2049 switch (c) {
Svetoslav976e8bd2014-07-16 15:12:03 -07002050 case ALPHA_8:
2051 bpp = 1;
2052 break;
2053 case RGB_565:
2054 case ARGB_4444:
2055 bpp = 2;
2056 break;
2057 case ARGB_8888:
2058 bpp = 4;
2059 break;
Adam Cohen5d200642012-04-24 10:43:31 -07002060 }
2061 }
2062 increment(b.getWidth() * b.getHeight() * bpp);
2063 }
2064
2065 int mMemoryUsage;
Winson Chung3ec9a452010-09-23 16:40:28 -07002066 }
2067
2068 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002069 * Create a new RemoteViews object that will display the views contained
2070 * in the specified layout file.
Jim Millere667a7a2012-08-09 19:22:32 -07002071 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002072 * @param packageName Name of the package that contains the layout resource
2073 * @param layoutId The id of the layout resource
2074 */
2075 public RemoteViews(String packageName, int layoutId) {
Svet Ganov0da85b62014-08-06 14:11:37 -07002076 this(getApplicationInfo(packageName, UserHandle.myUserId()), layoutId);
2077 }
2078
2079 /**
2080 * Create a new RemoteViews object that will display the views contained
2081 * in the specified layout file.
2082 *
2083 * @param packageName Name of the package that contains the layout resource.
2084 * @param userId The user under which the package is running.
2085 * @param layoutId The id of the layout resource.
2086 *
2087 * @hide
2088 */
2089 public RemoteViews(String packageName, int userId, int layoutId) {
2090 this(getApplicationInfo(packageName, userId), layoutId);
2091 }
2092
2093 /**
2094 * Create a new RemoteViews object that will display the views contained
2095 * in the specified layout file.
2096 *
2097 * @param application The application whose content is shown by the views.
2098 * @param layoutId The id of the layout resource.
Kenny Guy77320062014-08-27 21:37:15 +01002099 *
2100 * @hide
Svet Ganov0da85b62014-08-06 14:11:37 -07002101 */
Kenny Guy77320062014-08-27 21:37:15 +01002102 protected RemoteViews(ApplicationInfo application, int layoutId) {
Svet Ganov0da85b62014-08-06 14:11:37 -07002103 mApplication = application;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002104 mLayoutId = layoutId;
Adam Cohen5d200642012-04-24 10:43:31 -07002105 mBitmapCache = new BitmapCache();
Winson Chung3ec9a452010-09-23 16:40:28 -07002106 // setup the memory usage statistics
2107 mMemoryUsageCounter = new MemoryUsageCounter();
2108 recalculateMemoryUsage();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002109 }
2110
Adam Cohen5d200642012-04-24 10:43:31 -07002111 private boolean hasLandscapeAndPortraitLayouts() {
2112 return (mLandscape != null) && (mPortrait != null);
2113 }
2114
Jeff Sharkey6d515712012-09-20 16:06:08 -07002115 /**
Adam Cohen5d200642012-04-24 10:43:31 -07002116 * Create a new RemoteViews object that will inflate as the specified
2117 * landspace or portrait RemoteViews, depending on the current configuration.
2118 *
2119 * @param landscape The RemoteViews to inflate in landscape configuration
2120 * @param portrait The RemoteViews to inflate in portrait configuration
2121 */
2122 public RemoteViews(RemoteViews landscape, RemoteViews portrait) {
2123 if (landscape == null || portrait == null) {
2124 throw new RuntimeException("Both RemoteViews must be non-null");
2125 }
Svet Ganov0da85b62014-08-06 14:11:37 -07002126 if (landscape.mApplication.uid != portrait.mApplication.uid
2127 || !landscape.mApplication.packageName.equals(portrait.mApplication.packageName)) {
2128 throw new RuntimeException("Both RemoteViews must share the same package and user");
Adam Cohen5d200642012-04-24 10:43:31 -07002129 }
Svet Ganov0da85b62014-08-06 14:11:37 -07002130 mApplication = portrait.mApplication;
Adam Cohen5d200642012-04-24 10:43:31 -07002131 mLayoutId = portrait.getLayoutId();
2132
2133 mLandscape = landscape;
2134 mPortrait = portrait;
2135
2136 // setup the memory usage statistics
2137 mMemoryUsageCounter = new MemoryUsageCounter();
2138
2139 mBitmapCache = new BitmapCache();
2140 configureRemoteViewsAsChild(landscape);
2141 configureRemoteViewsAsChild(portrait);
2142
2143 recalculateMemoryUsage();
2144 }
2145
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002146 /**
2147 * Reads a RemoteViews object from a parcel.
Jim Millere667a7a2012-08-09 19:22:32 -07002148 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002149 * @param parcel
2150 */
2151 public RemoteViews(Parcel parcel) {
Adam Cohen5d200642012-04-24 10:43:31 -07002152 this(parcel, null);
2153 }
Adam Cohenca6fd842010-09-03 18:10:35 -07002154
Adam Cohen5d200642012-04-24 10:43:31 -07002155 private RemoteViews(Parcel parcel, BitmapCache bitmapCache) {
2156 int mode = parcel.readInt();
2157
2158 // We only store a bitmap cache in the root of the RemoteViews.
2159 if (bitmapCache == null) {
2160 mBitmapCache = new BitmapCache(parcel);
2161 } else {
2162 setBitmapCache(bitmapCache);
2163 setNotRoot();
2164 }
2165
2166 if (mode == MODE_NORMAL) {
Svet Ganov0da85b62014-08-06 14:11:37 -07002167 mApplication = parcel.readParcelable(null);
Adam Cohen5d200642012-04-24 10:43:31 -07002168 mLayoutId = parcel.readInt();
Romain Guye4d4e202013-07-22 13:02:02 -07002169 mIsWidgetCollectionChild = parcel.readInt() == 1;
Adam Cohen5d200642012-04-24 10:43:31 -07002170
2171 int count = parcel.readInt();
2172 if (count > 0) {
2173 mActions = new ArrayList<Action>(count);
2174 for (int i=0; i<count; i++) {
2175 int tag = parcel.readInt();
2176 switch (tag) {
Svetoslav976e8bd2014-07-16 15:12:03 -07002177 case SetOnClickPendingIntent.TAG:
2178 mActions.add(new SetOnClickPendingIntent(parcel));
2179 break;
2180 case SetDrawableParameters.TAG:
2181 mActions.add(new SetDrawableParameters(parcel));
2182 break;
2183 case ReflectionAction.TAG:
2184 mActions.add(new ReflectionAction(parcel));
2185 break;
2186 case ViewGroupAction.TAG:
2187 mActions.add(new ViewGroupAction(parcel, mBitmapCache));
2188 break;
2189 case ReflectionActionWithoutParams.TAG:
2190 mActions.add(new ReflectionActionWithoutParams(parcel));
2191 break;
2192 case SetEmptyView.TAG:
2193 mActions.add(new SetEmptyView(parcel));
2194 break;
2195 case SetPendingIntentTemplate.TAG:
2196 mActions.add(new SetPendingIntentTemplate(parcel));
2197 break;
2198 case SetOnClickFillInIntent.TAG:
2199 mActions.add(new SetOnClickFillInIntent(parcel));
2200 break;
2201 case SetRemoteViewsAdapterIntent.TAG:
2202 mActions.add(new SetRemoteViewsAdapterIntent(parcel));
2203 break;
2204 case TextViewDrawableAction.TAG:
2205 mActions.add(new TextViewDrawableAction(parcel));
2206 break;
2207 case TextViewSizeAction.TAG:
2208 mActions.add(new TextViewSizeAction(parcel));
2209 break;
2210 case ViewPaddingAction.TAG:
2211 mActions.add(new ViewPaddingAction(parcel));
2212 break;
2213 case BitmapReflectionAction.TAG:
2214 mActions.add(new BitmapReflectionAction(parcel));
2215 break;
2216 case SetRemoteViewsAdapterList.TAG:
2217 mActions.add(new SetRemoteViewsAdapterList(parcel));
2218 break;
2219 case TextViewDrawableColorFilterAction.TAG:
2220 mActions.add(new TextViewDrawableColorFilterAction(parcel));
2221 break;
Adrian Roosfe84e1f2015-11-04 15:55:39 -08002222 case SetRemoteInputsAction.TAG:
2223 mActions.add(new SetRemoteInputsAction(parcel));
2224 break;
Adrian Roos9b123cf2016-02-04 14:55:57 -08002225 case LayoutParamAction.TAG:
2226 mActions.add(new LayoutParamAction(parcel));
Selim Cinek65b2e7c2015-10-26 14:11:31 -07002227 break;
Svetoslav976e8bd2014-07-16 15:12:03 -07002228 default:
2229 throw new ActionException("Tag " + tag + " not found");
Adam Cohen5d200642012-04-24 10:43:31 -07002230 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002231 }
2232 }
Adam Cohen5d200642012-04-24 10:43:31 -07002233 } else {
2234 // MODE_HAS_LANDSCAPE_AND_PORTRAIT
2235 mLandscape = new RemoteViews(parcel, mBitmapCache);
2236 mPortrait = new RemoteViews(parcel, mBitmapCache);
Svet Ganov0da85b62014-08-06 14:11:37 -07002237 mApplication = mPortrait.mApplication;
Adam Cohen5d200642012-04-24 10:43:31 -07002238 mLayoutId = mPortrait.getLayoutId();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002239 }
Winson Chung3ec9a452010-09-23 16:40:28 -07002240
2241 // setup the memory usage statistics
2242 mMemoryUsageCounter = new MemoryUsageCounter();
2243 recalculateMemoryUsage();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002244 }
2245
Winson Chung3ec9a452010-09-23 16:40:28 -07002246
Adam Cohen3ff2d862012-09-26 14:07:57 -07002247 public RemoteViews clone() {
Adrian Roos7da889d2016-03-16 18:38:58 -07002248 Preconditions.checkState(mIsRoot, "RemoteView has been attached to another RemoteView. "
2249 + "May only clone the root of a RemoteView hierarchy.");
2250
Adam Cohen3ff2d862012-09-26 14:07:57 -07002251 Parcel p = Parcel.obtain();
Adrian Roos7da889d2016-03-16 18:38:58 -07002252
2253 // Do not parcel the Bitmap cache - doing so creates an expensive copy of all bitmaps.
2254 // Instead pretend we're not owning the cache while parceling.
2255 mIsRoot = false;
Adam Cohen3ff2d862012-09-26 14:07:57 -07002256 writeToParcel(p, 0);
2257 p.setDataPosition(0);
Adrian Roos7da889d2016-03-16 18:38:58 -07002258 mIsRoot = true;
2259
2260 RemoteViews rv = new RemoteViews(p, mBitmapCache.clone());
2261 rv.mIsRoot = true;
2262
Maunik Shahdb1a9a32014-06-19 14:18:39 +05302263 p.recycle();
2264 return rv;
Joe Onorato18e69df2010-05-17 22:26:12 -07002265 }
2266
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002267 public String getPackage() {
Svetoslavb6242442014-09-19 13:21:55 -07002268 return (mApplication != null) ? mApplication.packageName : null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002269 }
2270
Adam Cohen5d200642012-04-24 10:43:31 -07002271 /**
Adrian Roos7da889d2016-03-16 18:38:58 -07002272 * Returns the layout id of the root layout associated with this RemoteViews. In the case
Adam Cohen5d200642012-04-24 10:43:31 -07002273 * that the RemoteViews has both a landscape and portrait root, this will return the layout
2274 * id associated with the portrait layout.
2275 *
2276 * @return the layout id.
2277 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002278 public int getLayoutId() {
2279 return mLayoutId;
2280 }
2281
Winson Chung3ec9a452010-09-23 16:40:28 -07002282 /*
Adam Cohenca6fd842010-09-03 18:10:35 -07002283 * This flag indicates whether this RemoteViews object is being created from a
2284 * RemoteViewsService for use as a child of a widget collection. This flag is used
2285 * to determine whether or not certain features are available, in particular,
2286 * setting on click extras and setting on click pending intents. The former is enabled,
2287 * and the latter disabled when this flag is true.
2288 */
2289 void setIsWidgetCollectionChild(boolean isWidgetCollectionChild) {
2290 mIsWidgetCollectionChild = isWidgetCollectionChild;
2291 }
2292
2293 /**
Winson Chung3ec9a452010-09-23 16:40:28 -07002294 * Updates the memory usage statistics.
2295 */
2296 private void recalculateMemoryUsage() {
2297 mMemoryUsageCounter.clear();
2298
Adam Cohen5d200642012-04-24 10:43:31 -07002299 if (!hasLandscapeAndPortraitLayouts()) {
2300 // Accumulate the memory usage for each action
2301 if (mActions != null) {
2302 final int count = mActions.size();
2303 for (int i= 0; i < count; ++i) {
2304 mActions.get(i).updateMemoryUsageEstimate(mMemoryUsageCounter);
2305 }
Winson Chung3ec9a452010-09-23 16:40:28 -07002306 }
Adam Cohen5d200642012-04-24 10:43:31 -07002307 if (mIsRoot) {
2308 mBitmapCache.addBitmapMemory(mMemoryUsageCounter);
2309 }
2310 } else {
2311 mMemoryUsageCounter.increment(mLandscape.estimateMemoryUsage());
2312 mMemoryUsageCounter.increment(mPortrait.estimateMemoryUsage());
2313 mBitmapCache.addBitmapMemory(mMemoryUsageCounter);
2314 }
2315 }
2316
2317 /**
2318 * Recursively sets BitmapCache in the hierarchy and update the bitmap ids.
2319 */
2320 private void setBitmapCache(BitmapCache bitmapCache) {
2321 mBitmapCache = bitmapCache;
2322 if (!hasLandscapeAndPortraitLayouts()) {
2323 if (mActions != null) {
2324 final int count = mActions.size();
2325 for (int i= 0; i < count; ++i) {
2326 mActions.get(i).setBitmapCache(bitmapCache);
2327 }
2328 }
2329 } else {
2330 mLandscape.setBitmapCache(bitmapCache);
2331 mPortrait.setBitmapCache(bitmapCache);
Winson Chung3ec9a452010-09-23 16:40:28 -07002332 }
2333 }
2334
2335 /**
2336 * Returns an estimate of the bitmap heap memory usage for this RemoteViews.
2337 */
Adam Cohen311c79c2012-05-10 14:44:38 -07002338 /** @hide */
2339 public int estimateMemoryUsage() {
Adam Cohen5d200642012-04-24 10:43:31 -07002340 return mMemoryUsageCounter.getMemoryUsage();
Winson Chung3ec9a452010-09-23 16:40:28 -07002341 }
2342
2343 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002344 * Add an action to be executed on the remote side when apply is called.
Jim Millere667a7a2012-08-09 19:22:32 -07002345 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002346 * @param a The action to add
2347 */
2348 private void addAction(Action a) {
Adam Cohen5d200642012-04-24 10:43:31 -07002349 if (hasLandscapeAndPortraitLayouts()) {
2350 throw new RuntimeException("RemoteViews specifying separate landscape and portrait" +
2351 " layouts cannot be modified. Instead, fully configure the landscape and" +
2352 " portrait layouts individually before constructing the combined layout.");
2353 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002354 if (mActions == null) {
2355 mActions = new ArrayList<Action>();
2356 }
2357 mActions.add(a);
Winson Chung3ec9a452010-09-23 16:40:28 -07002358
2359 // update the memory usage stats
2360 a.updateMemoryUsageEstimate(mMemoryUsageCounter);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002361 }
Jeff Sharkey1162fd72009-11-04 17:58:08 -08002362
2363 /**
2364 * Equivalent to calling {@link ViewGroup#addView(View)} after inflating the
2365 * given {@link RemoteViews}. This allows users to build "nested"
2366 * {@link RemoteViews}. In cases where consumers of {@link RemoteViews} may
2367 * recycle layouts, use {@link #removeAllViews(int)} to clear any existing
2368 * children.
2369 *
2370 * @param viewId The id of the parent {@link ViewGroup} to add child into.
2371 * @param nestedView {@link RemoteViews} that describes the child.
2372 */
2373 public void addView(int viewId, RemoteViews nestedView) {
2374 addAction(new ViewGroupAction(viewId, nestedView));
2375 }
2376
2377 /**
2378 * Equivalent to calling {@link ViewGroup#removeAllViews()}.
2379 *
2380 * @param viewId The id of the parent {@link ViewGroup} to remove all
2381 * children from.
2382 */
2383 public void removeAllViews(int viewId) {
2384 addAction(new ViewGroupAction(viewId, null));
2385 }
2386
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002387 /**
Adam Cohen0b96a572011-02-10 15:56:16 -08002388 * Equivalent to calling {@link AdapterViewAnimator#showNext()}
Adam Cohen2dd21972010-08-15 18:20:04 -07002389 *
Adam Cohen0b96a572011-02-10 15:56:16 -08002390 * @param viewId The id of the view on which to call {@link AdapterViewAnimator#showNext()}
Adam Cohen2dd21972010-08-15 18:20:04 -07002391 */
2392 public void showNext(int viewId) {
2393 addAction(new ReflectionActionWithoutParams(viewId, "showNext"));
2394 }
2395
2396 /**
Adam Cohen0b96a572011-02-10 15:56:16 -08002397 * Equivalent to calling {@link AdapterViewAnimator#showPrevious()}
Adam Cohen2dd21972010-08-15 18:20:04 -07002398 *
Adam Cohen0b96a572011-02-10 15:56:16 -08002399 * @param viewId The id of the view on which to call {@link AdapterViewAnimator#showPrevious()}
Adam Cohen2dd21972010-08-15 18:20:04 -07002400 */
2401 public void showPrevious(int viewId) {
2402 addAction(new ReflectionActionWithoutParams(viewId, "showPrevious"));
2403 }
2404
2405 /**
Adam Cohen0b96a572011-02-10 15:56:16 -08002406 * Equivalent to calling {@link AdapterViewAnimator#setDisplayedChild(int)}
2407 *
2408 * @param viewId The id of the view on which to call
2409 * {@link AdapterViewAnimator#setDisplayedChild(int)}
2410 */
2411 public void setDisplayedChild(int viewId, int childIndex) {
2412 setInt(viewId, "setDisplayedChild", childIndex);
2413 }
2414
2415 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002416 * Equivalent to calling View.setVisibility
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 visibility should change
2419 * @param visibility The new visibility for the view
2420 */
2421 public void setViewVisibility(int viewId, int visibility) {
2422 setInt(viewId, "setVisibility", visibility);
2423 }
Adam Cohenca6fd842010-09-03 18:10:35 -07002424
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002425 /**
2426 * Equivalent to calling TextView.setText
Jim Millere667a7a2012-08-09 19:22:32 -07002427 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002428 * @param viewId The id of the view whose text should change
2429 * @param text The new text for the view
2430 */
2431 public void setTextViewText(int viewId, CharSequence text) {
2432 setCharSequence(viewId, "setText", text);
2433 }
Daniel Sandler7264f712012-05-21 14:48:23 -04002434
2435 /**
Daniel Sandler7264f712012-05-21 14:48:23 -04002436 * Equivalent to calling {@link TextView#setTextSize(int, float)}
Jim Millere667a7a2012-08-09 19:22:32 -07002437 *
Daniel Sandler7264f712012-05-21 14:48:23 -04002438 * @param viewId The id of the view whose text size should change
2439 * @param units The units of size (e.g. COMPLEX_UNIT_SP)
2440 * @param size The size of the text
2441 */
2442 public void setTextViewTextSize(int viewId, int units, float size) {
2443 addAction(new TextViewSizeAction(viewId, units, size));
2444 }
2445
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002446 /**
Jim Millere667a7a2012-08-09 19:22:32 -07002447 * Equivalent to calling
Daniel Sandler820ba322012-03-23 16:36:00 -05002448 * {@link TextView#setCompoundDrawablesWithIntrinsicBounds(int, int, int, int)}.
2449 *
2450 * @param viewId The id of the view whose text should change
2451 * @param left The id of a drawable to place to the left of the text, or 0
2452 * @param top The id of a drawable to place above the text, or 0
2453 * @param right The id of a drawable to place to the right of the text, or 0
Jim Millere667a7a2012-08-09 19:22:32 -07002454 * @param bottom The id of a drawable to place below the text, or 0
Daniel Sandler820ba322012-03-23 16:36:00 -05002455 */
2456 public void setTextViewCompoundDrawables(int viewId, int left, int top, int right, int bottom) {
2457 addAction(new TextViewDrawableAction(viewId, false, left, top, right, bottom));
2458 }
2459
2460 /**
Jim Millere667a7a2012-08-09 19:22:32 -07002461 * Equivalent to calling {@link
Daniel Sandler820ba322012-03-23 16:36:00 -05002462 * TextView#setCompoundDrawablesRelativeWithIntrinsicBounds(int, int, int, int)}.
2463 *
2464 * @param viewId The id of the view whose text should change
Jim Millere667a7a2012-08-09 19:22:32 -07002465 * @param start The id of a drawable to place before the text (relative to the
Daniel Sandler820ba322012-03-23 16:36:00 -05002466 * layout direction), or 0
2467 * @param top The id of a drawable to place above the text, or 0
2468 * @param end The id of a drawable to place after the text, or 0
Fabrice Di Meglio66388dc2012-05-03 18:51:57 -07002469 * @param bottom The id of a drawable to place below the text, or 0
Daniel Sandler820ba322012-03-23 16:36:00 -05002470 */
2471 public void setTextViewCompoundDrawablesRelative(int viewId, int start, int top, int end, int bottom) {
2472 addAction(new TextViewDrawableAction(viewId, true, start, top, end, bottom));
2473 }
2474
2475 /**
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01002476 * Equivalent to applying a color filter on one of the drawables in
2477 * {@link android.widget.TextView#getCompoundDrawablesRelative()}.
2478 *
2479 * @param viewId The id of the view whose text should change.
2480 * @param index The index of the drawable in the array of
2481 * {@link android.widget.TextView#getCompoundDrawablesRelative()} to set the color
2482 * filter on. Must be in [0, 3].
2483 * @param color The color of the color filter. See
2484 * {@link Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)}.
2485 * @param mode The mode of the color filter. See
2486 * {@link Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)}.
2487 * @hide
2488 */
2489 public void setTextViewCompoundDrawablesRelativeColorFilter(int viewId,
2490 int index, int color, PorterDuff.Mode mode) {
2491 if (index < 0 || index >= 4) {
2492 throw new IllegalArgumentException("index must be in range [0, 3].");
2493 }
2494 addAction(new TextViewDrawableColorFilterAction(viewId, true, index, color, mode));
2495 }
2496
2497 /**
Dan Sandler912282e2015-07-28 22:49:30 -04002498 * Equivalent to calling {@link
2499 * TextView#setCompoundDrawablesWithIntrinsicBounds(Drawable, Drawable, Drawable, Drawable)}
2500 * using the drawables yielded by {@link Icon#loadDrawable(Context)}.
2501 *
2502 * @param viewId The id of the view whose text should change
2503 * @param left an Icon to place to the left of the text, or 0
2504 * @param top an Icon to place above the text, or 0
2505 * @param right an Icon to place to the right of the text, or 0
2506 * @param bottom an Icon to place below the text, or 0
2507 *
2508 * @hide
2509 */
2510 public void setTextViewCompoundDrawables(int viewId, Icon left, Icon top, Icon right, Icon bottom) {
2511 addAction(new TextViewDrawableAction(viewId, false, left, top, right, bottom));
2512 }
2513
2514 /**
2515 * Equivalent to calling {@link
2516 * TextView#setCompoundDrawablesRelativeWithIntrinsicBounds(Drawable, Drawable, Drawable, Drawable)}
2517 * using the drawables yielded by {@link Icon#loadDrawable(Context)}.
2518 *
2519 * @param viewId The id of the view whose text should change
2520 * @param start an Icon to place before the text (relative to the
2521 * layout direction), or 0
2522 * @param top an Icon to place above the text, or 0
2523 * @param end an Icon to place after the text, or 0
2524 * @param bottom an Icon to place below the text, or 0
2525 *
2526 * @hide
2527 */
2528 public void setTextViewCompoundDrawablesRelative(int viewId, Icon start, Icon top, Icon end, Icon bottom) {
2529 addAction(new TextViewDrawableAction(viewId, true, start, top, end, bottom));
2530 }
2531
2532 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002533 * Equivalent to calling ImageView.setImageResource
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 srcId The new resource id for the drawable
2537 */
Jim Millere667a7a2012-08-09 19:22:32 -07002538 public void setImageViewResource(int viewId, int srcId) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002539 setInt(viewId, "setImageResource", srcId);
2540 }
2541
2542 /**
2543 * Equivalent to calling ImageView.setImageURI
Jim Millere667a7a2012-08-09 19:22:32 -07002544 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002545 * @param viewId The id of the view whose drawable should change
2546 * @param uri The Uri for the image
2547 */
2548 public void setImageViewUri(int viewId, Uri uri) {
2549 setUri(viewId, "setImageURI", uri);
2550 }
2551
2552 /**
2553 * Equivalent to calling ImageView.setImageBitmap
Jim Millere667a7a2012-08-09 19:22:32 -07002554 *
Scott Main93dc6422012-02-24 12:04:06 -08002555 * @param viewId The id of the view whose bitmap should change
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002556 * @param bitmap The new Bitmap for the drawable
2557 */
2558 public void setImageViewBitmap(int viewId, Bitmap bitmap) {
2559 setBitmap(viewId, "setImageBitmap", bitmap);
2560 }
2561
2562 /**
Dan Sandlera22a3802015-05-13 00:12:47 -04002563 * Equivalent to calling ImageView.setImageIcon
2564 *
2565 * @param viewId The id of the view whose bitmap should change
2566 * @param icon The new Icon for the ImageView
2567 */
2568 public void setImageViewIcon(int viewId, Icon icon) {
2569 setIcon(viewId, "setImageIcon", icon);
2570 }
2571
2572 /**
Adam Cohen1480fdd2010-08-25 17:24:53 -07002573 * Equivalent to calling AdapterView.setEmptyView
2574 *
2575 * @param viewId The id of the view on which to set the empty view
2576 * @param emptyViewId The view id of the empty view
2577 */
2578 public void setEmptyView(int viewId, int emptyViewId) {
2579 addAction(new SetEmptyView(viewId, emptyViewId));
2580 }
2581
2582 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002583 * Equivalent to calling {@link Chronometer#setBase Chronometer.setBase},
2584 * {@link Chronometer#setFormat Chronometer.setFormat},
2585 * and {@link Chronometer#start Chronometer.start()} or
2586 * {@link Chronometer#stop Chronometer.stop()}.
Jim Millere667a7a2012-08-09 19:22:32 -07002587 *
Scott Main93dc6422012-02-24 12:04:06 -08002588 * @param viewId The id of the {@link Chronometer} to change
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002589 * @param base The time at which the timer would have read 0:00. This
2590 * time should be based off of
2591 * {@link android.os.SystemClock#elapsedRealtime SystemClock.elapsedRealtime()}.
2592 * @param format The Chronometer format string, or null to
2593 * simply display the timer value.
2594 * @param started True if you want the clock to be started, false if not.
Selim Cinek81c23aa2016-02-25 16:23:13 -08002595 *
2596 * @see #setChronometerCountsDown(int, boolean)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002597 */
2598 public void setChronometer(int viewId, long base, String format, boolean started) {
2599 setLong(viewId, "setBase", base);
2600 setString(viewId, "setFormat", format);
2601 setBoolean(viewId, "setStarted", started);
2602 }
Jim Millere667a7a2012-08-09 19:22:32 -07002603
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002604 /**
Selim Cinek81c23aa2016-02-25 16:23:13 -08002605 * Equivalent to calling {@link Chronometer#setCountDown(boolean) Chronometer.setCountDown} on
2606 * the chronometer with the given viewId.
2607 *
2608 * @param viewId The id of the {@link Chronometer} to change
2609 * @param isCountDown True if you want the chronometer to count down to base instead of
2610 * counting up.
2611 */
2612 public void setChronometerCountsDown(int viewId, boolean isCountDown) {
2613 setBoolean(viewId, "setCountDown", isCountDown);
2614 }
2615
2616 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002617 * Equivalent to calling {@link ProgressBar#setMax ProgressBar.setMax},
2618 * {@link ProgressBar#setProgress ProgressBar.setProgress}, and
2619 * {@link ProgressBar#setIndeterminate ProgressBar.setIndeterminate}
2620 *
2621 * If indeterminate is true, then the values for max and progress are ignored.
Jim Millere667a7a2012-08-09 19:22:32 -07002622 *
Scott Main93dc6422012-02-24 12:04:06 -08002623 * @param viewId The id of the {@link ProgressBar} to change
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002624 * @param max The 100% value for the progress bar
2625 * @param progress The current value of the progress bar.
Jim Millere667a7a2012-08-09 19:22:32 -07002626 * @param indeterminate True if the progress bar is indeterminate,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002627 * false if not.
2628 */
Jim Millere667a7a2012-08-09 19:22:32 -07002629 public void setProgressBar(int viewId, int max, int progress,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002630 boolean indeterminate) {
2631 setBoolean(viewId, "setIndeterminate", indeterminate);
2632 if (!indeterminate) {
2633 setInt(viewId, "setMax", max);
2634 setInt(viewId, "setProgress", progress);
2635 }
2636 }
Jim Millere667a7a2012-08-09 19:22:32 -07002637
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002638 /**
2639 * Equivalent to calling
2640 * {@link android.view.View#setOnClickListener(android.view.View.OnClickListener)}
2641 * to launch the provided {@link PendingIntent}.
Jim Millere667a7a2012-08-09 19:22:32 -07002642 *
Adam Cohen35ae9ca2010-09-20 15:20:41 -07002643 * When setting the on-click action of items within collections (eg. {@link ListView},
2644 * {@link StackView} etc.), this method will not work. Instead, use {@link
2645 * RemoteViews#setPendingIntentTemplate(int, PendingIntent) in conjunction with
2646 * RemoteViews#setOnClickFillInIntent(int, Intent).
2647 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002648 * @param viewId The id of the view that will trigger the {@link PendingIntent} when clicked
2649 * @param pendingIntent The {@link PendingIntent} to send when user clicks
2650 */
2651 public void setOnClickPendingIntent(int viewId, PendingIntent pendingIntent) {
2652 addAction(new SetOnClickPendingIntent(viewId, pendingIntent));
2653 }
2654
2655 /**
Adam Cohen35ae9ca2010-09-20 15:20:41 -07002656 * When using collections (eg. {@link ListView}, {@link StackView} etc.) in widgets, it is very
2657 * costly to set PendingIntents on the individual items, and is hence not permitted. Instead
2658 * this method should be used to set a single PendingIntent template on the collection, and
2659 * individual items can differentiate their on-click behavior using
2660 * {@link RemoteViews#setOnClickFillInIntent(int, Intent)}.
Adam Cohenca6fd842010-09-03 18:10:35 -07002661 *
2662 * @param viewId The id of the collection who's children will use this PendingIntent template
2663 * when clicked
2664 * @param pendingIntentTemplate The {@link PendingIntent} to be combined with extras specified
2665 * by a child of viewId and executed when that child is clicked
2666 */
2667 public void setPendingIntentTemplate(int viewId, PendingIntent pendingIntentTemplate) {
2668 addAction(new SetPendingIntentTemplate(viewId, pendingIntentTemplate));
2669 }
2670
2671 /**
Adam Cohen35ae9ca2010-09-20 15:20:41 -07002672 * When using collections (eg. {@link ListView}, {@link StackView} etc.) in widgets, it is very
2673 * costly to set PendingIntents on the individual items, and is hence not permitted. Instead
2674 * a single PendingIntent template can be set on the collection, see {@link
2675 * RemoteViews#setPendingIntentTemplate(int, PendingIntent)}, and the individual on-click
2676 * action of a given item can be distinguished by setting a fillInIntent on that item. The
2677 * fillInIntent is then combined with the PendingIntent template in order to determine the final
2678 * intent which will be executed when the item is clicked. This works as follows: any fields
2679 * which are left blank in the PendingIntent template, but are provided by the fillInIntent
2680 * will be overwritten, and the resulting PendingIntent will be used.
2681 *
2682 *
2683 * of the PendingIntent template will then be filled in with the associated fields that are
2684 * set in fillInIntent. See {@link Intent#fillIn(Intent, int)} for more details.
2685 *
2686 * @param viewId The id of the view on which to set the fillInIntent
2687 * @param fillInIntent The intent which will be combined with the parent's PendingIntent
2688 * in order to determine the on-click behavior of the view specified by viewId
2689 */
2690 public void setOnClickFillInIntent(int viewId, Intent fillInIntent) {
2691 addAction(new SetOnClickFillInIntent(viewId, fillInIntent));
2692 }
2693
2694 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002695 * @hide
2696 * Equivalent to calling a combination of {@link Drawable#setAlpha(int)},
2697 * {@link Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)},
2698 * and/or {@link Drawable#setLevel(int)} on the {@link Drawable} of a given
2699 * view.
2700 * <p>
2701 * You can omit specific calls by marking their values with null or -1.
Jim Millere667a7a2012-08-09 19:22:32 -07002702 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002703 * @param viewId The id of the view that contains the target
2704 * {@link Drawable}
2705 * @param targetBackground If true, apply these parameters to the
2706 * {@link Drawable} returned by
2707 * {@link android.view.View#getBackground()}. Otherwise, assume
2708 * the target view is an {@link ImageView} and apply them to
2709 * {@link ImageView#getDrawable()}.
2710 * @param alpha Specify an alpha value for the drawable, or -1 to leave
2711 * unchanged.
2712 * @param colorFilter Specify a color for a
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +02002713 * {@link android.graphics.ColorFilter} for this drawable. This will be ignored if
2714 * {@code mode} is {@code null}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002715 * @param mode Specify a PorterDuff mode for this drawable, or null to leave
2716 * unchanged.
2717 * @param level Specify the level for the drawable, or -1 to leave
2718 * unchanged.
2719 */
2720 public void setDrawableParameters(int viewId, boolean targetBackground, int alpha,
2721 int colorFilter, PorterDuff.Mode mode, int level) {
2722 addAction(new SetDrawableParameters(viewId, targetBackground, alpha,
2723 colorFilter, mode, level));
2724 }
2725
2726 /**
Jorim Jaggief72a192014-08-26 21:57:46 +02002727 * @hide
2728 * Equivalent to calling {@link android.widget.ProgressBar#setProgressTintList}.
2729 *
2730 * @param viewId The id of the view whose tint should change
2731 * @param tint the tint to apply, may be {@code null} to clear tint
2732 */
2733 public void setProgressTintList(int viewId, ColorStateList tint) {
2734 addAction(new ReflectionAction(viewId, "setProgressTintList",
2735 ReflectionAction.COLOR_STATE_LIST, tint));
2736 }
2737
2738 /**
2739 * @hide
2740 * Equivalent to calling {@link android.widget.ProgressBar#setProgressBackgroundTintList}.
2741 *
2742 * @param viewId The id of the view whose tint should change
2743 * @param tint the tint to apply, may be {@code null} to clear tint
2744 */
2745 public void setProgressBackgroundTintList(int viewId, ColorStateList tint) {
2746 addAction(new ReflectionAction(viewId, "setProgressBackgroundTintList",
2747 ReflectionAction.COLOR_STATE_LIST, tint));
2748 }
2749
2750 /**
2751 * @hide
2752 * Equivalent to calling {@link android.widget.ProgressBar#setIndeterminateTintList}.
2753 *
2754 * @param viewId The id of the view whose tint should change
2755 * @param tint the tint to apply, may be {@code null} to clear tint
2756 */
2757 public void setProgressIndeterminateTintList(int viewId, ColorStateList tint) {
2758 addAction(new ReflectionAction(viewId, "setIndeterminateTintList",
2759 ReflectionAction.COLOR_STATE_LIST, tint));
2760 }
2761
2762 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002763 * Equivalent to calling {@link android.widget.TextView#setTextColor(int)}.
Jim Millere667a7a2012-08-09 19:22:32 -07002764 *
Scott Main93dc6422012-02-24 12:04:06 -08002765 * @param viewId The id of the view whose text color should change
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002766 * @param color Sets the text color for all the states (normal, selected,
2767 * focused) to be this color.
2768 */
Tor Norbye80756e32015-03-02 09:39:27 -08002769 public void setTextColor(int viewId, @ColorInt int color) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002770 setInt(viewId, "setTextColor", color);
2771 }
2772
Joe Onorato592d0652009-03-24 22:25:52 -07002773 /**
Adam Cohen3b4ca102012-12-14 12:00:41 -08002774 * Equivalent to calling {@link android.widget.AbsListView#setRemoteViewsAdapter(Intent)}.
Winson Chung499cb9f2010-07-16 11:18:17 -07002775 *
Winson Chung037300b2011-03-29 15:40:16 -07002776 * @param appWidgetId The id of the app widget which contains the specified view. (This
2777 * parameter is ignored in this deprecated method)
Adam Cohen50f3d1b2012-12-11 18:36:07 -08002778 * @param viewId The id of the {@link AdapterView}
Winson Chung037300b2011-03-29 15:40:16 -07002779 * @param intent The intent of the service which will be
2780 * providing data to the RemoteViewsAdapter
2781 * @deprecated This method has been deprecated. See
2782 * {@link android.widget.RemoteViews#setRemoteAdapter(int, Intent)}
2783 */
2784 @Deprecated
2785 public void setRemoteAdapter(int appWidgetId, int viewId, Intent intent) {
2786 setRemoteAdapter(viewId, intent);
2787 }
2788
2789 /**
2790 * Equivalent to calling {@link android.widget.AbsListView#setRemoteViewsAdapter(Intent)}.
2791 * Can only be used for App Widgets.
2792 *
Adam Cohen50f3d1b2012-12-11 18:36:07 -08002793 * @param viewId The id of the {@link AdapterView}
Winson Chung81f39eb2011-01-11 18:05:01 -08002794 * @param intent The intent of the service which will be
2795 * providing data to the RemoteViewsAdapter
2796 */
Winson Chung037300b2011-03-29 15:40:16 -07002797 public void setRemoteAdapter(int viewId, Intent intent) {
2798 addAction(new SetRemoteViewsAdapterIntent(viewId, intent));
Winson Chung499cb9f2010-07-16 11:18:17 -07002799 }
2800
2801 /**
Adam Cohen50f3d1b2012-12-11 18:36:07 -08002802 * Creates a simple Adapter for the viewId specified. The viewId must point to an AdapterView,
2803 * ie. {@link ListView}, {@link GridView}, {@link StackView} or {@link AdapterViewAnimator}.
2804 * This is a simpler but less flexible approach to populating collection widgets. Its use is
2805 * encouraged for most scenarios, as long as the total memory within the list of RemoteViews
2806 * is relatively small (ie. doesn't contain large or numerous Bitmaps, see {@link
2807 * RemoteViews#setImageViewBitmap}). In the case of numerous images, the use of API is still
2808 * possible by setting image URIs instead of Bitmaps, see {@link RemoteViews#setImageViewUri}.
2809 *
2810 * This API is supported in the compatibility library for previous API levels, see
2811 * RemoteViewsCompat.
2812 *
2813 * @param viewId The id of the {@link AdapterView}
2814 * @param list The list of RemoteViews which will populate the view specified by viewId.
Adam Cohenb00d9f02013-01-10 14:12:52 -08002815 * @param viewTypeCount The maximum number of unique layout id's used to construct the list of
2816 * RemoteViews. This count cannot change during the life-cycle of a given widget, so this
2817 * parameter should account for the maximum possible number of types that may appear in the
2818 * See {@link Adapter#getViewTypeCount()}.
Adam Cohen33f3aab2013-04-17 13:48:17 -07002819 *
2820 * @hide
Adam Cohen50f3d1b2012-12-11 18:36:07 -08002821 */
Adam Cohenb00d9f02013-01-10 14:12:52 -08002822 public void setRemoteAdapter(int viewId, ArrayList<RemoteViews> list, int viewTypeCount) {
2823 addAction(new SetRemoteViewsAdapterList(viewId, list, viewTypeCount));
Adam Cohen50f3d1b2012-12-11 18:36:07 -08002824 }
2825
2826 /**
Winson Chung499cb9f2010-07-16 11:18:17 -07002827 * 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 Chung499cb9f2010-07-16 11:18:17 -07002830 * @param position Scroll to this adapter position
2831 */
2832 public void setScrollPosition(int viewId, int position) {
2833 setInt(viewId, "smoothScrollToPosition", position);
2834 }
2835
2836 /**
2837 * Equivalent to calling {@link android.widget.AbsListView#smoothScrollToPosition(int, int)}.
2838 *
Scott Main93dc6422012-02-24 12:04:06 -08002839 * @param viewId The id of the view to change
Winson Chung95362592010-07-19 16:05:50 -07002840 * @param offset Scroll by this adapter position offset
Winson Chung499cb9f2010-07-16 11:18:17 -07002841 */
2842 public void setRelativeScrollPosition(int viewId, int offset) {
2843 setInt(viewId, "smoothScrollByOffset", offset);
2844 }
2845
2846 /**
Daniel Sandlerd5353b42012-06-21 09:28:07 -04002847 * Equivalent to calling {@link android.view.View#setPadding(int, int, int, int)}.
Daniel Sandler99d1f742012-05-21 16:14:14 -04002848 *
2849 * @param viewId The id of the view to change
2850 * @param left the left padding in pixels
2851 * @param top the top padding in pixels
2852 * @param right the right padding in pixels
2853 * @param bottom the bottom padding in pixels
2854 */
2855 public void setViewPadding(int viewId, int left, int top, int right, int bottom) {
2856 addAction(new ViewPaddingAction(viewId, left, top, right, bottom));
2857 }
2858
2859 /**
Selim Cinek65b2e7c2015-10-26 14:11:31 -07002860 * @hide
2861 * Equivalent to calling {@link android.view.ViewGroup.MarginLayoutParams#setMarginEnd(int)}.
2862 * Only works if the {@link View#getLayoutParams()} supports margins.
2863 * Hidden for now since we don't want to support this for all different layout margins yet.
2864 *
2865 * @param viewId The id of the view to change
2866 * @param endMargin the left padding in pixels
2867 */
2868 public void setViewLayoutMarginEnd(int viewId, int endMargin) {
Adrian Roos9b123cf2016-02-04 14:55:57 -08002869 addAction(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_MARGIN_END, endMargin));
2870 }
2871
2872 /**
2873 * Equivalent to setting {@link android.view.ViewGroup.LayoutParams#width}.
2874 * @hide
2875 */
2876 public void setViewLayoutWidth(int viewId, int layoutWidth) {
2877 mActions.add(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_WIDTH, layoutWidth));
Selim Cinek65b2e7c2015-10-26 14:11:31 -07002878 }
2879
2880 /**
Joe Onorato592d0652009-03-24 22:25:52 -07002881 * Call a method taking one boolean on a view in the layout for this RemoteViews.
2882 *
Scott Main93dc6422012-02-24 12:04:06 -08002883 * @param viewId The id of the view on which to call the method.
Joe Onorato592d0652009-03-24 22:25:52 -07002884 * @param methodName The name of the method to call.
2885 * @param value The value to pass to the method.
2886 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002887 public void setBoolean(int viewId, String methodName, boolean value) {
2888 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.BOOLEAN, value));
2889 }
2890
Joe Onorato592d0652009-03-24 22:25:52 -07002891 /**
2892 * Call a method taking one byte on a view in the layout for this RemoteViews.
2893 *
Scott Main93dc6422012-02-24 12:04:06 -08002894 * @param viewId The id of the view on which to call the method.
Joe Onorato592d0652009-03-24 22:25:52 -07002895 * @param methodName The name of the method to call.
2896 * @param value The value to pass to the method.
2897 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002898 public void setByte(int viewId, String methodName, byte value) {
2899 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.BYTE, value));
2900 }
2901
Joe Onorato592d0652009-03-24 22:25:52 -07002902 /**
2903 * Call a method taking one short on a view in the layout for this RemoteViews.
2904 *
Scott Main93dc6422012-02-24 12:04:06 -08002905 * @param viewId The id of the view on which to call the method.
Joe Onorato592d0652009-03-24 22:25:52 -07002906 * @param methodName The name of the method to call.
2907 * @param value The value to pass to the method.
2908 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002909 public void setShort(int viewId, String methodName, short value) {
2910 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.SHORT, value));
2911 }
2912
Joe Onorato592d0652009-03-24 22:25:52 -07002913 /**
2914 * Call a method taking one int on a view in the layout for this RemoteViews.
2915 *
Scott Main93dc6422012-02-24 12:04:06 -08002916 * @param viewId The id of the view on which to call the method.
Joe Onorato592d0652009-03-24 22:25:52 -07002917 * @param methodName The name of the method to call.
2918 * @param value The value to pass to the method.
2919 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002920 public void setInt(int viewId, String methodName, int value) {
2921 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.INT, value));
2922 }
2923
Joe Onorato592d0652009-03-24 22:25:52 -07002924 /**
2925 * Call a method taking one long on a view in the layout for this RemoteViews.
2926 *
Scott Main93dc6422012-02-24 12:04:06 -08002927 * @param viewId The id of the view on which to call the method.
Joe Onorato592d0652009-03-24 22:25:52 -07002928 * @param methodName The name of the method to call.
2929 * @param value The value to pass to the method.
2930 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002931 public void setLong(int viewId, String methodName, long value) {
2932 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.LONG, value));
2933 }
2934
Joe Onorato592d0652009-03-24 22:25:52 -07002935 /**
2936 * Call a method taking one float on a view in the layout for this RemoteViews.
2937 *
Scott Main93dc6422012-02-24 12:04:06 -08002938 * @param viewId The id of the view on which to call the method.
Joe Onorato592d0652009-03-24 22:25:52 -07002939 * @param methodName The name of the method to call.
2940 * @param value The value to pass to the method.
2941 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002942 public void setFloat(int viewId, String methodName, float value) {
2943 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.FLOAT, value));
2944 }
2945
Joe Onorato592d0652009-03-24 22:25:52 -07002946 /**
2947 * Call a method taking one double on a view in the layout for this RemoteViews.
2948 *
Scott Main93dc6422012-02-24 12:04:06 -08002949 * @param viewId The id of the view on which to call the method.
Joe Onorato592d0652009-03-24 22:25:52 -07002950 * @param methodName The name of the method to call.
2951 * @param value The value to pass to the method.
2952 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002953 public void setDouble(int viewId, String methodName, double value) {
2954 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.DOUBLE, value));
2955 }
2956
Joe Onorato592d0652009-03-24 22:25:52 -07002957 /**
2958 * Call a method taking one char on a view in the layout for this RemoteViews.
2959 *
Scott Main93dc6422012-02-24 12:04:06 -08002960 * @param viewId The id of the view on which to call the method.
Joe Onorato592d0652009-03-24 22:25:52 -07002961 * @param methodName The name of the method to call.
2962 * @param value The value to pass to the method.
2963 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002964 public void setChar(int viewId, String methodName, char value) {
2965 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.CHAR, value));
2966 }
2967
Joe Onorato592d0652009-03-24 22:25:52 -07002968 /**
2969 * Call a method taking one String on a view in the layout for this RemoteViews.
2970 *
Scott Main93dc6422012-02-24 12:04:06 -08002971 * @param viewId The id of the view on which to call the method.
Joe Onorato592d0652009-03-24 22:25:52 -07002972 * @param methodName The name of the method to call.
2973 * @param value The value to pass to the method.
2974 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002975 public void setString(int viewId, String methodName, String value) {
2976 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.STRING, value));
2977 }
2978
Joe Onorato592d0652009-03-24 22:25:52 -07002979 /**
2980 * Call a method taking one CharSequence on a view in the layout for this RemoteViews.
2981 *
Scott Main93dc6422012-02-24 12:04:06 -08002982 * @param viewId The id of the view on which to call the method.
Joe Onorato592d0652009-03-24 22:25:52 -07002983 * @param methodName The name of the method to call.
2984 * @param value The value to pass to the method.
2985 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002986 public void setCharSequence(int viewId, String methodName, CharSequence value) {
2987 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.CHAR_SEQUENCE, value));
2988 }
2989
Joe Onorato592d0652009-03-24 22:25:52 -07002990 /**
2991 * Call a method taking one Uri on a view in the layout for this RemoteViews.
2992 *
Scott Main93dc6422012-02-24 12:04:06 -08002993 * @param viewId The id of the view on which to call the method.
Joe Onorato592d0652009-03-24 22:25:52 -07002994 * @param methodName The name of the method to call.
2995 * @param value The value to pass to the method.
2996 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002997 public void setUri(int viewId, String methodName, Uri value) {
Jeff Sharkeya14acd22013-04-02 18:27:45 -07002998 if (value != null) {
2999 // Resolve any filesystem path before sending remotely
3000 value = value.getCanonicalUri();
3001 if (StrictMode.vmFileUriExposureEnabled()) {
3002 value.checkFileUriExposed("RemoteViews.setUri()");
3003 }
3004 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003005 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.URI, value));
3006 }
3007
Joe Onorato592d0652009-03-24 22:25:52 -07003008 /**
3009 * Call a method taking one Bitmap on a view in the layout for this RemoteViews.
3010 * @more
3011 * <p class="note">The bitmap will be flattened into the parcel if this object is
3012 * sent across processes, so it may end up using a lot of memory, and may be fairly slow.</p>
3013 *
Scott Main93dc6422012-02-24 12:04:06 -08003014 * @param viewId The id of the view on which to call the method.
Joe Onorato592d0652009-03-24 22:25:52 -07003015 * @param methodName The name of the method to call.
3016 * @param value The value to pass to the method.
3017 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003018 public void setBitmap(int viewId, String methodName, Bitmap value) {
Adam Cohen5d200642012-04-24 10:43:31 -07003019 addAction(new BitmapReflectionAction(viewId, methodName, value));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003020 }
3021
3022 /**
Bjorn Bringertd755b062010-01-06 17:15:37 +00003023 * Call a method taking one Bundle on a view in the layout for this RemoteViews.
3024 *
Scott Main93dc6422012-02-24 12:04:06 -08003025 * @param viewId The id of the view on which to call the method.
Bjorn Bringertd755b062010-01-06 17:15:37 +00003026 * @param methodName The name of the method to call.
3027 * @param value The value to pass to the method.
3028 */
3029 public void setBundle(int viewId, String methodName, Bundle value) {
3030 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.BUNDLE, value));
3031 }
3032
3033 /**
Scott Main93dc6422012-02-24 12:04:06 -08003034 * Call a method taking one Intent on a view in the layout for this RemoteViews.
Winson Chung499cb9f2010-07-16 11:18:17 -07003035 *
Scott Main93dc6422012-02-24 12:04:06 -08003036 * @param viewId The id of the view on which to call the method.
3037 * @param methodName The name of the method to call.
3038 * @param value The {@link android.content.Intent} to pass the method.
Winson Chung499cb9f2010-07-16 11:18:17 -07003039 */
3040 public void setIntent(int viewId, String methodName, Intent value) {
3041 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.INTENT, value));
3042 }
3043
3044 /**
Dan Sandlera22a3802015-05-13 00:12:47 -04003045 * Call a method taking one Icon on a view in the layout for this RemoteViews.
3046 *
3047 * @param viewId The id of the view on which to call the method.
3048 * @param methodName The name of the method to call.
3049 * @param value The {@link android.graphics.drawable.Icon} to pass the method.
3050 */
3051 public void setIcon(int viewId, String methodName, Icon value) {
3052 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.ICON, value));
3053 }
3054
3055 /**
Svetoslav Ganov33aef982012-09-13 12:49:03 -07003056 * Equivalent to calling View.setContentDescription(CharSequence).
Svetoslav Ganove261e282011-10-18 17:47:04 -07003057 *
Svetoslav Ganov33aef982012-09-13 12:49:03 -07003058 * @param viewId The id of the view whose content description should change.
3059 * @param contentDescription The new content description for the view.
Svetoslav Ganove261e282011-10-18 17:47:04 -07003060 */
3061 public void setContentDescription(int viewId, CharSequence contentDescription) {
3062 setCharSequence(viewId, "setContentDescription", contentDescription);
3063 }
3064
Svetoslav Ganov33aef982012-09-13 12:49:03 -07003065 /**
Svetoslav6c702902014-10-09 18:40:56 -07003066 * Equivalent to calling {@link android.view.View#setAccessibilityTraversalBefore(int)}.
3067 *
3068 * @param viewId The id of the view whose before view in accessibility traversal to set.
3069 * @param nextId The id of the next in the accessibility traversal.
3070 **/
3071 public void setAccessibilityTraversalBefore(int viewId, int nextId) {
3072 setInt(viewId, "setAccessibilityTraversalBefore", nextId);
3073 }
3074
3075 /**
3076 * Equivalent to calling {@link android.view.View#setAccessibilityTraversalAfter(int)}.
3077 *
3078 * @param viewId The id of the view whose after view in accessibility traversal to set.
3079 * @param nextId The id of the next in the accessibility traversal.
3080 **/
3081 public void setAccessibilityTraversalAfter(int viewId, int nextId) {
3082 setInt(viewId, "setAccessibilityTraversalAfter", nextId);
3083 }
3084
3085 /**
Svetoslav Ganov33aef982012-09-13 12:49:03 -07003086 * Equivalent to calling View.setLabelFor(int).
3087 *
3088 * @param viewId The id of the view whose property to set.
3089 * @param labeledId The id of a view for which this view serves as a label.
3090 */
3091 public void setLabelFor(int viewId, int labeledId) {
3092 setInt(viewId, "setLabelFor", labeledId);
3093 }
3094
Adam Cohen5d200642012-04-24 10:43:31 -07003095 private RemoteViews getRemoteViewsToApply(Context context) {
3096 if (hasLandscapeAndPortraitLayouts()) {
3097 int orientation = context.getResources().getConfiguration().orientation;
3098 if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
3099 return mLandscape;
3100 } else {
3101 return mPortrait;
3102 }
3103 }
3104 return this;
3105 }
3106
Svetoslav Ganove261e282011-10-18 17:47:04 -07003107 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003108 * Inflates the view hierarchy represented by this object and applies
3109 * all of the actions.
Jim Millere667a7a2012-08-09 19:22:32 -07003110 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003111 * <p><strong>Caller beware: this may throw</strong>
Jim Millere667a7a2012-08-09 19:22:32 -07003112 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003113 * @param context Default context to use
3114 * @param parent Parent that the resulting view hierarchy will be attached to. This method
3115 * does <strong>not</strong> attach the hierarchy. The caller should do so when appropriate.
3116 * @return The inflated view hierarchy
3117 */
3118 public View apply(Context context, ViewGroup parent) {
Jim Millere667a7a2012-08-09 19:22:32 -07003119 return apply(context, parent, null);
Dianne Hackborn1927ae82012-06-22 15:21:36 -07003120 }
3121
Dianne Hackborna1940212012-06-28 16:07:22 -07003122 /** @hide */
3123 public View apply(Context context, ViewGroup parent, OnClickHandler handler) {
Adam Cohen5d200642012-04-24 10:43:31 -07003124 RemoteViews rvToApply = getRemoteViewsToApply(context);
3125
Sunny Goyaldd292f42015-12-02 14:29:27 -08003126 View result = inflateView(context, rvToApply, parent);
3127 loadTransitionOverride(context, handler);
3128
3129 rvToApply.performApply(result, parent, handler);
3130
3131 return result;
3132 }
3133
3134 private View inflateView(Context context, RemoteViews rv, ViewGroup parent) {
Kenny Guy77320062014-08-27 21:37:15 +01003135 // RemoteViews may be built by an application installed in another
3136 // user. So build a context that loads resources from that user but
3137 // still returns the current users userId so settings like data / time formats
3138 // are loaded without requiring cross user persmissions.
3139 final Context contextForResources = getContextForResources(context);
3140 Context inflationContext = new ContextWrapper(context) {
3141 @Override
3142 public Resources getResources() {
3143 return contextForResources.getResources();
3144 }
3145 @Override
3146 public Resources.Theme getTheme() {
3147 return contextForResources.getTheme();
3148 }
Dan Sandler706274f2015-07-30 22:32:54 -04003149 @Override
3150 public String getPackageName() {
3151 return contextForResources.getPackageName();
3152 }
Kenny Guy77320062014-08-27 21:37:15 +01003153 };
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003154
Romain Guya5475592009-07-01 17:20:08 -07003155 LayoutInflater inflater = (LayoutInflater)
Kenny Guy77320062014-08-27 21:37:15 +01003156 context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003157
Kenny Guy77320062014-08-27 21:37:15 +01003158 // Clone inflater so we load resources from correct context and
3159 // we don't add a filter to the static version returned by getSystemService.
3160 inflater = inflater.cloneInContext(inflationContext);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003161 inflater.setFilter(this);
Sunny Goyaldd292f42015-12-02 14:29:27 -08003162 return inflater.inflate(rv.getLayoutId(), parent, false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003163 }
Adam Cohen5d200642012-04-24 10:43:31 -07003164
Gus Prevas1ed322b2015-09-17 17:34:46 -04003165 private static void loadTransitionOverride(Context context,
3166 RemoteViews.OnClickHandler handler) {
3167 if (handler != null && context.getResources().getBoolean(
3168 com.android.internal.R.bool.config_overrideRemoteViewsActivityTransition)) {
3169 TypedArray windowStyle = context.getTheme().obtainStyledAttributes(
3170 com.android.internal.R.styleable.Window);
3171 int windowAnimations = windowStyle.getResourceId(
3172 com.android.internal.R.styleable.Window_windowAnimationStyle, 0);
3173 TypedArray windowAnimationStyle = context.obtainStyledAttributes(
3174 windowAnimations, com.android.internal.R.styleable.WindowAnimation);
3175 handler.setEnterAnimationId(windowAnimationStyle.getResourceId(
3176 com.android.internal.R.styleable.
3177 WindowAnimation_activityOpenRemoteViewsEnterAnimation, 0));
3178 windowStyle.recycle();
3179 windowAnimationStyle.recycle();
3180 }
3181 }
3182
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003183 /**
Sunny Goyaldd292f42015-12-02 14:29:27 -08003184 * Implement this interface to receive a callback when
3185 * {@link #applyAsync} or {@link #reapplyAsync} is finished.
3186 * @hide
3187 */
3188 public interface OnViewAppliedListener {
3189 void onViewApplied(View v);
3190
3191 void onError(Exception e);
3192 }
3193
3194 /**
3195 * Applies the views asynchronously, moving as much of the task on the background
3196 * thread as possible.
3197 *
3198 * @see {@link #apply(Context, ViewGroup)}
3199 * @param context Default context to use
3200 * @param parent Parent that the resulting view hierarchy will be attached to. This method
3201 * does <strong>not</strong> attach the hierarchy. The caller should do so when appropriate.
3202 * @param listener the callback to run when all actions have been applied. May be null.
3203 * @param executor The executor to use. If null {@link AsyncTask#THREAD_POOL_EXECUTOR} is used.
3204 * @return CancellationSignal
3205 * @hide
3206 */
3207 public CancellationSignal applyAsync(
3208 Context context, ViewGroup parent, Executor executor, OnViewAppliedListener listener) {
3209 return applyAsync(context, parent, executor, listener, null);
3210 }
3211
3212 private CancellationSignal startTaskOnExecutor(AsyncApplyTask task, Executor executor) {
3213 CancellationSignal cancelSignal = new CancellationSignal();
3214 cancelSignal.setOnCancelListener(task);
3215
3216 task.executeOnExecutor(executor == null ? AsyncTask.THREAD_POOL_EXECUTOR : executor);
3217 return cancelSignal;
3218 }
3219
3220 /** @hide */
3221 public CancellationSignal applyAsync(Context context, ViewGroup parent,
3222 Executor executor, OnViewAppliedListener listener, OnClickHandler handler) {
3223 return startTaskOnExecutor(getAsyncApplyTask(context, parent, listener, handler), executor);
3224 }
3225
3226 private AsyncApplyTask getAsyncApplyTask(Context context, ViewGroup parent,
3227 OnViewAppliedListener listener, OnClickHandler handler) {
3228 return new AsyncApplyTask(getRemoteViewsToApply(context), parent, context, listener,
3229 handler, null);
3230 }
3231
3232 private class AsyncApplyTask extends AsyncTask<Void, Void, ViewTree>
3233 implements CancellationSignal.OnCancelListener {
3234 final RemoteViews mRV;
3235 final ViewGroup mParent;
3236 final Context mContext;
3237 final OnViewAppliedListener mListener;
3238 final OnClickHandler mHandler;
3239
3240 private View mResult;
3241 private ViewTree mTree;
3242 private Action[] mActions;
3243 private Exception mError;
3244
3245 private AsyncApplyTask(
3246 RemoteViews rv, ViewGroup parent, Context context, OnViewAppliedListener listener,
3247 OnClickHandler handler, View result) {
3248 mRV = rv;
3249 mParent = parent;
3250 mContext = context;
3251 mListener = listener;
3252 mHandler = handler;
3253
3254 mResult = result;
3255 loadTransitionOverride(context, handler);
3256 }
3257
3258 @Override
3259 protected ViewTree doInBackground(Void... params) {
3260 try {
3261 if (mResult == null) {
3262 mResult = inflateView(mContext, mRV, mParent);
3263 }
3264
3265 mTree = new ViewTree(mResult);
3266 if (mRV.mActions != null) {
3267 int count = mRV.mActions.size();
3268 mActions = new Action[count];
3269 for (int i = 0; i < count && !isCancelled(); i++) {
3270 // TODO: check if isCanclled in nested views.
3271 mActions[i] = mRV.mActions.get(i).initActionAsync(mTree, mParent, mHandler);
3272 }
3273 } else {
3274 mActions = null;
3275 }
3276 return mTree;
3277 } catch (Exception e) {
3278 mError = e;
3279 return null;
3280 }
3281 }
3282
3283 @Override
3284 protected void onPostExecute(ViewTree viewTree) {
3285 if (mError == null) {
3286 try {
3287 if (mActions != null) {
3288 OnClickHandler handler = mHandler == null
3289 ? DEFAULT_ON_CLICK_HANDLER : mHandler;
3290 for (Action a : mActions) {
3291 a.apply(viewTree.mRoot, mParent, handler);
3292 }
3293 }
3294 } catch (Exception e) {
3295 mError = e;
3296 }
3297 }
3298
3299 if (mListener != null) {
3300 if (mError != null) {
3301 mListener.onError(mError);
3302 } else {
3303 mListener.onViewApplied(viewTree.mRoot);
3304 }
3305 } else if (mError != null) {
3306 if (mError instanceof ActionException) {
3307 throw (ActionException) mError;
3308 } else {
3309 throw new ActionException(mError);
3310 }
3311 }
3312 }
3313
3314 @Override
3315 public void onCancel() {
3316 cancel(true);
3317 }
3318 }
3319
3320 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003321 * Applies all of the actions to the provided view.
3322 *
3323 * <p><strong>Caller beware: this may throw</strong>
Jim Millere667a7a2012-08-09 19:22:32 -07003324 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003325 * @param v The view to apply the actions to. This should be the result of
3326 * the {@link #apply(Context,ViewGroup)} call.
3327 */
3328 public void reapply(Context context, View v) {
Jim Millere667a7a2012-08-09 19:22:32 -07003329 reapply(context, v, null);
Dianne Hackborna1940212012-06-28 16:07:22 -07003330 }
3331
3332 /** @hide */
3333 public void reapply(Context context, View v, OnClickHandler handler) {
Adam Cohen5d200642012-04-24 10:43:31 -07003334 RemoteViews rvToApply = getRemoteViewsToApply(context);
3335
3336 // In the case that a view has this RemoteViews applied in one orientation, is persisted
3337 // across orientation change, and has the RemoteViews re-applied in the new orientation,
3338 // we throw an exception, since the layouts may be completely unrelated.
3339 if (hasLandscapeAndPortraitLayouts()) {
3340 if (v.getId() != rvToApply.getLayoutId()) {
3341 throw new RuntimeException("Attempting to re-apply RemoteViews to a view that" +
3342 " that does not share the same root layout id.");
3343 }
3344 }
3345
Dianne Hackborna1940212012-06-28 16:07:22 -07003346 rvToApply.performApply(v, (ViewGroup) v.getParent(), handler);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003347 }
3348
Sunny Goyaldd292f42015-12-02 14:29:27 -08003349 /**
3350 * Applies all the actions to the provided view, moving as much of the task on the background
3351 * thread as possible.
3352 *
3353 * @see {@link #reapply(Context, View)}
3354 * @param context Default context to use
3355 * @param v The view to apply the actions to. This should be the result of
3356 * the {@link #apply(Context,ViewGroup)} call.
3357 * @param listener the callback to run when all actions have been applied. May be null.
3358 * @param executor The executor to use. If null {@link AsyncTask#THREAD_POOL_EXECUTOR} is used
3359 * @return CancellationSignal
3360 * @hide
3361 */
3362 public CancellationSignal reapplyAsync(
3363 Context context, View v, Executor executor, OnViewAppliedListener listener) {
3364 return reapplyAsync(context, v, executor, listener, null);
3365 }
3366
3367 /** @hide */
3368 public CancellationSignal reapplyAsync(Context context, View v, Executor executor,
3369 OnViewAppliedListener listener, OnClickHandler handler) {
3370 RemoteViews rvToApply = getRemoteViewsToApply(context);
3371
3372 // In the case that a view has this RemoteViews applied in one orientation, is persisted
3373 // across orientation change, and has the RemoteViews re-applied in the new orientation,
3374 // we throw an exception, since the layouts may be completely unrelated.
3375 if (hasLandscapeAndPortraitLayouts()) {
3376 if (v.getId() != rvToApply.getLayoutId()) {
3377 throw new RuntimeException("Attempting to re-apply RemoteViews to a view that" +
3378 " that does not share the same root layout id.");
3379 }
3380 }
3381
3382 return startTaskOnExecutor(new AsyncApplyTask(rvToApply, (ViewGroup) v.getParent(),
3383 context, listener, handler, v), executor);
3384 }
3385
Dianne Hackborna1940212012-06-28 16:07:22 -07003386 private void performApply(View v, ViewGroup parent, OnClickHandler handler) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003387 if (mActions != null) {
Jim Millere667a7a2012-08-09 19:22:32 -07003388 handler = handler == null ? DEFAULT_ON_CLICK_HANDLER : handler;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003389 final int count = mActions.size();
3390 for (int i = 0; i < count; i++) {
3391 Action a = mActions.get(i);
Dianne Hackborna1940212012-06-28 16:07:22 -07003392 a.apply(v, parent, handler);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003393 }
3394 }
3395 }
3396
Kenny Guy77320062014-08-27 21:37:15 +01003397 private Context getContextForResources(Context context) {
Svetoslav976e8bd2014-07-16 15:12:03 -07003398 if (mApplication != null) {
3399 if (context.getUserId() == UserHandle.getUserId(mApplication.uid)
3400 && context.getPackageName().equals(mApplication.packageName)) {
3401 return context;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003402 }
Svetoslav976e8bd2014-07-16 15:12:03 -07003403 try {
3404 return context.createApplicationContext(mApplication,
3405 Context.CONTEXT_RESTRICTED);
3406 } catch (NameNotFoundException e) {
Svet Ganov0da85b62014-08-06 14:11:37 -07003407 Log.e(LOG_TAG, "Package name " + mApplication.packageName + " not found");
Svetoslav976e8bd2014-07-16 15:12:03 -07003408 }
3409 }
3410
3411 return context;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003412 }
3413
Christoph Studer4600f9b2014-07-22 22:44:43 +02003414 /**
3415 * Returns the number of actions in this RemoteViews. Can be used as a sequence number.
3416 *
3417 * @hide
3418 */
3419 public int getSequenceNumber() {
3420 return (mActions == null) ? 0 : mActions.size();
3421 }
3422
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003423 /* (non-Javadoc)
3424 * Used to restrict the views which can be inflated
Jim Millere667a7a2012-08-09 19:22:32 -07003425 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003426 * @see android.view.LayoutInflater.Filter#onLoadClass(java.lang.Class)
3427 */
Gilles Debunnee6ac8b92010-06-17 10:55:04 -07003428 public boolean onLoadClass(Class clazz) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003429 return clazz.isAnnotationPresent(RemoteView.class);
3430 }
Adam Cohen5d200642012-04-24 10:43:31 -07003431
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003432 public int describeContents() {
3433 return 0;
3434 }
3435
3436 public void writeToParcel(Parcel dest, int flags) {
Adam Cohen5d200642012-04-24 10:43:31 -07003437 if (!hasLandscapeAndPortraitLayouts()) {
3438 dest.writeInt(MODE_NORMAL);
3439 // We only write the bitmap cache if we are the root RemoteViews, as this cache
3440 // is shared by all children.
3441 if (mIsRoot) {
3442 mBitmapCache.writeBitmapsToParcel(dest, flags);
3443 }
Svet Ganov0da85b62014-08-06 14:11:37 -07003444 dest.writeParcelable(mApplication, flags);
Adam Cohen5d200642012-04-24 10:43:31 -07003445 dest.writeInt(mLayoutId);
3446 dest.writeInt(mIsWidgetCollectionChild ? 1 : 0);
3447 int count;
3448 if (mActions != null) {
3449 count = mActions.size();
3450 } else {
3451 count = 0;
3452 }
3453 dest.writeInt(count);
3454 for (int i=0; i<count; i++) {
3455 Action a = mActions.get(i);
3456 a.writeToParcel(dest, 0);
3457 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003458 } else {
Adam Cohen5d200642012-04-24 10:43:31 -07003459 dest.writeInt(MODE_HAS_LANDSCAPE_AND_PORTRAIT);
3460 // We only write the bitmap cache if we are the root RemoteViews, as this cache
3461 // is shared by all children.
3462 if (mIsRoot) {
3463 mBitmapCache.writeBitmapsToParcel(dest, flags);
3464 }
3465 mLandscape.writeToParcel(dest, flags);
3466 mPortrait.writeToParcel(dest, flags);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003467 }
Svet Ganov0da85b62014-08-06 14:11:37 -07003468 }
Svetoslav976e8bd2014-07-16 15:12:03 -07003469
Svet Ganov0da85b62014-08-06 14:11:37 -07003470 private static ApplicationInfo getApplicationInfo(String packageName, int userId) {
Svetoslavb6242442014-09-19 13:21:55 -07003471 if (packageName == null) {
3472 return null;
3473 }
3474
Svet Ganov0da85b62014-08-06 14:11:37 -07003475 // Get the application for the passed in package and user.
3476 Application application = ActivityThread.currentApplication();
3477 if (application == null) {
3478 throw new IllegalStateException("Cannot create remote views out of an aplication.");
3479 }
3480
3481 ApplicationInfo applicationInfo = application.getApplicationInfo();
3482 if (UserHandle.getUserId(applicationInfo.uid) != userId
3483 || !applicationInfo.packageName.equals(packageName)) {
3484 try {
Svetoslav14494a82014-08-18 10:43:27 -07003485 Context context = application.getBaseContext().createPackageContextAsUser(
Svet Ganov0da85b62014-08-06 14:11:37 -07003486 packageName, 0, new UserHandle(userId));
3487 applicationInfo = context.getApplicationInfo();
3488 } catch (NameNotFoundException nnfe) {
3489 throw new IllegalArgumentException("No such package " + packageName);
3490 }
3491 }
3492
3493 return applicationInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003494 }
3495
3496 /**
3497 * Parcelable.Creator that instantiates RemoteViews objects
3498 */
3499 public static final Parcelable.Creator<RemoteViews> CREATOR = new Parcelable.Creator<RemoteViews>() {
3500 public RemoteViews createFromParcel(Parcel parcel) {
3501 return new RemoteViews(parcel);
3502 }
3503
3504 public RemoteViews[] newArray(int size) {
3505 return new RemoteViews[size];
3506 }
3507 };
Sunny Goyaldd292f42015-12-02 14:29:27 -08003508
3509 /**
3510 * A representation of the view hierarchy. Only views which have a valid ID are added
3511 * and can be searched.
3512 */
3513 private static class ViewTree {
3514 private final View mRoot;
3515
3516 private ArrayList<ViewTree> mChildren;
3517
3518 private ViewTree(View root) {
3519 mRoot = root;
3520 }
3521
3522 public void createTree() {
3523 if (mChildren != null) {
3524 return;
3525 }
3526
3527 mChildren = new ArrayList<>();
3528 if (mRoot instanceof ViewGroup && mRoot.isRootNamespace()) {
3529 ViewGroup vg = (ViewGroup) mRoot;
3530 int count = vg.getChildCount();
3531 for (int i = 0; i < count; i++) {
3532 addViewChild(vg.getChildAt(i));
3533 }
3534 }
3535 }
3536
3537 public ViewTree findViewTreeById(int id) {
3538 if (mRoot.getId() == id) {
3539 return this;
3540 }
3541 if (mChildren == null) {
3542 return null;
3543 }
3544 for (ViewTree tree : mChildren) {
3545 ViewTree result = tree.findViewTreeById(id);
3546 if (result != null) {
3547 return result;
3548 }
3549 }
3550 return null;
3551 }
3552
3553 public View findViewById(int id) {
3554 if (mChildren == null) {
3555 return mRoot.findViewById(id);
3556 }
3557 ViewTree tree = findViewTreeById(id);
3558 return tree == null ? null : tree.mRoot;
3559 }
3560
3561 public void addChild(ViewTree child) {
3562 if (mChildren == null) {
3563 mChildren = new ArrayList<>();
3564 }
3565 child.createTree();
3566 mChildren.add(child);
3567 }
3568
3569 private void addViewChild(View v) {
3570 final ViewTree target;
3571
3572 // If the view has a valid id, i.e., if can be found using findViewById, add it to the
3573 // tree, otherwise skip this view and add its children instead.
3574 if (v.getId() != 0) {
3575 ViewTree tree = new ViewTree(v);
3576 mChildren.add(tree);
3577 target = tree;
3578 } else {
3579 target = this;
3580 }
3581
3582 if (v instanceof ViewGroup && v.isRootNamespace()) {
3583 if (target.mChildren == null) {
3584 target.mChildren = new ArrayList<>();
3585 ViewGroup vg = (ViewGroup) v;
3586 int count = vg.getChildCount();
3587 for (int i = 0; i < count; i++) {
3588 target.addViewChild(vg.getChildAt(i));
3589 }
3590 }
3591 }
3592 }
3593 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003594}