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