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.Handler; 24import android.os.IBinder; 25import android.os.Looper; 26import android.os.Message; 27import android.os.RemoteException; 28import android.os.ServiceManager; 29import android.util.DisplayMetrics; 30import android.util.TypedValue; 31import android.widget.RemoteViews; 32 33import com.android.internal.appwidget.IAppWidgetHost; 34import com.android.internal.appwidget.IAppWidgetService; 35 36/** 37 * AppWidgetHost provides the interaction with the AppWidget service for apps, 38 * like the home screen, that want to embed AppWidgets in their UI. 39 */ 40public class AppWidgetHost { 41 42 static final int HANDLE_UPDATE = 1; 43 static final int HANDLE_PROVIDER_CHANGED = 2; 44 static final int HANDLE_VIEW_DATA_CHANGED = 3; 45 46 final static Object sServiceLock = new Object(); 47 static IAppWidgetService sService; 48 private DisplayMetrics mDisplayMetrics; 49 50 Context mContext; 51 String mPackageName; 52 53 class Callbacks extends IAppWidgetHost.Stub { 54 public void updateAppWidget(int appWidgetId, RemoteViews views) { 55 Message msg = mHandler.obtainMessage(HANDLE_UPDATE); 56 msg.arg1 = appWidgetId; 57 msg.obj = views; 58 msg.sendToTarget(); 59 } 60 61 public void providerChanged(int appWidgetId, AppWidgetProviderInfo info) { 62 Message msg = mHandler.obtainMessage(HANDLE_PROVIDER_CHANGED); 63 msg.arg1 = appWidgetId; 64 msg.obj = info; 65 msg.sendToTarget(); 66 } 67 68 public void viewDataChanged(int appWidgetId, int viewId) { 69 Message msg = mHandler.obtainMessage(HANDLE_VIEW_DATA_CHANGED); 70 msg.arg1 = appWidgetId; 71 msg.arg2 = viewId; 72 msg.sendToTarget(); 73 } 74 } 75 76 class UpdateHandler extends Handler { 77 public UpdateHandler(Looper looper) { 78 super(looper); 79 } 80 81 public void handleMessage(Message msg) { 82 switch (msg.what) { 83 case HANDLE_UPDATE: { 84 updateAppWidgetView(msg.arg1, (RemoteViews)msg.obj); 85 break; 86 } 87 case HANDLE_PROVIDER_CHANGED: { 88 onProviderChanged(msg.arg1, (AppWidgetProviderInfo)msg.obj); 89 break; 90 } 91 case HANDLE_VIEW_DATA_CHANGED: { 92 viewDataChanged(msg.arg1, msg.arg2); 93 break; 94 } 95 } 96 } 97 } 98 99 Handler mHandler; 100 101 int mHostId; 102 Callbacks mCallbacks = new Callbacks(); 103 final HashMap<Integer,AppWidgetHostView> mViews = new HashMap<Integer, AppWidgetHostView>(); 104 105 public AppWidgetHost(Context context, int hostId) { 106 mContext = context; 107 mHostId = hostId; 108 mHandler = new UpdateHandler(context.getMainLooper()); 109 mDisplayMetrics = context.getResources().getDisplayMetrics(); 110 synchronized (sServiceLock) { 111 if (sService == null) { 112 IBinder b = ServiceManager.getService(Context.APPWIDGET_SERVICE); 113 sService = IAppWidgetService.Stub.asInterface(b); 114 } 115 } 116 } 117 118 /** 119 * Start receiving onAppWidgetChanged calls for your AppWidgets. Call this when your activity 120 * becomes visible, i.e. from onStart() in your Activity. 121 */ 122 public void startListening() { 123 int[] updatedIds; 124 ArrayList<RemoteViews> updatedViews = new ArrayList<RemoteViews>(); 125 126 try { 127 if (mPackageName == null) { 128 mPackageName = mContext.getPackageName(); 129 } 130 updatedIds = sService.startListening(mCallbacks, mPackageName, mHostId, updatedViews); 131 } 132 catch (RemoteException e) { 133 throw new RuntimeException("system server dead?", e); 134 } 135 136 final int N = updatedIds.length; 137 for (int i=0; i<N; i++) { 138 updateAppWidgetView(updatedIds[i], updatedViews.get(i)); 139 } 140 } 141 142 /** 143 * Stop receiving onAppWidgetChanged calls for your AppWidgets. Call this when your activity is 144 * no longer visible, i.e. from onStop() in your Activity. 145 */ 146 public void stopListening() { 147 try { 148 sService.stopListening(mHostId); 149 } 150 catch (RemoteException e) { 151 throw new RuntimeException("system server dead?", e); 152 } 153 } 154 155 /** 156 * Get a appWidgetId for a host in the calling process. 157 * 158 * @return a appWidgetId 159 */ 160 public int allocateAppWidgetId() { 161 try { 162 if (mPackageName == null) { 163 mPackageName = mContext.getPackageName(); 164 } 165 return sService.allocateAppWidgetId(mPackageName, mHostId); 166 } 167 catch (RemoteException e) { 168 throw new RuntimeException("system server dead?", e); 169 } 170 } 171 172 /** 173 * Stop listening to changes for this AppWidget. 174 */ 175 public void deleteAppWidgetId(int appWidgetId) { 176 synchronized (mViews) { 177 mViews.remove(appWidgetId); 178 try { 179 sService.deleteAppWidgetId(appWidgetId); 180 } 181 catch (RemoteException e) { 182 throw new RuntimeException("system server dead?", e); 183 } 184 } 185 } 186 187 /** 188 * Remove all records about this host from the AppWidget manager. 189 * <ul> 190 * <li>Call this when initializing your database, as it might be because of a data wipe.</li> 191 * <li>Call this to have the AppWidget manager release all resources associated with your 192 * host. Any future calls about this host will cause the records to be re-allocated.</li> 193 * </ul> 194 */ 195 public void deleteHost() { 196 try { 197 sService.deleteHost(mHostId); 198 } 199 catch (RemoteException e) { 200 throw new RuntimeException("system server dead?", e); 201 } 202 } 203 204 /** 205 * Remove all records about all hosts for your package. 206 * <ul> 207 * <li>Call this when initializing your database, as it might be because of a data wipe.</li> 208 * <li>Call this to have the AppWidget manager release all resources associated with your 209 * host. Any future calls about this host will cause the records to be re-allocated.</li> 210 * </ul> 211 */ 212 public static void deleteAllHosts() { 213 try { 214 sService.deleteAllHosts(); 215 } 216 catch (RemoteException e) { 217 throw new RuntimeException("system server dead?", e); 218 } 219 } 220 221 /** 222 * Create the AppWidgetHostView for the given widget. 223 * The AppWidgetHost retains a pointer to the newly-created View. 224 */ 225 public final AppWidgetHostView createView(Context context, int appWidgetId, 226 AppWidgetProviderInfo appWidget) { 227 AppWidgetHostView view = onCreateView(context, appWidgetId, appWidget); 228 view.setAppWidget(appWidgetId, appWidget); 229 synchronized (mViews) { 230 mViews.put(appWidgetId, view); 231 } 232 RemoteViews views; 233 try { 234 views = sService.getAppWidgetViews(appWidgetId); 235 } catch (RemoteException e) { 236 throw new RuntimeException("system server dead?", e); 237 } 238 view.updateAppWidget(views); 239 return view; 240 } 241 242 /** 243 * Called to create the AppWidgetHostView. Override to return a custom subclass if you 244 * need it. {@more} 245 */ 246 protected AppWidgetHostView onCreateView(Context context, int appWidgetId, 247 AppWidgetProviderInfo appWidget) { 248 return new AppWidgetHostView(context); 249 } 250 251 /** 252 * Called when the AppWidget provider for a AppWidget has been upgraded to a new apk. 253 */ 254 protected void onProviderChanged(int appWidgetId, AppWidgetProviderInfo appWidget) { 255 AppWidgetHostView v; 256 257 // Convert complex to dp -- we are getting the AppWidgetProviderInfo from the 258 // AppWidgetService, which doesn't have our context, hence we need to do the 259 // conversion here. 260 appWidget.minWidth = 261 TypedValue.complexToDimensionPixelSize(appWidget.minWidth, mDisplayMetrics); 262 appWidget.minHeight = 263 TypedValue.complexToDimensionPixelSize(appWidget.minHeight, mDisplayMetrics); 264 appWidget.minResizeWidth = 265 TypedValue.complexToDimensionPixelSize(appWidget.minResizeWidth, mDisplayMetrics); 266 appWidget.minResizeHeight = 267 TypedValue.complexToDimensionPixelSize(appWidget.minResizeHeight, mDisplayMetrics); 268 269 synchronized (mViews) { 270 v = mViews.get(appWidgetId); 271 } 272 if (v != null) { 273 v.resetAppWidget(appWidget); 274 } 275 } 276 277 void updateAppWidgetView(int appWidgetId, RemoteViews views) { 278 AppWidgetHostView v; 279 synchronized (mViews) { 280 v = mViews.get(appWidgetId); 281 } 282 if (v != null) { 283 v.updateAppWidget(views); 284 } 285 } 286 287 void viewDataChanged(int appWidgetId, int viewId) { 288 AppWidgetHostView v; 289 synchronized (mViews) { 290 v = mViews.get(appWidgetId); 291 } 292 if (v != null) { 293 v.viewDataChanged(viewId); 294 } 295 } 296 297 /** 298 * Clear the list of Views that have been created by this AppWidgetHost. 299 */ 300 protected void clearViews() { 301 mViews.clear(); 302 } 303} 304 305 306