AppWidgetManager.java revision 8f25c426b118c35f558cbf27bd413e1eb6d59823
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
17package android.appwidget;
18
19import android.content.ComponentName;
20import android.content.Context;
21import android.os.IBinder;
22import android.os.RemoteException;
23import android.os.ServiceManager;
24import android.util.DisplayMetrics;
25import android.util.Log;
26import android.util.TypedValue;
27import 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    /**
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 = new WeakHashMap();
188    static IAppWidgetService sService;
189
190    Context mContext;
191
192    private DisplayMetrics mDisplayMetrics;
193
194    /**
195     * Get the AppWidgetManager instance to use for the supplied {@link android.content.Context
196     * Context} object.
197     */
198    public static AppWidgetManager getInstance(Context context) {
199        synchronized (sManagerCache) {
200            if (sService == null) {
201                IBinder b = ServiceManager.getService(Context.APPWIDGET_SERVICE);
202                sService = IAppWidgetService.Stub.asInterface(b);
203            }
204
205            WeakReference<AppWidgetManager> ref = sManagerCache.get(context);
206            AppWidgetManager result = null;
207            if (ref != null) {
208                result = ref.get();
209            }
210            if (result == null) {
211                result = new AppWidgetManager(context);
212                sManagerCache.put(context, new WeakReference(result));
213            }
214            return result;
215        }
216    }
217
218    private AppWidgetManager(Context context) {
219        mContext = context;
220        mDisplayMetrics = context.getResources().getDisplayMetrics();
221    }
222
223    /**
224     * Set the RemoteViews to use for the specified appWidgetIds.
225     *
226     * <p>
227     * It is okay to call this method both inside an {@link #ACTION_APPWIDGET_UPDATE} broadcast,
228     * and outside of the handler.
229     * This method will only work when called from the uid that owns the AppWidget provider.
230     *
231     * @param appWidgetIds     The AppWidget instances for which to set the RemoteViews.
232     * @param views         The RemoteViews object to show.
233     */
234    public void updateAppWidget(int[] appWidgetIds, RemoteViews views) {
235        try {
236            sService.updateAppWidgetIds(appWidgetIds, views);
237        }
238        catch (RemoteException e) {
239            throw new RuntimeException("system server dead?", e);
240        }
241    }
242
243    /**
244     * Set the RemoteViews to use for the specified appWidgetId.
245     *
246     * <p>
247     * It is okay to call this method both inside an {@link #ACTION_APPWIDGET_UPDATE} broadcast,
248     * and outside of the handler.
249     * This method will only work when called from the uid that owns the AppWidget provider.
250     *
251     * @param appWidgetId      The AppWidget instance for which to set the RemoteViews.
252     * @param views         The RemoteViews object to show.
253     */
254    public void updateAppWidget(int appWidgetId, RemoteViews views) {
255        updateAppWidget(new int[] { appWidgetId }, views);
256    }
257
258    /**
259     * Set the RemoteViews to use for all AppWidget instances for the supplied AppWidget provider.
260     *
261     * <p>
262     * It is okay to call this method both inside an {@link #ACTION_APPWIDGET_UPDATE} broadcast,
263     * and outside of the handler.
264     * This method will only work when called from the uid that owns the AppWidget provider.
265     *
266     * @param provider      The {@link ComponentName} for the {@link
267     * android.content.BroadcastReceiver BroadcastReceiver} provider
268     *                      for your AppWidget.
269     * @param views         The RemoteViews object to show.
270     */
271    public void updateAppWidget(ComponentName provider, RemoteViews views) {
272        try {
273            sService.updateAppWidgetProvider(provider, views);
274        }
275        catch (RemoteException e) {
276            throw new RuntimeException("system server dead?", e);
277        }
278    }
279
280    /**
281     * Return a list of the AppWidget providers that are currently installed.
282     */
283    public List<AppWidgetProviderInfo> getInstalledProviders() {
284        try {
285            return sService.getInstalledProviders();
286        }
287        catch (RemoteException e) {
288            throw new RuntimeException("system server dead?", e);
289        }
290    }
291
292    /**
293     * Get the available info about the AppWidget.
294     *
295     * @return A appWidgetId.  If the appWidgetId has not been bound to a provider yet, or
296     * you don't have access to that appWidgetId, null is returned.
297     */
298    public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) {
299        try {
300            AppWidgetProviderInfo info = sService.getAppWidgetInfo(appWidgetId);
301            if (info != null) {
302                // Converting complex to dp.
303                info.minWidth =
304                        TypedValue.complexToDimensionPixelSize(info.minWidth, mDisplayMetrics);
305                info.minHeight =
306                        TypedValue.complexToDimensionPixelSize(info.minHeight, mDisplayMetrics);
307            }
308            return info;
309        }
310        catch (RemoteException e) {
311            throw new RuntimeException("system server dead?", e);
312        }
313    }
314
315    /**
316     * Set the component for a given appWidgetId.
317     *
318     * <p class="note">You need the APPWIDGET_LIST permission.  This method is to be used by the
319     * AppWidget picker.
320     *
321     * @param appWidgetId     The AppWidget instance for which to set the RemoteViews.
322     * @param provider      The {@link android.content.BroadcastReceiver} that will be the AppWidget
323     *                      provider for this AppWidget.
324     */
325    public void bindAppWidgetId(int appWidgetId, ComponentName provider) {
326        try {
327            sService.bindAppWidgetId(appWidgetId, provider);
328        }
329        catch (RemoteException e) {
330            throw new RuntimeException("system server dead?", e);
331        }
332    }
333
334    /**
335     * Get the list of appWidgetIds that have been bound to the given AppWidget
336     * provider.
337     *
338     * @param provider The {@link android.content.BroadcastReceiver} that is the
339     *            AppWidget provider to find appWidgetIds for.
340     */
341    public int[] getAppWidgetIds(ComponentName provider) {
342        try {
343            return sService.getAppWidgetIds(provider);
344        }
345        catch (RemoteException e) {
346            throw new RuntimeException("system server dead?", e);
347        }
348    }
349}
350
351