AppWidgetHost.java revision 6090995951c6e2e4dcf38102f01793f8a94166e1
1/* 2 * Copyright (C) 2009 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 java.util.ArrayList; 20import java.util.HashMap; 21 22import android.content.Context; 23import android.os.Binder; 24import android.os.Handler; 25import android.os.IBinder; 26import android.os.Looper; 27import android.os.Message; 28import android.os.Process; 29import android.os.RemoteException; 30import android.os.ServiceManager; 31import android.os.UserHandle; 32import android.util.DisplayMetrics; 33import android.util.TypedValue; 34import android.widget.RemoteViews; 35import android.widget.RemoteViews.OnClickHandler; 36 37import com.android.internal.appwidget.IAppWidgetHost; 38import com.android.internal.appwidget.IAppWidgetService; 39 40/** 41 * AppWidgetHost provides the interaction with the AppWidget service for apps, 42 * like the home screen, that want to embed AppWidgets in their UI. 43 */ 44public class AppWidgetHost { 45 46 static final int HANDLE_UPDATE = 1; 47 static final int HANDLE_PROVIDER_CHANGED = 2; 48 static final int HANDLE_PROVIDERS_CHANGED = 3; 49 static final int HANDLE_VIEW_DATA_CHANGED = 4; 50 51 final static Object sServiceLock = new Object(); 52 static IAppWidgetService sService; 53 private DisplayMetrics mDisplayMetrics; 54 55 Context mContext; 56 String mPackageName; 57 Handler mHandler; 58 int mHostId; 59 Callbacks mCallbacks = new Callbacks(); 60 final HashMap<Integer,AppWidgetHostView> mViews = new HashMap<Integer, AppWidgetHostView>(); 61 private OnClickHandler mOnClickHandler; 62 63 class Callbacks extends IAppWidgetHost.Stub { 64 public void updateAppWidget(int appWidgetId, RemoteViews views, int userId) { 65 if (isLocalBinder() && views != null) { 66 views = views.clone(); 67 views.setUser(new UserHandle(userId)); 68 } 69 Message msg = mHandler.obtainMessage(HANDLE_UPDATE, appWidgetId, userId, views); 70 msg.sendToTarget(); 71 } 72 73 public void providerChanged(int appWidgetId, AppWidgetProviderInfo info, int userId) { 74 if (isLocalBinder() && info != null) { 75 info = info.clone(); 76 } 77 Message msg = mHandler.obtainMessage(HANDLE_PROVIDER_CHANGED, 78 appWidgetId, userId, info); 79 msg.sendToTarget(); 80 } 81 82 public void providersChanged(int userId) { 83 Message msg = mHandler.obtainMessage(HANDLE_PROVIDERS_CHANGED, userId, 0); 84 msg.sendToTarget(); 85 } 86 87 public void viewDataChanged(int appWidgetId, int viewId, int userId) { 88 Message msg = mHandler.obtainMessage(HANDLE_VIEW_DATA_CHANGED, 89 appWidgetId, viewId, userId); 90 msg.sendToTarget(); 91 } 92 } 93 94 class UpdateHandler extends Handler { 95 public UpdateHandler(Looper looper) { 96 super(looper); 97 } 98 99 public void handleMessage(Message msg) { 100 switch (msg.what) { 101 case HANDLE_UPDATE: { 102 updateAppWidgetView(msg.arg1, (RemoteViews)msg.obj, msg.arg2); 103 break; 104 } 105 case HANDLE_PROVIDER_CHANGED: { 106 onProviderChanged(msg.arg1, (AppWidgetProviderInfo)msg.obj); 107 break; 108 } 109 case HANDLE_PROVIDERS_CHANGED: { 110 onProvidersChanged(); 111 break; 112 } 113 case HANDLE_VIEW_DATA_CHANGED: { 114 viewDataChanged(msg.arg1, msg.arg2, (Integer) msg.obj); 115 break; 116 } 117 } 118 } 119 } 120 121 public AppWidgetHost(Context context, int hostId) { 122 this(context, hostId, null, context.getMainLooper()); 123 } 124 125 /** 126 * @hide 127 */ 128 public AppWidgetHost(Context context, int hostId, OnClickHandler handler, Looper looper) { 129 mContext = context; 130 mHostId = hostId; 131 mOnClickHandler = handler; 132 mHandler = new UpdateHandler(looper); 133 mDisplayMetrics = context.getResources().getDisplayMetrics(); 134 bindService(); 135 } 136 137 138 private static void bindService() { 139 synchronized (sServiceLock) { 140 if (sService == null) { 141 IBinder b = ServiceManager.getService(Context.APPWIDGET_SERVICE); 142 sService = IAppWidgetService.Stub.asInterface(b); 143 } 144 } 145 } 146 147 /** 148 * Start receiving onAppWidgetChanged calls for your AppWidgets. Call this when your activity 149 * becomes visible, i.e. from onStart() in your Activity. 150 */ 151 public void startListening() { 152 int[] updatedIds; 153 ArrayList<RemoteViews> updatedViews = new ArrayList<RemoteViews>(); 154 155 final int userId = mContext.getUserId(); 156 try { 157 if (mPackageName == null) { 158 mPackageName = mContext.getPackageName(); 159 } 160 updatedIds = sService.startListening( 161 mCallbacks, mPackageName, mHostId, updatedViews, userId); 162 } 163 catch (RemoteException e) { 164 throw new RuntimeException("system server dead?", e); 165 } 166 167 final int N = updatedIds.length; 168 for (int i=0; i<N; i++) { 169 if (updatedViews.get(i) != null) { 170 updatedViews.get(i).setUser(new UserHandle(userId)); 171 } 172 updateAppWidgetView(updatedIds[i], updatedViews.get(i), userId); 173 } 174 } 175 176 /** 177 * Stop receiving onAppWidgetChanged calls for your AppWidgets. Call this when your activity is 178 * no longer visible, i.e. from onStop() in your Activity. 179 */ 180 public void stopListening() { 181 try { 182 sService.stopListening(mHostId, mContext.getUserId()); 183 } 184 catch (RemoteException e) { 185 throw new RuntimeException("system server dead?", e); 186 } 187 188 // This is here because keyguard needs it since it'll be switching users after this call. 189 // If it turns out other apps need to call this often, we should re-think how this works. 190 clearViews(); 191 } 192 193 /** 194 * Get a appWidgetId for a host in the calling process. 195 * 196 * @return a appWidgetId 197 */ 198 public int allocateAppWidgetId() { 199 try { 200 if (mPackageName == null) { 201 mPackageName = mContext.getPackageName(); 202 } 203 return sService.allocateAppWidgetId(mPackageName, mHostId, mContext.getUserId()); 204 } 205 catch (RemoteException e) { 206 throw new RuntimeException("system server dead?", e); 207 } 208 } 209 210 /** 211 * Get a appWidgetId for a host in the given package. 212 * 213 * @return a appWidgetId 214 * @hide 215 */ 216 public static int allocateAppWidgetIdForPackage(int hostId, int userId, String packageName) { 217 checkCallerIsSystem(); 218 try { 219 if (sService == null) { 220 bindService(); 221 } 222 return sService.allocateAppWidgetId(packageName, hostId, userId); 223 } catch (RemoteException e) { 224 throw new RuntimeException("system server dead?", e); 225 } 226 } 227 228 /** 229 * Gets a list of all the appWidgetIds that are bound to the current host 230 * 231 * @hide 232 */ 233 public int[] getAppWidgetIds() { 234 try { 235 if (sService == null) { 236 bindService(); 237 } 238 return sService.getAppWidgetIdsForHost(mHostId, mContext.getUserId()); 239 } catch (RemoteException e) { 240 throw new RuntimeException("system server dead?", e); 241 } 242 } 243 244 private static void checkCallerIsSystem() { 245 int uid = Process.myUid(); 246 if (UserHandle.getAppId(uid) == Process.SYSTEM_UID || uid == 0) { 247 return; 248 } 249 throw new SecurityException("Disallowed call for uid " + uid); 250 } 251 252 private boolean isLocalBinder() { 253 return Process.myPid() == Binder.getCallingPid(); 254 } 255 256 /** 257 * Stop listening to changes for this AppWidget. 258 */ 259 public void deleteAppWidgetId(int appWidgetId) { 260 synchronized (mViews) { 261 mViews.remove(appWidgetId); 262 try { 263 sService.deleteAppWidgetId(appWidgetId, mContext.getUserId()); 264 } 265 catch (RemoteException e) { 266 throw new RuntimeException("system server dead?", e); 267 } 268 } 269 } 270 271 /** 272 * Stop listening to changes for this AppWidget. 273 * @hide 274 */ 275 public static void deleteAppWidgetIdForSystem(int appWidgetId, int userId) { 276 checkCallerIsSystem(); 277 try { 278 if (sService == null) { 279 bindService(); 280 } 281 sService.deleteAppWidgetId(appWidgetId, userId); 282 } catch (RemoteException e) { 283 throw new RuntimeException("system server dead?", e); 284 } 285 } 286 287 /** 288 * Remove all records about this host from the AppWidget manager. 289 * <ul> 290 * <li>Call this when initializing your database, as it might be because of a data wipe.</li> 291 * <li>Call this to have the AppWidget manager release all resources associated with your 292 * host. Any future calls about this host will cause the records to be re-allocated.</li> 293 * </ul> 294 */ 295 public void deleteHost() { 296 try { 297 sService.deleteHost(mHostId, mContext.getUserId()); 298 } 299 catch (RemoteException e) { 300 throw new RuntimeException("system server dead?", e); 301 } 302 } 303 304 /** 305 * Remove all records about all hosts for your package. 306 * <ul> 307 * <li>Call this when initializing your database, as it might be because of a data wipe.</li> 308 * <li>Call this to have the AppWidget manager release all resources associated with your 309 * host. Any future calls about this host will cause the records to be re-allocated.</li> 310 * </ul> 311 */ 312 public static void deleteAllHosts() { 313 deleteAllHosts(UserHandle.myUserId()); 314 } 315 316 /** 317 * Private method containing a userId 318 * @hide 319 */ 320 public static void deleteAllHosts(int userId) { 321 try { 322 sService.deleteAllHosts(userId); 323 } 324 catch (RemoteException e) { 325 throw new RuntimeException("system server dead?", e); 326 } 327 } 328 329 /** 330 * Create the AppWidgetHostView for the given widget. 331 * The AppWidgetHost retains a pointer to the newly-created View. 332 */ 333 public final AppWidgetHostView createView(Context context, int appWidgetId, 334 AppWidgetProviderInfo appWidget) { 335 final int userId = mContext.getUserId(); 336 AppWidgetHostView view = onCreateView(mContext, appWidgetId, appWidget); 337 view.setUserId(userId); 338 view.setOnClickHandler(mOnClickHandler); 339 view.setAppWidget(appWidgetId, appWidget); 340 synchronized (mViews) { 341 mViews.put(appWidgetId, view); 342 } 343 RemoteViews views; 344 try { 345 views = sService.getAppWidgetViews(appWidgetId, userId); 346 if (views != null) { 347 views.setUser(new UserHandle(mContext.getUserId())); 348 } 349 } catch (RemoteException e) { 350 throw new RuntimeException("system server dead?", e); 351 } 352 view.updateAppWidget(views); 353 354 return view; 355 } 356 357 /** 358 * Called to create the AppWidgetHostView. Override to return a custom subclass if you 359 * need it. {@more} 360 */ 361 protected AppWidgetHostView onCreateView(Context context, int appWidgetId, 362 AppWidgetProviderInfo appWidget) { 363 return new AppWidgetHostView(context, mOnClickHandler); 364 } 365 366 /** 367 * Called when the AppWidget provider for a AppWidget has been upgraded to a new apk. 368 */ 369 protected void onProviderChanged(int appWidgetId, AppWidgetProviderInfo appWidget) { 370 AppWidgetHostView v; 371 372 // Convert complex to dp -- we are getting the AppWidgetProviderInfo from the 373 // AppWidgetService, which doesn't have our context, hence we need to do the 374 // conversion here. 375 appWidget.minWidth = 376 TypedValue.complexToDimensionPixelSize(appWidget.minWidth, mDisplayMetrics); 377 appWidget.minHeight = 378 TypedValue.complexToDimensionPixelSize(appWidget.minHeight, mDisplayMetrics); 379 appWidget.minResizeWidth = 380 TypedValue.complexToDimensionPixelSize(appWidget.minResizeWidth, mDisplayMetrics); 381 appWidget.minResizeHeight = 382 TypedValue.complexToDimensionPixelSize(appWidget.minResizeHeight, mDisplayMetrics); 383 384 synchronized (mViews) { 385 v = mViews.get(appWidgetId); 386 } 387 if (v != null) { 388 v.resetAppWidget(appWidget); 389 } 390 } 391 392 /** 393 * Called when the set of available widgets changes (ie. widget containing packages 394 * are added, updated or removed, or widget components are enabled or disabled.) 395 */ 396 protected void onProvidersChanged() { 397 // Does nothing 398 } 399 400 void updateAppWidgetView(int appWidgetId, RemoteViews views, int userId) { 401 AppWidgetHostView v; 402 synchronized (mViews) { 403 v = mViews.get(appWidgetId); 404 } 405 if (v != null) { 406 v.updateAppWidget(views); 407 } 408 } 409 410 void viewDataChanged(int appWidgetId, int viewId, int userId) { 411 AppWidgetHostView v; 412 synchronized (mViews) { 413 v = mViews.get(appWidgetId); 414 } 415 if (v != null) { 416 v.viewDataChanged(viewId); 417 } 418 } 419 420 /** 421 * Clear the list of Views that have been created by this AppWidgetHost. 422 */ 423 protected void clearViews() { 424 mViews.clear(); 425 } 426} 427 428 429