blob: 700bba816a930b1f526d35e734d82e7e55f1ebec [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2008 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
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070017package android.appwidget;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080018
Adam Cohen5d5f3402011-09-30 15:02:43 -070019import android.content.ComponentName;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080020import android.content.Context;
Adam Cohen5d5f3402011-09-30 15:02:43 -070021import android.content.pm.ApplicationInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080022import android.content.pm.PackageManager;
Jeff Sharkeyb8560b62009-08-06 21:24:25 -070023import android.content.pm.PackageManager.NameNotFoundException;
Adam Cohen5d5f3402011-09-30 15:02:43 -070024import android.content.res.Resources;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080025import android.graphics.Bitmap;
26import android.graphics.Canvas;
27import android.graphics.Color;
28import android.graphics.Paint;
Adam Cohen4c96a592011-11-04 12:12:48 -070029import android.graphics.Rect;
Adam Cohen5d5f3402011-09-30 15:02:43 -070030import android.os.Build;
Adam Cohene8724c82012-04-19 17:11:40 -070031import android.os.Bundle;
Romain Guy09ddc082009-08-11 12:10:08 -070032import android.os.Parcel;
Gilles Debunne30301932010-06-16 18:32:00 -070033import android.os.Parcelable;
Amith Yamasani94022e82012-12-04 11:05:39 -080034import android.os.Process;
Gilles Debunne30301932010-06-16 18:32:00 -070035import android.os.SystemClock;
Amith Yamasani94022e82012-12-04 11:05:39 -080036import android.os.UserHandle;
Jeff Sharkeyb8560b62009-08-06 21:24:25 -070037import android.util.AttributeSet;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080038import android.util.Log;
Romain Guy09ddc082009-08-11 12:10:08 -070039import android.util.SparseArray;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080040import android.view.Gravity;
41import android.view.LayoutInflater;
42import android.view.View;
Guang Zhu1affb932012-05-15 14:25:51 -070043import android.view.accessibility.AccessibilityNodeInfo;
Winson Chung499cb9f2010-07-16 11:18:17 -070044import android.widget.Adapter;
45import android.widget.AdapterView;
46import android.widget.BaseAdapter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080047import android.widget.FrameLayout;
48import android.widget.RemoteViews;
Jim Millere667a7a2012-08-09 19:22:32 -070049import android.widget.RemoteViews.OnClickHandler;
Adam Cohen2148d432011-07-28 14:59:54 -070050import android.widget.RemoteViewsAdapter.RemoteAdapterConnectionCallback;
Adam Cohen4c96a592011-11-04 12:12:48 -070051import android.widget.TextView;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080052
53/**
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070054 * Provides the glue to show AppWidget views. This class offers automatic animation
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080055 * between updates, and will try recycling old views for each incoming
56 * {@link RemoteViews}.
57 */
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070058public class AppWidgetHostView extends FrameLayout {
59 static final String TAG = "AppWidgetHostView";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080060 static final boolean LOGD = false;
61 static final boolean CROSSFADE = false;
62
63 static final int VIEW_MODE_NOINIT = 0;
64 static final int VIEW_MODE_CONTENT = 1;
65 static final int VIEW_MODE_ERROR = 2;
66 static final int VIEW_MODE_DEFAULT = 3;
67
68 static final int FADE_DURATION = 1000;
69
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070070 // When we're inflating the initialLayout for a AppWidget, we only allow
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080071 // views that are allowed in RemoteViews.
72 static final LayoutInflater.Filter sInflaterFilter = new LayoutInflater.Filter() {
Gilles Debunnee6ac8b92010-06-17 10:55:04 -070073 public boolean onLoadClass(Class clazz) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080074 return clazz.isAnnotationPresent(RemoteViews.RemoteView.class);
75 }
76 };
77
78 Context mContext;
Jeff Sharkeyb8560b62009-08-06 21:24:25 -070079 Context mRemoteContext;
80
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070081 int mAppWidgetId;
82 AppWidgetProviderInfo mInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080083 View mView;
84 int mViewMode = VIEW_MODE_NOINIT;
85 int mLayoutId = -1;
86 long mFadeStartTime = -1;
87 Bitmap mOld;
88 Paint mOldPaint = new Paint();
Jim Millere667a7a2012-08-09 19:22:32 -070089 private OnClickHandler mOnClickHandler;
Amith Yamasani94022e82012-12-04 11:05:39 -080090 private UserHandle mUser;
Jim Millere667a7a2012-08-09 19:22:32 -070091
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080092 /**
93 * Create a host view. Uses default fade animations.
94 */
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070095 public AppWidgetHostView(Context context) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080096 this(context, android.R.anim.fade_in, android.R.anim.fade_out);
97 }
98
99 /**
Jim Millere667a7a2012-08-09 19:22:32 -0700100 * @hide
101 */
102 public AppWidgetHostView(Context context, OnClickHandler handler) {
103 this(context, android.R.anim.fade_in, android.R.anim.fade_out);
104 mOnClickHandler = handler;
105 }
106
107 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800108 * Create a host view. Uses specified animations when pushing
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700109 * {@link #updateAppWidget(RemoteViews)}.
Jim Millere667a7a2012-08-09 19:22:32 -0700110 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800111 * @param animationIn Resource ID of in animation to use
112 * @param animationOut Resource ID of out animation to use
113 */
Romain Guya5475592009-07-01 17:20:08 -0700114 @SuppressWarnings({"UnusedDeclaration"})
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700115 public AppWidgetHostView(Context context, int animationIn, int animationOut) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800116 super(context);
117 mContext = context;
Amith Yamasani94022e82012-12-04 11:05:39 -0800118 mUser = Process.myUserHandle();
Adam Cohend1108892011-01-16 19:40:49 -0800119 // We want to segregate the view ids within AppWidgets to prevent
120 // problems when those ids collide with view ids in the AppWidgetHost.
121 setIsRootNamespace(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800122 }
Adam Cohend1108892011-01-16 19:40:49 -0800123
Amith Yamasani94022e82012-12-04 11:05:39 -0800124 /** @hide */
125 public void setUserId(int userId) {
126 mUser = new UserHandle(userId);
127 }
128
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800129 /**
Jim Millere667a7a2012-08-09 19:22:32 -0700130 * Pass the given handler to RemoteViews when updating this widget. Unless this
131 * is done immediatly after construction, a call to {@link #updateAppWidget(RemoteViews)}
132 * should be made.
133 * @param handler
134 * @hide
135 */
136 public void setOnClickHandler(OnClickHandler handler) {
137 mOnClickHandler = handler;
138 }
139
140 /**
Adam Cohen4c96a592011-11-04 12:12:48 -0700141 * Set the AppWidget that will be displayed by this view. This method also adds default padding
142 * to widgets, as described in {@link #getDefaultPaddingForWidget(Context, ComponentName, Rect)}
143 * and can be overridden in order to add custom padding.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800144 */
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700145 public void setAppWidget(int appWidgetId, AppWidgetProviderInfo info) {
146 mAppWidgetId = appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800147 mInfo = info;
Adam Cohen5d5f3402011-09-30 15:02:43 -0700148
149 // Sometimes the AppWidgetManager returns a null AppWidgetProviderInfo object for
150 // a widget, eg. for some widgets in safe mode.
151 if (info != null) {
152 // We add padding to the AppWidgetHostView if necessary
Adam Cohen4c96a592011-11-04 12:12:48 -0700153 Rect padding = getDefaultPaddingForWidget(mContext, info.provider, null);
Adam Cohen5d5f3402011-09-30 15:02:43 -0700154 setPadding(padding.left, padding.top, padding.right, padding.bottom);
Svetoslav Ganovc4842c12012-10-31 14:33:32 -0700155 setContentDescription(info.label);
Adam Cohen5d5f3402011-09-30 15:02:43 -0700156 }
157 }
158
Adam Cohen5d5f3402011-09-30 15:02:43 -0700159 /**
160 * As of ICE_CREAM_SANDWICH we are automatically adding padding to widgets targeting
161 * ICE_CREAM_SANDWICH and higher. The new widget design guidelines strongly recommend
162 * that widget developers do not add extra padding to their widgets. This will help
163 * achieve consistency among widgets.
Adam Cohen4c96a592011-11-04 12:12:48 -0700164 *
165 * Note: this method is only needed by developers of AppWidgetHosts. The method is provided in
166 * order for the AppWidgetHost to account for the automatic padding when computing the number
167 * of cells to allocate to a particular widget.
168 *
169 * @param context the current context
170 * @param component the component name of the widget
171 * @param padding Rect in which to place the output, if null, a new Rect will be allocated and
172 * returned
Adam Cohena21bf0c2012-10-05 18:20:46 -0700173 * @return default padding for this widget, in pixels
Adam Cohen5d5f3402011-09-30 15:02:43 -0700174 */
Adam Cohen4c96a592011-11-04 12:12:48 -0700175 public static Rect getDefaultPaddingForWidget(Context context, ComponentName component,
176 Rect padding) {
177 PackageManager packageManager = context.getPackageManager();
Adam Cohen5d5f3402011-09-30 15:02:43 -0700178 ApplicationInfo appInfo;
179
Adam Cohen4c96a592011-11-04 12:12:48 -0700180 if (padding == null) {
181 padding = new Rect(0, 0, 0, 0);
182 } else {
183 padding.set(0, 0, 0, 0);
184 }
185
Adam Cohen5d5f3402011-09-30 15:02:43 -0700186 try {
187 appInfo = packageManager.getApplicationInfo(component.getPackageName(), 0);
Adam Cohen4c96a592011-11-04 12:12:48 -0700188 } catch (NameNotFoundException e) {
Adam Cohen5d5f3402011-09-30 15:02:43 -0700189 // if we can't find the package, return 0 padding
Adam Cohen4c96a592011-11-04 12:12:48 -0700190 return padding;
Adam Cohen5d5f3402011-09-30 15:02:43 -0700191 }
192
193 if (appInfo.targetSdkVersion >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
Adam Cohen4c96a592011-11-04 12:12:48 -0700194 Resources r = context.getResources();
195 padding.left = r.getDimensionPixelSize(com.android.internal.
Adam Cohen5d5f3402011-09-30 15:02:43 -0700196 R.dimen.default_app_widget_padding_left);
Adam Cohen4c96a592011-11-04 12:12:48 -0700197 padding.right = r.getDimensionPixelSize(com.android.internal.
Adam Cohen5d5f3402011-09-30 15:02:43 -0700198 R.dimen.default_app_widget_padding_right);
Adam Cohen4c96a592011-11-04 12:12:48 -0700199 padding.top = r.getDimensionPixelSize(com.android.internal.
Adam Cohen5d5f3402011-09-30 15:02:43 -0700200 R.dimen.default_app_widget_padding_top);
Adam Cohen4c96a592011-11-04 12:12:48 -0700201 padding.bottom = r.getDimensionPixelSize(com.android.internal.
Adam Cohen5d5f3402011-09-30 15:02:43 -0700202 R.dimen.default_app_widget_padding_bottom);
203 }
Adam Cohen4c96a592011-11-04 12:12:48 -0700204 return padding;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800205 }
Joe Onoratoc27bb552010-06-23 17:44:30 -0700206
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700207 public int getAppWidgetId() {
208 return mAppWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800209 }
Jim Millere667a7a2012-08-09 19:22:32 -0700210
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700211 public AppWidgetProviderInfo getAppWidgetInfo() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800212 return mInfo;
213 }
214
Romain Guy09ddc082009-08-11 12:10:08 -0700215 @Override
216 protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
217 final ParcelableSparseArray jail = new ParcelableSparseArray();
218 super.dispatchSaveInstanceState(jail);
219 container.put(generateId(), jail);
220 }
221
222 private int generateId() {
223 final int id = getId();
224 return id == View.NO_ID ? mAppWidgetId : id;
225 }
226
227 @Override
228 protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
Romain Guyae8dd292009-11-05 13:36:15 -0800229 final Parcelable parcelable = container.get(generateId());
230
231 ParcelableSparseArray jail = null;
232 if (parcelable != null && parcelable instanceof ParcelableSparseArray) {
233 jail = (ParcelableSparseArray) parcelable;
234 }
235
Romain Guya8a720d2009-10-15 15:09:26 -0700236 if (jail == null) jail = new ParcelableSparseArray();
Romain Guyae8dd292009-11-05 13:36:15 -0800237
Adam Cohen6ccf4e22012-09-21 16:09:34 -0700238 try {
239 super.dispatchRestoreInstanceState(jail);
240 } catch (Exception e) {
241 Log.e(TAG, "failed to restoreInstanceState for widget id: " + mAppWidgetId + ", "
242 + (mInfo == null ? "null" : mInfo.provider), e);
243 }
Romain Guy09ddc082009-08-11 12:10:08 -0700244 }
245
Adam Cohene8724c82012-04-19 17:11:40 -0700246 /**
Adam Cohen88f041e2012-05-08 15:32:23 -0700247 * Provide guidance about the size of this widget to the AppWidgetManager. The widths and
248 * heights should correspond to the full area the AppWidgetHostView is given. Padding added by
249 * the framework will be accounted for automatically. This information gets embedded into the
250 * AppWidget options and causes a callback to the AppWidgetProvider.
Adam Cohend2097eb2012-05-01 18:10:28 -0700251 * @see AppWidgetProvider#onAppWidgetOptionsChanged(Context, AppWidgetManager, int, Bundle)
Adam Cohene8724c82012-04-19 17:11:40 -0700252 *
Adam Cohena21bf0c2012-10-05 18:20:46 -0700253 * @param newOptions The bundle of options, in addition to the size information,
Adam Cohenc88d11f2012-04-27 18:33:18 -0700254 * can be null.
Adam Cohen4ddcd572012-11-01 17:36:32 -0700255 * @param minWidth The minimum width in dips that the widget will be displayed at.
256 * @param minHeight The maximum height in dips that the widget will be displayed at.
257 * @param maxWidth The maximum width in dips that the widget will be displayed at.
258 * @param maxHeight The maximum height in dips that the widget will be displayed at.
Adam Cohenc88d11f2012-04-27 18:33:18 -0700259 *
Adam Cohene8724c82012-04-19 17:11:40 -0700260 */
Adam Cohena21bf0c2012-10-05 18:20:46 -0700261 public void updateAppWidgetSize(Bundle newOptions, int minWidth, int minHeight, int maxWidth,
Adam Cohenc88d11f2012-04-27 18:33:18 -0700262 int maxHeight) {
Adam Cohen4ddcd572012-11-01 17:36:32 -0700263 updateAppWidgetSize(newOptions, minWidth, minHeight, maxWidth, maxHeight, false);
264 }
265
266 /**
267 * @hide
268 */
269 public void updateAppWidgetSize(Bundle newOptions, int minWidth, int minHeight, int maxWidth,
270 int maxHeight, boolean ignorePadding) {
Adam Cohena21bf0c2012-10-05 18:20:46 -0700271 if (newOptions == null) {
272 newOptions = new Bundle();
Adam Cohene8724c82012-04-19 17:11:40 -0700273 }
Adam Cohen88f041e2012-05-08 15:32:23 -0700274
275 Rect padding = new Rect();
276 if (mInfo != null) {
277 padding = getDefaultPaddingForWidget(mContext, mInfo.provider, padding);
278 }
Adam Cohenf065c962012-05-09 11:13:54 -0700279 float density = getResources().getDisplayMetrics().density;
Adam Cohen88f041e2012-05-08 15:32:23 -0700280
Adam Cohenf065c962012-05-09 11:13:54 -0700281 int xPaddingDips = (int) ((padding.left + padding.right) / density);
282 int yPaddingDips = (int) ((padding.top + padding.bottom) / density);
Adam Cohen88f041e2012-05-08 15:32:23 -0700283
Adam Cohen4ddcd572012-11-01 17:36:32 -0700284 int newMinWidth = minWidth - (ignorePadding ? 0 : xPaddingDips);
285 int newMinHeight = minHeight - (ignorePadding ? 0 : yPaddingDips);
286 int newMaxWidth = maxWidth - (ignorePadding ? 0 : xPaddingDips);
287 int newMaxHeight = maxHeight - (ignorePadding ? 0 : yPaddingDips);
Adam Cohena21bf0c2012-10-05 18:20:46 -0700288
289 AppWidgetManager widgetManager = AppWidgetManager.getInstance(mContext);
290
291 // We get the old options to see if the sizes have changed
292 Bundle oldOptions = widgetManager.getAppWidgetOptions(mAppWidgetId);
293 boolean needsUpdate = false;
294 if (newMinWidth != oldOptions.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH) ||
295 newMinHeight != oldOptions.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT) ||
296 newMaxWidth != oldOptions.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH) ||
297 newMaxHeight != oldOptions.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT)) {
298 needsUpdate = true;
299 }
300
301 if (needsUpdate) {
302 newOptions.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, newMinWidth);
303 newOptions.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT, newMinHeight);
304 newOptions.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH, newMaxWidth);
305 newOptions.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT, newMaxHeight);
306 updateAppWidgetOptions(newOptions);
307 }
Adam Cohene8724c82012-04-19 17:11:40 -0700308 }
309
Adam Cohenc88d11f2012-04-27 18:33:18 -0700310 /**
311 * Specify some extra information for the widget provider. Causes a callback to the
312 * AppWidgetProvider.
Adam Cohend2097eb2012-05-01 18:10:28 -0700313 * @see AppWidgetProvider#onAppWidgetOptionsChanged(Context, AppWidgetManager, int, Bundle)
Adam Cohenc88d11f2012-04-27 18:33:18 -0700314 *
Adam Cohend2097eb2012-05-01 18:10:28 -0700315 * @param options The bundle of options information.
Adam Cohenc88d11f2012-04-27 18:33:18 -0700316 */
Adam Cohend2097eb2012-05-01 18:10:28 -0700317 public void updateAppWidgetOptions(Bundle options) {
318 AppWidgetManager.getInstance(mContext).updateAppWidgetOptions(mAppWidgetId, options);
Adam Cohene8724c82012-04-19 17:11:40 -0700319 }
320
Jeff Sharkeyb8560b62009-08-06 21:24:25 -0700321 /** {@inheritDoc} */
322 @Override
323 public LayoutParams generateLayoutParams(AttributeSet attrs) {
324 // We're being asked to inflate parameters, probably by a LayoutInflater
325 // in a remote Context. To help resolve any remote references, we
326 // inflate through our last mRemoteContext when it exists.
327 final Context context = mRemoteContext != null ? mRemoteContext : mContext;
328 return new FrameLayout.LayoutParams(context, attrs);
329 }
330
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800331 /**
Joe Onoratoc27bb552010-06-23 17:44:30 -0700332 * Update the AppWidgetProviderInfo for this view, and reset it to the
333 * initial layout.
334 */
335 void resetAppWidget(AppWidgetProviderInfo info) {
336 mInfo = info;
337 mViewMode = VIEW_MODE_NOINIT;
338 updateAppWidget(null);
339 }
340
341 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800342 * Process a set of {@link RemoteViews} coming in as an update from the
Romain Guyae8dd292009-11-05 13:36:15 -0800343 * AppWidget provider. Will animate into these new views as needed
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800344 */
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700345 public void updateAppWidget(RemoteViews remoteViews) {
Jim Millere667a7a2012-08-09 19:22:32 -0700346
Joe Onoratoc27bb552010-06-23 17:44:30 -0700347 if (LOGD) Log.d(TAG, "updateAppWidget called mOld=" + mOld);
Joe Onoratof140be62010-05-04 11:49:12 -0700348
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800349 boolean recycled = false;
350 View content = null;
351 Exception exception = null;
Jim Millere667a7a2012-08-09 19:22:32 -0700352
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800353 // Capture the old view into a bitmap so we can do the crossfade.
354 if (CROSSFADE) {
355 if (mFadeStartTime < 0) {
356 if (mView != null) {
357 final int width = mView.getWidth();
358 final int height = mView.getHeight();
359 try {
360 mOld = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
361 } catch (OutOfMemoryError e) {
362 // we just won't do the fade
363 mOld = null;
364 }
365 if (mOld != null) {
366 //mView.drawIntoBitmap(mOld);
367 }
368 }
369 }
370 }
Jim Millere667a7a2012-08-09 19:22:32 -0700371
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800372 if (remoteViews == null) {
373 if (mViewMode == VIEW_MODE_DEFAULT) {
374 // We've already done this -- nothing to do.
375 return;
376 }
377 content = getDefaultView();
378 mLayoutId = -1;
379 mViewMode = VIEW_MODE_DEFAULT;
380 } else {
Jeff Sharkeyb8560b62009-08-06 21:24:25 -0700381 // Prepare a local reference to the remote Context so we're ready to
382 // inflate any requested LayoutParams.
383 mRemoteContext = getRemoteContext(remoteViews);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800384 int layoutId = remoteViews.getLayoutId();
385
386 // If our stale view has been prepared to match active, and the new
387 // layout matches, try recycling it
388 if (content == null && layoutId == mLayoutId) {
389 try {
Jim Millere667a7a2012-08-09 19:22:32 -0700390 remoteViews.reapply(mContext, mView, mOnClickHandler);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800391 content = mView;
392 recycled = true;
393 if (LOGD) Log.d(TAG, "was able to recycled existing layout");
394 } catch (RuntimeException e) {
395 exception = e;
396 }
397 }
Jim Millere667a7a2012-08-09 19:22:32 -0700398
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800399 // Try normal RemoteView inflation
400 if (content == null) {
401 try {
Jim Millere667a7a2012-08-09 19:22:32 -0700402 content = remoteViews.apply(mContext, this, mOnClickHandler);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800403 if (LOGD) Log.d(TAG, "had to inflate new layout");
404 } catch (RuntimeException e) {
405 exception = e;
406 }
407 }
408
409 mLayoutId = layoutId;
410 mViewMode = VIEW_MODE_CONTENT;
411 }
Jim Millere667a7a2012-08-09 19:22:32 -0700412
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800413 if (content == null) {
414 if (mViewMode == VIEW_MODE_ERROR) {
415 // We've already done this -- nothing to do.
416 return ;
417 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700418 Log.w(TAG, "updateAppWidget couldn't find any view, using error view", exception);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800419 content = getErrorView();
420 mViewMode = VIEW_MODE_ERROR;
421 }
Jim Millere667a7a2012-08-09 19:22:32 -0700422
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800423 if (!recycled) {
424 prepareView(content);
425 addView(content);
426 }
427
428 if (mView != content) {
429 removeView(mView);
430 mView = content;
431 }
432
433 if (CROSSFADE) {
434 if (mFadeStartTime < 0) {
435 // if there is already an animation in progress, don't do anything --
436 // the new view will pop in on top of the old one during the cross fade,
437 // and that looks okay.
438 mFadeStartTime = SystemClock.uptimeMillis();
439 invalidate();
440 }
441 }
442 }
443
Jeff Sharkeyb8560b62009-08-06 21:24:25 -0700444 /**
Winson Chung499cb9f2010-07-16 11:18:17 -0700445 * Process data-changed notifications for the specified view in the specified
446 * set of {@link RemoteViews} views.
447 */
Winson Chung6394c0e2010-08-16 10:14:56 -0700448 void viewDataChanged(int viewId) {
Winson Chung499cb9f2010-07-16 11:18:17 -0700449 View v = findViewById(viewId);
450 if ((v != null) && (v instanceof AdapterView<?>)) {
451 AdapterView<?> adapterView = (AdapterView<?>) v;
452 Adapter adapter = adapterView.getAdapter();
453 if (adapter instanceof BaseAdapter) {
454 BaseAdapter baseAdapter = (BaseAdapter) adapter;
455 baseAdapter.notifyDataSetChanged();
Adam Cohen2148d432011-07-28 14:59:54 -0700456 } else if (adapter == null && adapterView instanceof RemoteAdapterConnectionCallback) {
457 // If the adapter is null, it may mean that the RemoteViewsAapter has not yet
458 // connected to its associated service, and hence the adapter hasn't been set.
459 // In this case, we need to defer the notify call until it has been set.
460 ((RemoteAdapterConnectionCallback) adapterView).deferNotifyDataSetChanged();
Winson Chung499cb9f2010-07-16 11:18:17 -0700461 }
462 }
463 }
464
465 /**
Jeff Sharkeyb8560b62009-08-06 21:24:25 -0700466 * Build a {@link Context} cloned into another package name, usually for the
467 * purposes of reading remote resources.
468 */
469 private Context getRemoteContext(RemoteViews views) {
470 // Bail if missing package name
471 final String packageName = views.getPackage();
472 if (packageName == null) return mContext;
473
474 try {
475 // Return if cloned successfully, otherwise default
Amith Yamasani94022e82012-12-04 11:05:39 -0800476 return mContext.createPackageContextAsUser(packageName, Context.CONTEXT_RESTRICTED,
477 mUser);
Jeff Sharkeyb8560b62009-08-06 21:24:25 -0700478 } catch (NameNotFoundException e) {
479 Log.e(TAG, "Package name " + packageName + " not found");
480 return mContext;
481 }
482 }
483
Gilles Debunne30301932010-06-16 18:32:00 -0700484 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800485 protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
486 if (CROSSFADE) {
487 int alpha;
488 int l = child.getLeft();
489 int t = child.getTop();
490 if (mFadeStartTime > 0) {
491 alpha = (int)(((drawingTime-mFadeStartTime)*255)/FADE_DURATION);
492 if (alpha > 255) {
493 alpha = 255;
494 }
495 Log.d(TAG, "drawChild alpha=" + alpha + " l=" + l + " t=" + t
496 + " w=" + child.getWidth());
497 if (alpha != 255 && mOld != null) {
498 mOldPaint.setAlpha(255-alpha);
499 //canvas.drawBitmap(mOld, l, t, mOldPaint);
500 }
501 } else {
502 alpha = 255;
503 }
504 int restoreTo = canvas.saveLayerAlpha(l, t, child.getWidth(), child.getHeight(), alpha,
505 Canvas.HAS_ALPHA_LAYER_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG);
506 boolean rv = super.drawChild(canvas, child, drawingTime);
507 canvas.restoreToCount(restoreTo);
508 if (alpha < 255) {
509 invalidate();
510 } else {
511 mFadeStartTime = -1;
512 if (mOld != null) {
513 mOld.recycle();
514 mOld = null;
515 }
516 }
517 return rv;
518 } else {
519 return super.drawChild(canvas, child, drawingTime);
520 }
521 }
Jim Millere667a7a2012-08-09 19:22:32 -0700522
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800523 /**
524 * Prepare the given view to be shown. This might include adjusting
525 * {@link FrameLayout.LayoutParams} before inserting.
526 */
527 protected void prepareView(View view) {
Jeff Sharkeyb8560b62009-08-06 21:24:25 -0700528 // Take requested dimensions from child, but apply default gravity.
529 FrameLayout.LayoutParams requested = (FrameLayout.LayoutParams)view.getLayoutParams();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800530 if (requested == null) {
Romain Guy980a9382010-01-08 15:06:28 -0800531 requested = new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT,
532 LayoutParams.MATCH_PARENT);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800533 }
Jeff Sharkeyb8560b62009-08-06 21:24:25 -0700534
535 requested.gravity = Gravity.CENTER;
536 view.setLayoutParams(requested);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800537 }
Jim Millere667a7a2012-08-09 19:22:32 -0700538
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800539 /**
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700540 * Inflate and return the default layout requested by AppWidget provider.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800541 */
542 protected View getDefaultView() {
Joe Onoratof140be62010-05-04 11:49:12 -0700543 if (LOGD) {
544 Log.d(TAG, "getDefaultView");
545 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800546 View defaultView = null;
547 Exception exception = null;
Jim Millere667a7a2012-08-09 19:22:32 -0700548
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800549 try {
550 if (mInfo != null) {
Amith Yamasani94022e82012-12-04 11:05:39 -0800551 Context theirContext = mContext.createPackageContextAsUser(
552 mInfo.provider.getPackageName(), Context.CONTEXT_RESTRICTED, mUser);
Jeff Sharkey220dec22009-08-19 09:31:51 -0700553 mRemoteContext = theirContext;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800554 LayoutInflater inflater = (LayoutInflater)
555 theirContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
556 inflater = inflater.cloneInContext(theirContext);
557 inflater.setFilter(sInflaterFilter);
Adam Cohen0aa2d422012-09-07 17:37:26 -0700558 AppWidgetManager manager = AppWidgetManager.getInstance(mContext);
559 Bundle options = manager.getAppWidgetOptions(mAppWidgetId);
560
561 int layoutId = mInfo.initialLayout;
562 if (options.containsKey(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY)) {
563 int category = options.getInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY);
564 if (category == AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD) {
Adam Cohen180cfd52012-11-04 12:03:12 -0800565 int kgLayoutId = mInfo.initialKeyguardLayout;
566 // If a default keyguard layout is not specified, use the standard
567 // default layout.
568 layoutId = kgLayoutId == 0 ? layoutId : kgLayoutId;
Adam Cohen0aa2d422012-09-07 17:37:26 -0700569 }
570 }
571 defaultView = inflater.inflate(layoutId, this, false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800572 } else {
573 Log.w(TAG, "can't inflate defaultView because mInfo is missing");
574 }
575 } catch (PackageManager.NameNotFoundException e) {
576 exception = e;
577 } catch (RuntimeException e) {
578 exception = e;
579 }
Jim Millere667a7a2012-08-09 19:22:32 -0700580
Jeff Sharkey220dec22009-08-19 09:31:51 -0700581 if (exception != null) {
582 Log.w(TAG, "Error inflating AppWidget " + mInfo + ": " + exception.toString());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800583 }
Jim Millere667a7a2012-08-09 19:22:32 -0700584
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800585 if (defaultView == null) {
586 if (LOGD) Log.d(TAG, "getDefaultView couldn't find any view, so inflating error");
587 defaultView = getErrorView();
588 }
Jim Millere667a7a2012-08-09 19:22:32 -0700589
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800590 return defaultView;
591 }
Jim Millere667a7a2012-08-09 19:22:32 -0700592
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800593 /**
594 * Inflate and return a view that represents an error state.
595 */
596 protected View getErrorView() {
597 TextView tv = new TextView(mContext);
598 tv.setText(com.android.internal.R.string.gadget_host_error_inflating);
599 // TODO: get this color from somewhere.
600 tv.setBackgroundColor(Color.argb(127, 0, 0, 0));
601 return tv;
602 }
Romain Guy09ddc082009-08-11 12:10:08 -0700603
Guang Zhu1affb932012-05-15 14:25:51 -0700604 @Override
605 public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
606 super.onInitializeAccessibilityNodeInfo(info);
607 info.setClassName(AppWidgetHostView.class.getName());
608 }
609
Romain Guy09ddc082009-08-11 12:10:08 -0700610 private static class ParcelableSparseArray extends SparseArray<Parcelable> implements Parcelable {
611 public int describeContents() {
612 return 0;
613 }
614
615 public void writeToParcel(Parcel dest, int flags) {
616 final int count = size();
617 dest.writeInt(count);
618 for (int i = 0; i < count; i++) {
619 dest.writeInt(keyAt(i));
620 dest.writeParcelable(valueAt(i), 0);
621 }
622 }
623
624 public static final Parcelable.Creator<ParcelableSparseArray> CREATOR =
625 new Parcelable.Creator<ParcelableSparseArray>() {
626 public ParcelableSparseArray createFromParcel(Parcel source) {
627 final ParcelableSparseArray array = new ParcelableSparseArray();
628 final ClassLoader loader = array.getClass().getClassLoader();
629 final int count = source.readInt();
630 for (int i = 0; i < count; i++) {
631 array.put(source.readInt(), source.readParcelable(loader));
632 }
633 return array;
634 }
635
636 public ParcelableSparseArray[] newArray(int size) {
637 return new ParcelableSparseArray[size];
638 }
639 };
640 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800641}