• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 package android.appwidget;
18 
19 import android.content.ComponentName;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.os.IBinder;
23 import android.os.RemoteException;
24 import android.os.ServiceManager;
25 import android.util.DisplayMetrics;
26 import android.util.TypedValue;
27 import android.widget.RemoteViews;
28 
29 import com.android.internal.appwidget.IAppWidgetService;
30 
31 import java.lang.ref.WeakReference;
32 import java.util.List;
33 import java.util.WeakHashMap;
34 
35 /**
36  * Updates AppWidget state; gets information about installed AppWidget providers and other
37  * AppWidget related state.
38  */
39 public 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     /**
114      * 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     /**
131      * 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>
152      *
153      * @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.
166      *
167      * @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.
175      *
176      * @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";
186 
187     static WeakHashMap<Context, WeakReference<AppWidgetManager>> sManagerCache =
188         new WeakHashMap<Context, WeakReference<AppWidgetManager>>();
189     static IAppWidgetService sService;
190 
191     Context mContext;
192 
193     private DisplayMetrics mDisplayMetrics;
194 
195     /**
196      * Get the AppWidgetManager instance to use for the supplied {@link android.content.Context
197      * Context} object.
198      */
getInstance(Context context)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);
213                 sManagerCache.put(context, new WeakReference<AppWidgetManager>(result));
214             }
215             return result;
216         }
217     }
218 
AppWidgetManager(Context context)219     private AppWidgetManager(Context context) {
220         mContext = context;
221         mDisplayMetrics = context.getResources().getDisplayMetrics();
222     }
223 
224     /**
225      * Set the RemoteViews to use for the specified appWidgetIds.
226      *
227      * 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      *
231      * <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      */
updateAppWidget(int[] appWidgetIds, RemoteViews views)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      *
251      * 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      *
255      * <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      */
updateAppWidget(int appWidgetId, RemoteViews views)263     public void updateAppWidget(int appWidgetId, RemoteViews views) {
264         updateAppWidget(new int[] { appWidgetId }, views);
265     }
266 
267     /**
268      * 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      */
partiallyUpdateAppWidget(int[] appWidgetIds, RemoteViews views)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      */
partiallyUpdateAppWidget(int appWidgetId, RemoteViews views)316     public void partiallyUpdateAppWidget(int appWidgetId, RemoteViews views) {
317         partiallyUpdateAppWidget(new int[] { appWidgetId }, views);
318     }
319 
320     /**
321      * 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      */
updateAppWidget(ComponentName provider, RemoteViews views)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     /**
343      * 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.
347      * @param viewId        The collection view id.
348      */
notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId)349     public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId) {
350         try {
351             sService.notifyAppWidgetViewDataChanged(appWidgetIds, viewId);
352         }
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.
363      * @param viewId        The collection view id.
364      */
notifyAppWidgetViewDataChanged(int appWidgetId, int viewId)365     public void notifyAppWidgetViewDataChanged(int appWidgetId, int viewId) {
366         notifyAppWidgetViewDataChanged(new int[] { appWidgetId }, viewId);
367     }
368 
369     /**
370      * Return a list of the AppWidget providers that are currently installed.
371      */
getInstalledProviders()372     public List<AppWidgetProviderInfo> getInstalledProviders() {
373         try {
374             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);
381                 info.minResizeWidth =
382                     TypedValue.complexToDimensionPixelSize(info.minResizeWidth, mDisplayMetrics);
383                 info.minResizeHeight =
384                     TypedValue.complexToDimensionPixelSize(info.minResizeHeight, mDisplayMetrics);
385             }
386             return providers;
387         }
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      */
getAppWidgetInfo(int appWidgetId)399     public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) {
400         try {
401             AppWidgetProviderInfo info = sService.getAppWidgetInfo(appWidgetId);
402             if (info != null) {
403                 // Converting complex to dp.
404                 info.minWidth =
405                         TypedValue.complexToDimensionPixelSize(info.minWidth, mDisplayMetrics);
406                 info.minHeight =
407                         TypedValue.complexToDimensionPixelSize(info.minHeight, mDisplayMetrics);
408                 info.minResizeWidth =
409                     TypedValue.complexToDimensionPixelSize(info.minResizeWidth, mDisplayMetrics);
410                 info.minResizeHeight =
411                     TypedValue.complexToDimensionPixelSize(info.minResizeHeight, mDisplayMetrics);
412             }
413             return info;
414         }
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      */
bindAppWidgetId(int appWidgetId, ComponentName provider)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     /**
440      * 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      */
bindRemoteViewsService(int appWidgetId, Intent intent, IBinder connection)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      */
unbindRemoteViewsService(int appWidgetId, Intent intent)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     /**
481      * Get the list of appWidgetIds that have been bound to the given AppWidget
482      * provider.
483      *
484      * @param provider The {@link android.content.BroadcastReceiver} that is the
485      *            AppWidget provider to find appWidgetIds for.
486      */
getAppWidgetIds(ComponentName provider)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 
497