blob: cb61a71b147afad655e2b7ed25cb0c073a6c946e [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
Kenny Root15a4d2f2010-03-11 18:20:12 -08002 * Copyright (C) 2009 The Android Open Source Project
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003 *
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
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070017package android.appwidget;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080018
Adam Cohend2e20de2011-02-25 12:03:37 -080019import java.util.ArrayList;
20import java.util.HashMap;
21
Adam Cohen60264732012-09-18 13:39:42 -070022import android.app.ActivityThread;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080023import android.content.Context;
Adam Cohen3ff2d862012-09-26 14:07:57 -070024import android.os.Binder;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080025import android.os.Handler;
26import android.os.IBinder;
27import android.os.Looper;
28import android.os.Message;
Jim Millerf229e4d2012-09-12 20:32:50 -070029import android.os.Process;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080030import android.os.RemoteException;
31import android.os.ServiceManager;
Jim Millerf229e4d2012-09-12 20:32:50 -070032import android.os.UserHandle;
Adam Cohend2e20de2011-02-25 12:03:37 -080033import android.util.DisplayMetrics;
34import android.util.TypedValue;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080035import android.widget.RemoteViews;
Jim Millere667a7a2012-08-09 19:22:32 -070036import android.widget.RemoteViews.OnClickHandler;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080037
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070038import com.android.internal.appwidget.IAppWidgetHost;
39import com.android.internal.appwidget.IAppWidgetService;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080040
41/**
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070042 * AppWidgetHost provides the interaction with the AppWidget service for apps,
43 * like the home screen, that want to embed AppWidgets in their UI.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080044 */
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070045public class AppWidgetHost {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080046
47 static final int HANDLE_UPDATE = 1;
48 static final int HANDLE_PROVIDER_CHANGED = 2;
Winson Chung7fbd2842012-06-13 10:35:51 -070049 static final int HANDLE_PROVIDERS_CHANGED = 3;
50 static final int HANDLE_VIEW_DATA_CHANGED = 4;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080051
Romain Guya5475592009-07-01 17:20:08 -070052 final static Object sServiceLock = new Object();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070053 static IAppWidgetService sService;
Adam Cohend2e20de2011-02-25 12:03:37 -080054 private DisplayMetrics mDisplayMetrics;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080055
56 Context mContext;
57 String mPackageName;
58
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070059 class Callbacks extends IAppWidgetHost.Stub {
60 public void updateAppWidget(int appWidgetId, RemoteViews views) {
Adam Cohen3ff2d862012-09-26 14:07:57 -070061 if (isLocalBinder() && views != null) {
62 views = views.clone();
63 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080064 Message msg = mHandler.obtainMessage(HANDLE_UPDATE);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070065 msg.arg1 = appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080066 msg.obj = views;
67 msg.sendToTarget();
68 }
69
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070070 public void providerChanged(int appWidgetId, AppWidgetProviderInfo info) {
Adam Cohen3ff2d862012-09-26 14:07:57 -070071 if (isLocalBinder() && info != null) {
72 info = info.clone();
73 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080074 Message msg = mHandler.obtainMessage(HANDLE_PROVIDER_CHANGED);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070075 msg.arg1 = appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080076 msg.obj = info;
77 msg.sendToTarget();
78 }
Winson Chung499cb9f2010-07-16 11:18:17 -070079
Winson Chung7fbd2842012-06-13 10:35:51 -070080 public void providersChanged() {
81 Message msg = mHandler.obtainMessage(HANDLE_PROVIDERS_CHANGED);
82 msg.sendToTarget();
83 }
84
Winson Chung6394c0e2010-08-16 10:14:56 -070085 public void viewDataChanged(int appWidgetId, int viewId) {
Winson Chung499cb9f2010-07-16 11:18:17 -070086 Message msg = mHandler.obtainMessage(HANDLE_VIEW_DATA_CHANGED);
87 msg.arg1 = appWidgetId;
88 msg.arg2 = viewId;
Winson Chung499cb9f2010-07-16 11:18:17 -070089 msg.sendToTarget();
90 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080091 }
92
93 class UpdateHandler extends Handler {
94 public UpdateHandler(Looper looper) {
95 super(looper);
96 }
Jim Millere667a7a2012-08-09 19:22:32 -070097
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080098 public void handleMessage(Message msg) {
99 switch (msg.what) {
100 case HANDLE_UPDATE: {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700101 updateAppWidgetView(msg.arg1, (RemoteViews)msg.obj);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800102 break;
103 }
104 case HANDLE_PROVIDER_CHANGED: {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700105 onProviderChanged(msg.arg1, (AppWidgetProviderInfo)msg.obj);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800106 break;
107 }
Winson Chung7fbd2842012-06-13 10:35:51 -0700108 case HANDLE_PROVIDERS_CHANGED: {
109 onProvidersChanged();
110 break;
111 }
Winson Chung499cb9f2010-07-16 11:18:17 -0700112 case HANDLE_VIEW_DATA_CHANGED: {
Winson Chung6394c0e2010-08-16 10:14:56 -0700113 viewDataChanged(msg.arg1, msg.arg2);
Winson Chung499cb9f2010-07-16 11:18:17 -0700114 break;
115 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800116 }
117 }
118 }
Jim Millere667a7a2012-08-09 19:22:32 -0700119
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800120 Handler mHandler;
121
122 int mHostId;
123 Callbacks mCallbacks = new Callbacks();
Romain Guya5475592009-07-01 17:20:08 -0700124 final HashMap<Integer,AppWidgetHostView> mViews = new HashMap<Integer, AppWidgetHostView>();
Jim Millere667a7a2012-08-09 19:22:32 -0700125 private OnClickHandler mOnClickHandler;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800126
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700127 public AppWidgetHost(Context context, int hostId) {
Michael Jurkae6d55452012-09-17 17:30:16 -0700128 this(context, hostId, null, context.getMainLooper());
Jim Millere667a7a2012-08-09 19:22:32 -0700129 }
130
131 /**
132 * @hide
133 */
Jim Millerf229e4d2012-09-12 20:32:50 -0700134 public AppWidgetHost(Context context, int hostId, OnClickHandler handler, Looper looper) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800135 mContext = context;
136 mHostId = hostId;
Jim Millere667a7a2012-08-09 19:22:32 -0700137 mOnClickHandler = handler;
Jim Millerf229e4d2012-09-12 20:32:50 -0700138 mHandler = new UpdateHandler(looper);
Adam Cohend2e20de2011-02-25 12:03:37 -0800139 mDisplayMetrics = context.getResources().getDisplayMetrics();
Jim Millerf229e4d2012-09-12 20:32:50 -0700140 bindService();
141 }
142
143 private static void bindService() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800144 synchronized (sServiceLock) {
145 if (sService == null) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700146 IBinder b = ServiceManager.getService(Context.APPWIDGET_SERVICE);
147 sService = IAppWidgetService.Stub.asInterface(b);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800148 }
149 }
150 }
151
152 /**
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700153 * Start receiving onAppWidgetChanged calls for your AppWidgets. Call this when your activity
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800154 * becomes visible, i.e. from onStart() in your Activity.
155 */
156 public void startListening() {
Romain Guya5475592009-07-01 17:20:08 -0700157 int[] updatedIds;
158 ArrayList<RemoteViews> updatedViews = new ArrayList<RemoteViews>();
Jim Millere667a7a2012-08-09 19:22:32 -0700159
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800160 try {
161 if (mPackageName == null) {
162 mPackageName = mContext.getPackageName();
163 }
164 updatedIds = sService.startListening(mCallbacks, mPackageName, mHostId, updatedViews);
165 }
166 catch (RemoteException e) {
167 throw new RuntimeException("system server dead?", e);
168 }
169
170 final int N = updatedIds.length;
171 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700172 updateAppWidgetView(updatedIds[i], updatedViews.get(i));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800173 }
174 }
175
176 /**
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700177 * Stop receiving onAppWidgetChanged calls for your AppWidgets. Call this when your activity is
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800178 * no longer visible, i.e. from onStop() in your Activity.
179 */
180 public void stopListening() {
181 try {
182 sService.stopListening(mHostId);
183 }
184 catch (RemoteException e) {
185 throw new RuntimeException("system server dead?", e);
186 }
187 }
188
189 /**
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700190 * Get a appWidgetId for a host in the calling process.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800191 *
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700192 * @return a appWidgetId
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800193 */
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700194 public int allocateAppWidgetId() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800195 try {
196 if (mPackageName == null) {
197 mPackageName = mContext.getPackageName();
198 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700199 return sService.allocateAppWidgetId(mPackageName, mHostId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800200 }
201 catch (RemoteException e) {
202 throw new RuntimeException("system server dead?", e);
203 }
204 }
205
206 /**
Jim Millerf229e4d2012-09-12 20:32:50 -0700207 * Get a appWidgetId for a host in the calling process.
208 *
209 * @return a appWidgetId
210 * @hide
211 */
Adam Cohen60264732012-09-18 13:39:42 -0700212 public static int allocateAppWidgetIdForSystem(int hostId) {
Jim Millerf229e4d2012-09-12 20:32:50 -0700213 checkCallerIsSystem();
214 try {
215 if (sService == null) {
216 bindService();
217 }
Adam Cohen60264732012-09-18 13:39:42 -0700218 Context systemContext =
219 (Context) ActivityThread.currentActivityThread().getSystemContext();
220 String packageName = systemContext.getPackageName();
Jim Millerf229e4d2012-09-12 20:32:50 -0700221 return sService.allocateAppWidgetId(packageName, hostId);
222 } catch (RemoteException e) {
223 throw new RuntimeException("system server dead?", e);
224 }
225 }
226
227 private static void checkCallerIsSystem() {
228 int uid = Process.myUid();
229 if (UserHandle.getAppId(uid) == Process.SYSTEM_UID || uid == 0) {
230 return;
231 }
232 throw new SecurityException("Disallowed call for uid " + uid);
233 }
234
Adam Cohen3ff2d862012-09-26 14:07:57 -0700235 private boolean isLocalBinder() {
236 return Process.myPid() == Binder.getCallingPid();
237 }
238
Jim Millerf229e4d2012-09-12 20:32:50 -0700239 /**
Jim Millere667a7a2012-08-09 19:22:32 -0700240 * Stop listening to changes for this AppWidget.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800241 */
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700242 public void deleteAppWidgetId(int appWidgetId) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800243 synchronized (mViews) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700244 mViews.remove(appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800245 try {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700246 sService.deleteAppWidgetId(appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800247 }
248 catch (RemoteException e) {
249 throw new RuntimeException("system server dead?", e);
250 }
251 }
252 }
253
254 /**
Jim Millerf229e4d2012-09-12 20:32:50 -0700255 * Stop listening to changes for this AppWidget.
256 * @hide
257 */
Adam Cohen60264732012-09-18 13:39:42 -0700258 public static void deleteAppWidgetIdForSystem(int appWidgetId) {
Jim Millerf229e4d2012-09-12 20:32:50 -0700259 checkCallerIsSystem();
260 try {
261 if (sService == null) {
262 bindService();
263 }
264 sService.deleteAppWidgetId(appWidgetId);
265 } catch (RemoteException e) {
266 throw new RuntimeException("system server dead?", e);
267 }
268 }
269
270 /**
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700271 * Remove all records about this host from the AppWidget manager.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800272 * <ul>
273 * <li>Call this when initializing your database, as it might be because of a data wipe.</li>
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700274 * <li>Call this to have the AppWidget manager release all resources associated with your
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800275 * host. Any future calls about this host will cause the records to be re-allocated.</li>
276 * </ul>
277 */
278 public void deleteHost() {
279 try {
280 sService.deleteHost(mHostId);
281 }
282 catch (RemoteException e) {
283 throw new RuntimeException("system server dead?", e);
284 }
285 }
286
287 /**
288 * Remove all records about all hosts for your package.
289 * <ul>
290 * <li>Call this when initializing your database, as it might be because of a data wipe.</li>
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700291 * <li>Call this to have the AppWidget manager release all resources associated with your
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800292 * host. Any future calls about this host will cause the records to be re-allocated.</li>
293 * </ul>
294 */
295 public static void deleteAllHosts() {
296 try {
297 sService.deleteAllHosts();
298 }
299 catch (RemoteException e) {
300 throw new RuntimeException("system server dead?", e);
301 }
302 }
303
Patrick Dubroyec84c3a2011-01-13 17:55:37 -0800304 /**
305 * Create the AppWidgetHostView for the given widget.
306 * The AppWidgetHost retains a pointer to the newly-created View.
307 */
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700308 public final AppWidgetHostView createView(Context context, int appWidgetId,
309 AppWidgetProviderInfo appWidget) {
310 AppWidgetHostView view = onCreateView(context, appWidgetId, appWidget);
Jim Millere667a7a2012-08-09 19:22:32 -0700311 view.setOnClickHandler(mOnClickHandler);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700312 view.setAppWidget(appWidgetId, appWidget);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800313 synchronized (mViews) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700314 mViews.put(appWidgetId, view);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800315 }
Romain Guya5475592009-07-01 17:20:08 -0700316 RemoteViews views;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800317 try {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700318 views = sService.getAppWidgetViews(appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800319 } catch (RemoteException e) {
320 throw new RuntimeException("system server dead?", e);
321 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700322 view.updateAppWidget(views);
Jim Millere667a7a2012-08-09 19:22:32 -0700323
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800324 return view;
325 }
326
327 /**
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700328 * Called to create the AppWidgetHostView. Override to return a custom subclass if you
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800329 * need it. {@more}
330 */
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700331 protected AppWidgetHostView onCreateView(Context context, int appWidgetId,
332 AppWidgetProviderInfo appWidget) {
Jim Millere667a7a2012-08-09 19:22:32 -0700333 return new AppWidgetHostView(context, mOnClickHandler);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800334 }
Adam Cohend2e20de2011-02-25 12:03:37 -0800335
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800336 /**
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700337 * Called when the AppWidget provider for a AppWidget has been upgraded to a new apk.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800338 */
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700339 protected void onProviderChanged(int appWidgetId, AppWidgetProviderInfo appWidget) {
Joe Onoratof140be62010-05-04 11:49:12 -0700340 AppWidgetHostView v;
Adam Cohend2e20de2011-02-25 12:03:37 -0800341
342 // Convert complex to dp -- we are getting the AppWidgetProviderInfo from the
Jim Millere667a7a2012-08-09 19:22:32 -0700343 // AppWidgetService, which doesn't have our context, hence we need to do the
Adam Cohend2e20de2011-02-25 12:03:37 -0800344 // conversion here.
345 appWidget.minWidth =
346 TypedValue.complexToDimensionPixelSize(appWidget.minWidth, mDisplayMetrics);
347 appWidget.minHeight =
348 TypedValue.complexToDimensionPixelSize(appWidget.minHeight, mDisplayMetrics);
Adam Cohen1bfaf562011-07-19 18:05:33 -0700349 appWidget.minResizeWidth =
350 TypedValue.complexToDimensionPixelSize(appWidget.minResizeWidth, mDisplayMetrics);
351 appWidget.minResizeHeight =
352 TypedValue.complexToDimensionPixelSize(appWidget.minResizeHeight, mDisplayMetrics);
Adam Cohend2e20de2011-02-25 12:03:37 -0800353
Joe Onoratof140be62010-05-04 11:49:12 -0700354 synchronized (mViews) {
355 v = mViews.get(appWidgetId);
356 }
357 if (v != null) {
Joe Onoratoc27bb552010-06-23 17:44:30 -0700358 v.resetAppWidget(appWidget);
Joe Onoratof140be62010-05-04 11:49:12 -0700359 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800360 }
361
Winson Chung7fbd2842012-06-13 10:35:51 -0700362 /**
363 * Called when the set of available widgets changes (ie. widget containing packages
364 * are added, updated or removed, or widget components are enabled or disabled.)
365 */
366 protected void onProvidersChanged() {
367 // Do nothing
368 }
369
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700370 void updateAppWidgetView(int appWidgetId, RemoteViews views) {
371 AppWidgetHostView v;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800372 synchronized (mViews) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700373 v = mViews.get(appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800374 }
375 if (v != null) {
Joe Onoratoc27bb552010-06-23 17:44:30 -0700376 v.updateAppWidget(views);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800377 }
378 }
Winson Chung499cb9f2010-07-16 11:18:17 -0700379
Winson Chung6394c0e2010-08-16 10:14:56 -0700380 void viewDataChanged(int appWidgetId, int viewId) {
Winson Chung499cb9f2010-07-16 11:18:17 -0700381 AppWidgetHostView v;
382 synchronized (mViews) {
383 v = mViews.get(appWidgetId);
384 }
385 if (v != null) {
Winson Chung6394c0e2010-08-16 10:14:56 -0700386 v.viewDataChanged(viewId);
Winson Chung499cb9f2010-07-16 11:18:17 -0700387 }
388 }
Patrick Dubroyec84c3a2011-01-13 17:55:37 -0800389
390 /**
391 * Clear the list of Views that have been created by this AppWidgetHost.
392 */
393 protected void clearViews() {
394 mViews.clear();
395 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800396}
397
398