blob: 09661a59334ca0818ab0350478545548c6039b9e [file] [log] [blame]
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001/*
2 * Copyright (C) 2006 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.appwidget;
18
19import android.content.ComponentName;
20import android.content.Context;
Winson Chung81f39eb2011-01-11 18:05:01 -080021import android.content.Intent;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070022import android.os.IBinder;
23import android.os.RemoteException;
24import android.os.ServiceManager;
Mitsuru Oshima8f25c422009-07-01 00:10:43 -070025import android.util.DisplayMetrics;
Mitsuru Oshima8f25c422009-07-01 00:10:43 -070026import android.util.TypedValue;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070027import android.widget.RemoteViews;
28
29import com.android.internal.appwidget.IAppWidgetService;
30
31import java.lang.ref.WeakReference;
32import java.util.List;
33import java.util.WeakHashMap;
34
35/**
36 * Updates AppWidget state; gets information about installed AppWidget providers and other
37 * AppWidget related state.
38 */
39public class AppWidgetManager {
40 static final String TAG = "AppWidgetManager";
41
42 /**
43 * Send this from your {@link AppWidgetHost} activity when you want to pick an AppWidget to display.
44 * The AppWidget picker activity will be launched.
45 * <p>
46 * You must supply the following extras:
47 * <table>
48 * <tr>
49 * <td>{@link #EXTRA_APPWIDGET_ID}</td>
50 * <td>A newly allocated appWidgetId, which will be bound to the AppWidget provider
51 * once the user has selected one.</td>
52 * </tr>
53 * </table>
54 *
55 * <p>
56 * The system will respond with an onActivityResult call with the following extras in
57 * the intent:
58 * <table>
59 * <tr>
60 * <td>{@link #EXTRA_APPWIDGET_ID}</td>
61 * <td>The appWidgetId that you supplied in the original intent.</td>
62 * </tr>
63 * </table>
64 * <p>
65 * When you receive the result from the AppWidget pick activity, if the resultCode is
66 * {@link android.app.Activity#RESULT_OK}, an AppWidget has been selected. You should then
67 * check the AppWidgetProviderInfo for the returned AppWidget, and if it has one, launch its configuration
68 * activity. If {@link android.app.Activity#RESULT_CANCELED} is returned, you should delete
69 * the appWidgetId.
70 *
71 * @see #ACTION_APPWIDGET_CONFIGURE
72 */
73 public static final String ACTION_APPWIDGET_PICK = "android.appwidget.action.APPWIDGET_PICK";
74
75 /**
76 * Sent when it is time to configure your AppWidget while it is being added to a host.
77 * This action is not sent as a broadcast to the AppWidget provider, but as a startActivity
78 * to the activity specified in the {@link AppWidgetProviderInfo AppWidgetProviderInfo meta-data}.
79 *
80 * <p>
81 * The intent will contain the following extras:
82 * <table>
83 * <tr>
84 * <td>{@link #EXTRA_APPWIDGET_ID}</td>
85 * <td>The appWidgetId to configure.</td>
86 * </tr>
87 * </table>
88 *
89 * <p>If you return {@link android.app.Activity#RESULT_OK} using
90 * {@link android.app.Activity#setResult Activity.setResult()}, the AppWidget will be added,
91 * and you will receive an {@link #ACTION_APPWIDGET_UPDATE} broadcast for this AppWidget.
92 * If you return {@link android.app.Activity#RESULT_CANCELED}, the host will cancel the add
93 * and not display this AppWidget, and you will receive a {@link #ACTION_APPWIDGET_DELETED} broadcast.
94 */
95 public static final String ACTION_APPWIDGET_CONFIGURE = "android.appwidget.action.APPWIDGET_CONFIGURE";
96
97 /**
98 * An intent extra that contains one appWidgetId.
99 * <p>
100 * The value will be an int that can be retrieved like this:
101 * {@sample frameworks/base/tests/appwidgets/AppWidgetHostTest/src/com/android/tests/appwidgethost/AppWidgetHostActivity.java getExtra_EXTRA_APPWIDGET_ID}
102 */
103 public static final String EXTRA_APPWIDGET_ID = "appWidgetId";
104
105 /**
106 * An intent extra that contains multiple appWidgetIds.
107 * <p>
108 * The value will be an int array that can be retrieved like this:
109 * {@sample frameworks/base/tests/appwidgets/AppWidgetHostTest/src/com/android/tests/appwidgethost/TestAppWidgetProvider.java getExtra_EXTRA_APPWIDGET_IDS}
110 */
111 public static final String EXTRA_APPWIDGET_IDS = "appWidgetIds";
112
113 /**
The Android Open Source Project10592532009-03-18 17:39:46 -0700114 * An intent extra to pass to the AppWidget picker containing a {@link java.util.List} of
115 * {@link AppWidgetProviderInfo} objects to mix in to the list of AppWidgets that are
116 * installed. (This is how the launcher shows the search widget).
117 */
118 public static final String EXTRA_CUSTOM_INFO = "customInfo";
119
120 /**
121 * An intent extra to pass to the AppWidget picker containing a {@link java.util.List} of
122 * {@link android.os.Bundle} objects to mix in to the list of AppWidgets that are
123 * installed. It will be added to the extras object on the {@link android.content.Intent}
124 * that is returned from the picker activity.
125 *
126 * {@more}
127 */
128 public static final String EXTRA_CUSTOM_EXTRAS = "customExtras";
129
130 /**
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700131 * A sentiel value that the AppWidget manager will never return as a appWidgetId.
132 */
133 public static final int INVALID_APPWIDGET_ID = 0;
134
135 /**
136 * Sent when it is time to update your AppWidget.
137 *
138 * <p>This may be sent in response to a new instance for this AppWidget provider having
139 * been instantiated, the requested {@link AppWidgetProviderInfo#updatePeriodMillis update interval}
140 * having lapsed, or the system booting.
141 *
142 * <p>
143 * The intent will contain the following extras:
144 * <table>
145 * <tr>
146 * <td>{@link #EXTRA_APPWIDGET_IDS}</td>
147 * <td>The appWidgetIds to update. This may be all of the AppWidgets created for this
148 * provider, or just a subset. The system tries to send updates for as few AppWidget
149 * instances as possible.</td>
150 * </tr>
151 * </table>
Christian Mehlmauer10c543d2010-06-25 19:27:04 +0200152 *
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700153 * @see AppWidgetProvider#onUpdate AppWidgetProvider.onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds)
154 */
155 public static final String ACTION_APPWIDGET_UPDATE = "android.appwidget.action.APPWIDGET_UPDATE";
156
157 /**
158 * Sent when an instance of an AppWidget is deleted from its host.
159 *
160 * @see AppWidgetProvider#onDeleted AppWidgetProvider.onDeleted(Context context, int[] appWidgetIds)
161 */
162 public static final String ACTION_APPWIDGET_DELETED = "android.appwidget.action.APPWIDGET_DELETED";
163
164 /**
165 * Sent when an instance of an AppWidget is removed from the last host.
Christian Mehlmauer10c543d2010-06-25 19:27:04 +0200166 *
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700167 * @see AppWidgetProvider#onEnabled AppWidgetProvider.onEnabled(Context context)
168 */
169 public static final String ACTION_APPWIDGET_DISABLED = "android.appwidget.action.APPWIDGET_DISABLED";
170
171 /**
172 * Sent when an instance of an AppWidget is added to a host for the first time.
173 * This broadcast is sent at boot time if there is a AppWidgetHost installed with
174 * an instance for this provider.
Christian Mehlmauer10c543d2010-06-25 19:27:04 +0200175 *
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700176 * @see AppWidgetProvider#onEnabled AppWidgetProvider.onEnabled(Context context)
177 */
178 public static final String ACTION_APPWIDGET_ENABLED = "android.appwidget.action.APPWIDGET_ENABLED";
179
180 /**
181 * Field for the manifest meta-data tag.
182 *
183 * @see AppWidgetProviderInfo
184 */
185 public static final String META_DATA_APPWIDGET_PROVIDER = "android.appwidget.provider";
Christian Mehlmauer10c543d2010-06-25 19:27:04 +0200186
Christian Mehlmauer10c543d2010-06-25 19:27:04 +0200187 static WeakHashMap<Context, WeakReference<AppWidgetManager>> sManagerCache =
188 new WeakHashMap<Context, WeakReference<AppWidgetManager>>();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700189 static IAppWidgetService sService;
Christian Mehlmauer10c543d2010-06-25 19:27:04 +0200190
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700191 Context mContext;
192
Mitsuru Oshima8f25c422009-07-01 00:10:43 -0700193 private DisplayMetrics mDisplayMetrics;
194
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700195 /**
196 * Get the AppWidgetManager instance to use for the supplied {@link android.content.Context
197 * Context} object.
198 */
199 public static AppWidgetManager getInstance(Context context) {
200 synchronized (sManagerCache) {
201 if (sService == null) {
202 IBinder b = ServiceManager.getService(Context.APPWIDGET_SERVICE);
203 sService = IAppWidgetService.Stub.asInterface(b);
204 }
205
206 WeakReference<AppWidgetManager> ref = sManagerCache.get(context);
207 AppWidgetManager result = null;
208 if (ref != null) {
209 result = ref.get();
210 }
211 if (result == null) {
212 result = new AppWidgetManager(context);
Christian Mehlmauer10c543d2010-06-25 19:27:04 +0200213 sManagerCache.put(context, new WeakReference<AppWidgetManager>(result));
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700214 }
215 return result;
216 }
217 }
218
219 private AppWidgetManager(Context context) {
220 mContext = context;
Mitsuru Oshima8f25c422009-07-01 00:10:43 -0700221 mDisplayMetrics = context.getResources().getDisplayMetrics();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700222 }
223
224 /**
225 * Set the RemoteViews to use for the specified appWidgetIds.
226 *
Adam Cohen2dd21972010-08-15 18:20:04 -0700227 * Note that the RemoteViews parameter will be cached by the AppWidgetService, and hence should
228 * contain a complete representation of the widget. For performing partial widget updates, see
229 * {@link #partiallyUpdateAppWidget(int[], RemoteViews)}.
230 *
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700231 * <p>
232 * It is okay to call this method both inside an {@link #ACTION_APPWIDGET_UPDATE} broadcast,
233 * and outside of the handler.
234 * This method will only work when called from the uid that owns the AppWidget provider.
235 *
236 * @param appWidgetIds The AppWidget instances for which to set the RemoteViews.
237 * @param views The RemoteViews object to show.
238 */
239 public void updateAppWidget(int[] appWidgetIds, RemoteViews views) {
240 try {
241 sService.updateAppWidgetIds(appWidgetIds, views);
242 }
243 catch (RemoteException e) {
244 throw new RuntimeException("system server dead?", e);
245 }
246 }
247
248 /**
249 * Set the RemoteViews to use for the specified appWidgetId.
250 *
Adam Cohen2dd21972010-08-15 18:20:04 -0700251 * Note that the RemoteViews parameter will be cached by the AppWidgetService, and hence should
252 * contain a complete representation of the widget. For performing partial widget updates, see
253 * {@link #partiallyUpdateAppWidget(int, RemoteViews)}.
254 *
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700255 * <p>
256 * It is okay to call this method both inside an {@link #ACTION_APPWIDGET_UPDATE} broadcast,
257 * and outside of the handler.
258 * This method will only work when called from the uid that owns the AppWidget provider.
259 *
260 * @param appWidgetId The AppWidget instance for which to set the RemoteViews.
261 * @param views The RemoteViews object to show.
262 */
263 public void updateAppWidget(int appWidgetId, RemoteViews views) {
264 updateAppWidget(new int[] { appWidgetId }, views);
265 }
266
267 /**
Adam Cohen2dd21972010-08-15 18:20:04 -0700268 * Perform an incremental update or command on the widget(s) specified by appWidgetIds.
269 *
270 * This update differs from {@link #updateAppWidget(int[], RemoteViews)} in that the
271 * RemoteViews object which is passed is understood to be an incomplete representation of the
272 * widget, and hence is not cached by the AppWidgetService. Note that because these updates are
273 * not cached, any state that they modify that is not restored by restoreInstanceState will not
274 * persist in the case that the widgets are restored using the cached version in
275 * AppWidgetService.
276 *
277 * Use with {@link RemoteViews#showNext(int)}, {@link RemoteViews#showPrevious(int)},
278 * {@link RemoteViews#setScrollPosition(int, int)} and similar commands.
279 *
280 * <p>
281 * It is okay to call this method both inside an {@link #ACTION_APPWIDGET_UPDATE} broadcast,
282 * and outside of the handler.
283 * This method will only work when called from the uid that owns the AppWidget provider.
284 *
285 * @param appWidgetIds The AppWidget instances for which to set the RemoteViews.
286 * @param views The RemoteViews object containing the incremental update / command.
287 */
288 public void partiallyUpdateAppWidget(int[] appWidgetIds, RemoteViews views) {
289 try {
290 sService.partiallyUpdateAppWidgetIds(appWidgetIds, views);
291 } catch (RemoteException e) {
292 throw new RuntimeException("system server dead?", e);
293 }
294 }
295
296 /**
297 * Perform an incremental update or command on the widget specified by appWidgetId.
298 *
299 * This update differs from {@link #updateAppWidget(int, RemoteViews)} in that the RemoteViews
300 * object which is passed is understood to be an incomplete representation of the widget, and
301 * hence is not cached by the AppWidgetService. Note that because these updates are not cached,
302 * any state that they modify that is not restored by restoreInstanceState will not persist in
303 * the case that the widgets are restored using the cached version in AppWidgetService.
304 *
305 * Use with {@link RemoteViews#showNext(int)}, {@link RemoteViews#showPrevious(int)},
306 * {@link RemoteViews#setScrollPosition(int, int)} and similar commands.
307 *
308 * <p>
309 * It is okay to call this method both inside an {@link #ACTION_APPWIDGET_UPDATE} broadcast,
310 * and outside of the handler.
311 * This method will only work when called from the uid that owns the AppWidget provider.
312 *
313 * @param appWidgetId The AppWidget instance for which to set the RemoteViews.
314 * @param views The RemoteViews object containing the incremental update / command.
315 */
316 public void partiallyUpdateAppWidget(int appWidgetId, RemoteViews views) {
317 partiallyUpdateAppWidget(new int[] { appWidgetId }, views);
318 }
319
320 /**
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700321 * Set the RemoteViews to use for all AppWidget instances for the supplied AppWidget provider.
322 *
323 * <p>
324 * It is okay to call this method both inside an {@link #ACTION_APPWIDGET_UPDATE} broadcast,
325 * and outside of the handler.
326 * This method will only work when called from the uid that owns the AppWidget provider.
327 *
328 * @param provider The {@link ComponentName} for the {@link
329 * android.content.BroadcastReceiver BroadcastReceiver} provider
330 * for your AppWidget.
331 * @param views The RemoteViews object to show.
332 */
333 public void updateAppWidget(ComponentName provider, RemoteViews views) {
334 try {
335 sService.updateAppWidgetProvider(provider, views);
336 }
337 catch (RemoteException e) {
338 throw new RuntimeException("system server dead?", e);
339 }
340 }
341
342 /**
Winson Chung499cb9f2010-07-16 11:18:17 -0700343 * Notifies the specified collection view in all the specified AppWidget instances
344 * to invalidate their currently data.
345 *
346 * @param appWidgetIds The AppWidget instances for which to notify of view data changes.
Winson Chung499cb9f2010-07-16 11:18:17 -0700347 * @param viewId The collection view id.
348 */
Winson Chung6394c0e2010-08-16 10:14:56 -0700349 public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId) {
Winson Chung499cb9f2010-07-16 11:18:17 -0700350 try {
Winson Chung6394c0e2010-08-16 10:14:56 -0700351 sService.notifyAppWidgetViewDataChanged(appWidgetIds, viewId);
Winson Chung499cb9f2010-07-16 11:18:17 -0700352 }
353 catch (RemoteException e) {
354 throw new RuntimeException("system server dead?", e);
355 }
356 }
357
358 /**
359 * Notifies the specified collection view in all the specified AppWidget instance
360 * to invalidate it's currently data.
361 *
362 * @param appWidgetId The AppWidget instance for which to notify of view data changes.
Winson Chung499cb9f2010-07-16 11:18:17 -0700363 * @param viewId The collection view id.
364 */
Winson Chung6394c0e2010-08-16 10:14:56 -0700365 public void notifyAppWidgetViewDataChanged(int appWidgetId, int viewId) {
366 notifyAppWidgetViewDataChanged(new int[] { appWidgetId }, viewId);
Winson Chung499cb9f2010-07-16 11:18:17 -0700367 }
368
369 /**
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700370 * Return a list of the AppWidget providers that are currently installed.
371 */
372 public List<AppWidgetProviderInfo> getInstalledProviders() {
373 try {
Patrick Dubroy5d140912010-07-12 10:25:27 -0700374 List<AppWidgetProviderInfo> providers = sService.getInstalledProviders();
375 for (AppWidgetProviderInfo info : providers) {
376 // Converting complex to dp.
377 info.minWidth =
378 TypedValue.complexToDimensionPixelSize(info.minWidth, mDisplayMetrics);
379 info.minHeight =
380 TypedValue.complexToDimensionPixelSize(info.minHeight, mDisplayMetrics);
Adam Cohen324afba2011-07-22 11:51:45 -0700381 info.minResizeWidth =
382 TypedValue.complexToDimensionPixelSize(info.minResizeWidth, mDisplayMetrics);
383 info.minResizeHeight =
384 TypedValue.complexToDimensionPixelSize(info.minResizeHeight, mDisplayMetrics);
Patrick Dubroy5d140912010-07-12 10:25:27 -0700385 }
386 return providers;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700387 }
388 catch (RemoteException e) {
389 throw new RuntimeException("system server dead?", e);
390 }
391 }
392
393 /**
394 * Get the available info about the AppWidget.
395 *
396 * @return A appWidgetId. If the appWidgetId has not been bound to a provider yet, or
397 * you don't have access to that appWidgetId, null is returned.
398 */
399 public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) {
400 try {
Mitsuru Oshima8f25c422009-07-01 00:10:43 -0700401 AppWidgetProviderInfo info = sService.getAppWidgetInfo(appWidgetId);
402 if (info != null) {
403 // Converting complex to dp.
Christian Mehlmauer10c543d2010-06-25 19:27:04 +0200404 info.minWidth =
Mitsuru Oshima8f25c422009-07-01 00:10:43 -0700405 TypedValue.complexToDimensionPixelSize(info.minWidth, mDisplayMetrics);
406 info.minHeight =
407 TypedValue.complexToDimensionPixelSize(info.minHeight, mDisplayMetrics);
Adam Cohen324afba2011-07-22 11:51:45 -0700408 info.minResizeWidth =
409 TypedValue.complexToDimensionPixelSize(info.minResizeWidth, mDisplayMetrics);
410 info.minResizeHeight =
411 TypedValue.complexToDimensionPixelSize(info.minResizeHeight, mDisplayMetrics);
Mitsuru Oshima8f25c422009-07-01 00:10:43 -0700412 }
413 return info;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700414 }
415 catch (RemoteException e) {
416 throw new RuntimeException("system server dead?", e);
417 }
418 }
419
420 /**
421 * Set the component for a given appWidgetId.
422 *
423 * <p class="note">You need the APPWIDGET_LIST permission. This method is to be used by the
424 * AppWidget picker.
425 *
426 * @param appWidgetId The AppWidget instance for which to set the RemoteViews.
427 * @param provider The {@link android.content.BroadcastReceiver} that will be the AppWidget
428 * provider for this AppWidget.
429 */
430 public void bindAppWidgetId(int appWidgetId, ComponentName provider) {
431 try {
432 sService.bindAppWidgetId(appWidgetId, provider);
433 }
434 catch (RemoteException e) {
435 throw new RuntimeException("system server dead?", e);
436 }
437 }
438
439 /**
Winson Chung81f39eb2011-01-11 18:05:01 -0800440 * Binds the RemoteViewsService for a given appWidgetId and intent.
441 *
442 * The appWidgetId specified must already be bound to the calling AppWidgetHost via
443 * {@link android.appwidget.AppWidgetManager#bindAppWidgetId AppWidgetManager.bindAppWidgetId()}.
444 *
445 * @param appWidgetId The AppWidget instance for which to bind the RemoteViewsService.
446 * @param intent The intent of the service which will be providing the data to the
447 * RemoteViewsAdapter.
448 * @param connection The callback interface to be notified when a connection is made or lost.
449 * @hide
450 */
451 public void bindRemoteViewsService(int appWidgetId, Intent intent, IBinder connection) {
452 try {
453 sService.bindRemoteViewsService(appWidgetId, intent, connection);
454 }
455 catch (RemoteException e) {
456 throw new RuntimeException("system server dead?", e);
457 }
458 }
459
460 /**
461 * Unbinds the RemoteViewsService for a given appWidgetId and intent.
462 *
463 * The appWidgetId specified muse already be bound to the calling AppWidgetHost via
464 * {@link android.appwidget.AppWidgetManager#bindAppWidgetId AppWidgetManager.bindAppWidgetId()}.
465 *
466 * @param appWidgetId The AppWidget instance for which to bind the RemoteViewsService.
467 * @param intent The intent of the service which will be providing the data to the
468 * RemoteViewsAdapter.
469 * @hide
470 */
471 public void unbindRemoteViewsService(int appWidgetId, Intent intent) {
472 try {
473 sService.unbindRemoteViewsService(appWidgetId, intent);
474 }
475 catch (RemoteException e) {
476 throw new RuntimeException("system server dead?", e);
477 }
478 }
479
480 /**
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700481 * Get the list of appWidgetIds that have been bound to the given AppWidget
482 * provider.
Christian Mehlmauer10c543d2010-06-25 19:27:04 +0200483 *
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700484 * @param provider The {@link android.content.BroadcastReceiver} that is the
485 * AppWidget provider to find appWidgetIds for.
486 */
487 public int[] getAppWidgetIds(ComponentName provider) {
488 try {
489 return sService.getAppWidgetIds(provider);
490 }
491 catch (RemoteException e) {
492 throw new RuntimeException("system server dead?", e);
493 }
494 }
495}
496