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