• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 {
updateAppWidget(int appWidgetId, RemoteViews views, int userId)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 
providerChanged(int appWidgetId, AppWidgetProviderInfo info, int userId)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 
providersChanged(int userId)84         public void providersChanged(int userId) {
85             Message msg = mHandler.obtainMessage(HANDLE_PROVIDERS_CHANGED, userId, 0);
86             msg.sendToTarget();
87         }
88 
viewDataChanged(int appWidgetId, int viewId, int userId)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 {
UpdateHandler(Looper looper)97         public UpdateHandler(Looper looper) {
98             super(looper);
99         }
100 
handleMessage(Message msg)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 
AppWidgetHost(Context context, int hostId)123     public AppWidgetHost(Context context, int hostId) {
124         this(context, hostId, null, context.getMainLooper());
125     }
126 
127     /**
128      * @hide
129      */
AppWidgetHost(Context context, int hostId, OnClickHandler handler, Looper looper)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 
bindService()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      */
startListening()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      */
stopListening()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      */
allocateAppWidgetId()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      */
allocateAppWidgetIdForPackage(int hostId, int userId, String packageName)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      */
getAppWidgetIds()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 
checkCallerIsSystem()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 
isLocalBinder()254     private boolean isLocalBinder() {
255         return Process.myPid() == Binder.getCallingPid();
256     }
257 
258     /**
259      * Stop listening to changes for this AppWidget.
260      */
deleteAppWidgetId(int appWidgetId)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      */
deleteAppWidgetIdForSystem(int appWidgetId, int userId)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      */
deleteHost()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      */
deleteAllHosts()314     public static void deleteAllHosts() {
315         deleteAllHosts(UserHandle.myUserId());
316     }
317 
318     /**
319      * Private method containing a userId
320      * @hide
321      */
deleteAllHosts(int userId)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      */
createView(Context context, int appWidgetId, AppWidgetProviderInfo appWidget)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      */
onCreateView(Context context, int appWidgetId, AppWidgetProviderInfo appWidget)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      */
onProviderChanged(int appWidgetId, AppWidgetProviderInfo appWidget)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      */
onProvidersChanged()398     protected void onProvidersChanged() {
399         // Does nothing
400     }
401 
updateAppWidgetView(int appWidgetId, RemoteViews views, int userId)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 
viewDataChanged(int appWidgetId, int viewId, int userId)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      */
clearViews()425     protected void clearViews() {
426         mViews.clear();
427     }
428 }
429 
430 
431