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