• 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 
202         try {
203             if (mPackageName == null) {
204                 mPackageName = mContext.getPackageName();
205             }
206             return sService.allocateAppWidgetId(mPackageName, mHostId, mContext.getUserId());
207         }
208         catch (RemoteException e) {
209             throw new RuntimeException("system server dead?", e);
210         }
211     }
212 
213     /**
214      * Get a appWidgetId for a host in the calling process.
215      *
216      * @return a appWidgetId
217      * @hide
218      */
allocateAppWidgetIdForSystem(int hostId, int userId)219     public static int allocateAppWidgetIdForSystem(int hostId, int userId) {
220         checkCallerIsSystem();
221         try {
222             if (sService == null) {
223                 bindService();
224             }
225             Context systemContext =
226                     (Context) ActivityThread.currentActivityThread().getSystemContext();
227             String packageName = systemContext.getPackageName();
228             return sService.allocateAppWidgetId(packageName, hostId, userId);
229         } catch (RemoteException e) {
230             throw new RuntimeException("system server dead?", e);
231         }
232     }
233 
234     /**
235      * Gets a list of all the appWidgetIds that are bound to the current host
236      *
237      * @hide
238      */
getAppWidgetIds()239     public int[] getAppWidgetIds() {
240         try {
241             if (sService == null) {
242                 bindService();
243             }
244             return sService.getAppWidgetIdsForHost(mHostId, mContext.getUserId());
245         } catch (RemoteException e) {
246             throw new RuntimeException("system server dead?", e);
247         }
248     }
249 
checkCallerIsSystem()250     private static void checkCallerIsSystem() {
251         int uid = Process.myUid();
252         if (UserHandle.getAppId(uid) == Process.SYSTEM_UID || uid == 0) {
253             return;
254         }
255         throw new SecurityException("Disallowed call for uid " + uid);
256     }
257 
isLocalBinder()258     private boolean isLocalBinder() {
259         return Process.myPid() == Binder.getCallingPid();
260     }
261 
262     /**
263      * Stop listening to changes for this AppWidget.
264      */
deleteAppWidgetId(int appWidgetId)265     public void deleteAppWidgetId(int appWidgetId) {
266         synchronized (mViews) {
267             mViews.remove(appWidgetId);
268             try {
269                 sService.deleteAppWidgetId(appWidgetId, mContext.getUserId());
270             }
271             catch (RemoteException e) {
272                 throw new RuntimeException("system server dead?", e);
273             }
274         }
275     }
276 
277     /**
278      * Stop listening to changes for this AppWidget.
279      * @hide
280      */
deleteAppWidgetIdForSystem(int appWidgetId, int userId)281     public static void deleteAppWidgetIdForSystem(int appWidgetId, int userId) {
282         checkCallerIsSystem();
283         try {
284             if (sService == null) {
285                 bindService();
286             }
287             sService.deleteAppWidgetId(appWidgetId, userId);
288         } catch (RemoteException e) {
289             throw new RuntimeException("system server dead?", e);
290         }
291     }
292 
293     /**
294      * Remove all records about this host from the AppWidget manager.
295      * <ul>
296      *   <li>Call this when initializing your database, as it might be because of a data wipe.</li>
297      *   <li>Call this to have the AppWidget manager release all resources associated with your
298      *   host.  Any future calls about this host will cause the records to be re-allocated.</li>
299      * </ul>
300      */
deleteHost()301     public void deleteHost() {
302         try {
303             sService.deleteHost(mHostId, mContext.getUserId());
304         }
305         catch (RemoteException e) {
306             throw new RuntimeException("system server dead?", e);
307         }
308     }
309 
310     /**
311      * Remove all records about all hosts for your package.
312      * <ul>
313      *   <li>Call this when initializing your database, as it might be because of a data wipe.</li>
314      *   <li>Call this to have the AppWidget manager release all resources associated with your
315      *   host.  Any future calls about this host will cause the records to be re-allocated.</li>
316      * </ul>
317      */
deleteAllHosts()318     public static void deleteAllHosts() {
319         deleteAllHosts(UserHandle.myUserId());
320     }
321 
322     /**
323      * Private method containing a userId
324      * @hide
325      */
deleteAllHosts(int userId)326     public static void deleteAllHosts(int userId) {
327         try {
328             sService.deleteAllHosts(userId);
329         }
330         catch (RemoteException e) {
331             throw new RuntimeException("system server dead?", e);
332         }
333     }
334 
335     /**
336      * Create the AppWidgetHostView for the given widget.
337      * The AppWidgetHost retains a pointer to the newly-created View.
338      */
createView(Context context, int appWidgetId, AppWidgetProviderInfo appWidget)339     public final AppWidgetHostView createView(Context context, int appWidgetId,
340             AppWidgetProviderInfo appWidget) {
341         final int userId = mContext.getUserId();
342         AppWidgetHostView view = onCreateView(mContext, appWidgetId, appWidget);
343         view.setUserId(userId);
344         view.setOnClickHandler(mOnClickHandler);
345         view.setAppWidget(appWidgetId, appWidget);
346         synchronized (mViews) {
347             mViews.put(appWidgetId, view);
348         }
349         RemoteViews views;
350         try {
351             views = sService.getAppWidgetViews(appWidgetId, userId);
352             if (views != null) {
353                 views.setUser(new UserHandle(mContext.getUserId()));
354             }
355         } catch (RemoteException e) {
356             throw new RuntimeException("system server dead?", e);
357         }
358         view.updateAppWidget(views);
359 
360         return view;
361     }
362 
363     /**
364      * Called to create the AppWidgetHostView.  Override to return a custom subclass if you
365      * need it.  {@more}
366      */
onCreateView(Context context, int appWidgetId, AppWidgetProviderInfo appWidget)367     protected AppWidgetHostView onCreateView(Context context, int appWidgetId,
368             AppWidgetProviderInfo appWidget) {
369         return new AppWidgetHostView(context, mOnClickHandler);
370     }
371 
372     /**
373      * Called when the AppWidget provider for a AppWidget has been upgraded to a new apk.
374      */
onProviderChanged(int appWidgetId, AppWidgetProviderInfo appWidget)375     protected void onProviderChanged(int appWidgetId, AppWidgetProviderInfo appWidget) {
376         AppWidgetHostView v;
377 
378         // Convert complex to dp -- we are getting the AppWidgetProviderInfo from the
379         // AppWidgetService, which doesn't have our context, hence we need to do the
380         // conversion here.
381         appWidget.minWidth =
382             TypedValue.complexToDimensionPixelSize(appWidget.minWidth, mDisplayMetrics);
383         appWidget.minHeight =
384             TypedValue.complexToDimensionPixelSize(appWidget.minHeight, mDisplayMetrics);
385         appWidget.minResizeWidth =
386             TypedValue.complexToDimensionPixelSize(appWidget.minResizeWidth, mDisplayMetrics);
387         appWidget.minResizeHeight =
388             TypedValue.complexToDimensionPixelSize(appWidget.minResizeHeight, mDisplayMetrics);
389 
390         synchronized (mViews) {
391             v = mViews.get(appWidgetId);
392         }
393         if (v != null) {
394             v.resetAppWidget(appWidget);
395         }
396     }
397 
398     /**
399      * Called when the set of available widgets changes (ie. widget containing packages
400      * are added, updated or removed, or widget components are enabled or disabled.)
401      */
onProvidersChanged()402     protected void onProvidersChanged() {
403         // Does nothing
404     }
405 
updateAppWidgetView(int appWidgetId, RemoteViews views, int userId)406     void updateAppWidgetView(int appWidgetId, RemoteViews views, int userId) {
407         AppWidgetHostView v;
408         synchronized (mViews) {
409             v = mViews.get(appWidgetId);
410         }
411         if (v != null) {
412             v.updateAppWidget(views);
413         }
414     }
415 
viewDataChanged(int appWidgetId, int viewId, int userId)416     void viewDataChanged(int appWidgetId, int viewId, int userId) {
417         AppWidgetHostView v;
418         synchronized (mViews) {
419             v = mViews.get(appWidgetId);
420         }
421         if (v != null) {
422             v.viewDataChanged(viewId);
423         }
424     }
425 
426     /**
427      * Clear the list of Views that have been created by this AppWidgetHost.
428      */
clearViews()429     protected void clearViews() {
430         mViews.clear();
431     }
432 }
433 
434 
435