• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2007 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.widget;
18 
19 import java.util.HashMap;
20 
21 import android.app.Service;
22 import android.content.Intent;
23 import android.os.IBinder;
24 
25 import com.android.internal.widget.IRemoteViewsFactory;
26 
27 /**
28  * The service to be connected to for a remote adapter to request RemoteViews.  Users should
29  * extend the RemoteViewsService to provide the appropriate RemoteViewsFactory's used to
30  * populate the remote collection view (ListView, GridView, etc).
31  */
32 public abstract class RemoteViewsService extends Service {
33 
34     private static final String LOG_TAG = "RemoteViewsService";
35 
36     // Used for reference counting of RemoteViewsFactories
37     // Because we are now unbinding when we are not using the Service (to allow them to be
38     // reclaimed), the references to the factories that are created need to be stored and used when
39     // the service is restarted (in response to user input for example).  When the process is
40     // destroyed, so is this static cache of RemoteViewsFactories.
41     private static final HashMap<Intent.FilterComparison, RemoteViewsFactory> sRemoteViewFactories =
42             new HashMap<Intent.FilterComparison, RemoteViewsFactory>();
43     private static final Object sLock = new Object();
44 
45     /**
46      * An interface for an adapter between a remote collection view (ListView, GridView, etc) and
47      * the underlying data for that view.  The implementor is responsible for making a RemoteView
48      * for each item in the data set. This interface is a thin wrapper around {@link Adapter}.
49      *
50      * @see android.widget.Adapter
51      * @see android.appwidget.AppWidgetManager
52      */
53     public interface RemoteViewsFactory {
54         /**
55          * Called when your factory is first constructed. The same factory may be shared across
56          * multiple RemoteViewAdapters depending on the intent passed.
57          */
onCreate()58         public void onCreate();
59 
60         /**
61          * Called when notifyDataSetChanged() is triggered on the remote adapter. This allows a
62          * RemoteViewsFactory to respond to data changes by updating any internal references.
63          *
64          * Note: expensive tasks can be safely performed synchronously within this method. In the
65          * interim, the old data will be displayed within the widget.
66          *
67          * @see android.appwidget.AppWidgetManager#notifyAppWidgetViewDataChanged(int[], int)
68          */
onDataSetChanged()69         public void onDataSetChanged();
70 
71         /**
72          * Called when the last RemoteViewsAdapter that is associated with this factory is
73          * unbound.
74          */
onDestroy()75         public void onDestroy();
76 
77         /**
78          * See {@link Adapter#getCount()}
79          *
80          * @return Count of items.
81          */
getCount()82         public int getCount();
83 
84         /**
85          * See {@link Adapter#getView(int, android.view.View, android.view.ViewGroup)}.
86          *
87          * Note: expensive tasks can be safely performed synchronously within this method, and a
88          * loading view will be displayed in the interim. See {@link #getLoadingView()}.
89          *
90          * @param position The position of the item within the Factory's data set of the item whose
91          *        view we want.
92          * @return A RemoteViews object corresponding to the data at the specified position.
93          */
getViewAt(int position)94         public RemoteViews getViewAt(int position);
95 
96         /**
97          * This allows for the use of a custom loading view which appears between the time that
98          * {@link #getViewAt(int)} is called and returns. If null is returned, a default loading
99          * view will be used.
100          *
101          * @return The RemoteViews representing the desired loading view.
102          */
getLoadingView()103         public RemoteViews getLoadingView();
104 
105         /**
106          * See {@link Adapter#getViewTypeCount()}.
107          *
108          * @return The number of types of Views that will be returned by this factory.
109          */
getViewTypeCount()110         public int getViewTypeCount();
111 
112         /**
113          * See {@link Adapter#getItemId(int)}.
114          *
115          * @param position The position of the item within the data set whose row id we want.
116          * @return The id of the item at the specified position.
117          */
getItemId(int position)118         public long getItemId(int position);
119 
120         /**
121          * See {@link Adapter#hasStableIds()}.
122          *
123          * @return True if the same id always refers to the same object.
124          */
hasStableIds()125         public boolean hasStableIds();
126     }
127 
128     /**
129      * A private proxy class for the private IRemoteViewsFactory interface through the
130      * public RemoteViewsFactory interface.
131      */
132     private static class RemoteViewsFactoryAdapter extends IRemoteViewsFactory.Stub {
RemoteViewsFactoryAdapter(RemoteViewsFactory factory, boolean isCreated)133         public RemoteViewsFactoryAdapter(RemoteViewsFactory factory, boolean isCreated) {
134             mFactory = factory;
135             mIsCreated = isCreated;
136         }
isCreated()137         public synchronized boolean isCreated() {
138             return mIsCreated;
139         }
onDataSetChanged()140         public synchronized void onDataSetChanged() {
141             try {
142                 mFactory.onDataSetChanged();
143             } catch (Exception ex) {
144                 Thread t = Thread.currentThread();
145                 Thread.getDefaultUncaughtExceptionHandler().uncaughtException(t, ex);
146             }
147         }
getCount()148         public synchronized int getCount() {
149             int count = 0;
150             try {
151                 count = mFactory.getCount();
152             } catch (Exception ex) {
153                 Thread t = Thread.currentThread();
154                 Thread.getDefaultUncaughtExceptionHandler().uncaughtException(t, ex);
155             }
156             return count;
157         }
getViewAt(int position)158         public synchronized RemoteViews getViewAt(int position) {
159             RemoteViews rv = null;
160             try {
161                 rv = mFactory.getViewAt(position);
162                 if (rv != null) {
163                     rv.setIsWidgetCollectionChild(true);
164                 }
165             } catch (Exception ex) {
166                 Thread t = Thread.currentThread();
167                 Thread.getDefaultUncaughtExceptionHandler().uncaughtException(t, ex);
168             }
169             return rv;
170         }
getLoadingView()171         public synchronized RemoteViews getLoadingView() {
172             RemoteViews rv = null;
173             try {
174                 rv = mFactory.getLoadingView();
175             } catch (Exception ex) {
176                 Thread t = Thread.currentThread();
177                 Thread.getDefaultUncaughtExceptionHandler().uncaughtException(t, ex);
178             }
179             return rv;
180         }
getViewTypeCount()181         public synchronized int getViewTypeCount() {
182             int count = 0;
183             try {
184                 count = mFactory.getViewTypeCount();
185             } catch (Exception ex) {
186                 Thread t = Thread.currentThread();
187                 Thread.getDefaultUncaughtExceptionHandler().uncaughtException(t, ex);
188             }
189             return count;
190         }
getItemId(int position)191         public synchronized long getItemId(int position) {
192             long id = 0;
193             try {
194                 id = mFactory.getItemId(position);
195             } catch (Exception ex) {
196                 Thread t = Thread.currentThread();
197                 Thread.getDefaultUncaughtExceptionHandler().uncaughtException(t, ex);
198             }
199             return id;
200         }
hasStableIds()201         public synchronized boolean hasStableIds() {
202             boolean hasStableIds = false;
203             try {
204                 hasStableIds = mFactory.hasStableIds();
205             } catch (Exception ex) {
206                 Thread t = Thread.currentThread();
207                 Thread.getDefaultUncaughtExceptionHandler().uncaughtException(t, ex);
208             }
209             return hasStableIds;
210         }
onDestroy(Intent intent)211         public void onDestroy(Intent intent) {
212             synchronized (sLock) {
213                 Intent.FilterComparison fc = new Intent.FilterComparison(intent);
214                 if (RemoteViewsService.sRemoteViewFactories.containsKey(fc)) {
215                     RemoteViewsFactory factory = RemoteViewsService.sRemoteViewFactories.get(fc);
216                     try {
217                         factory.onDestroy();
218                     } catch (Exception ex) {
219                         Thread t = Thread.currentThread();
220                         Thread.getDefaultUncaughtExceptionHandler().uncaughtException(t, ex);
221                     }
222                     RemoteViewsService.sRemoteViewFactories.remove(fc);
223                 }
224             }
225         }
226 
227         private RemoteViewsFactory mFactory;
228         private boolean mIsCreated;
229     }
230 
231     @Override
onBind(Intent intent)232     public IBinder onBind(Intent intent) {
233         synchronized (sLock) {
234             Intent.FilterComparison fc = new Intent.FilterComparison(intent);
235             RemoteViewsFactory factory = null;
236             boolean isCreated = false;
237             if (!sRemoteViewFactories.containsKey(fc)) {
238                 factory = onGetViewFactory(intent);
239                 sRemoteViewFactories.put(fc, factory);
240                 factory.onCreate();
241                 isCreated = false;
242             } else {
243                 factory = sRemoteViewFactories.get(fc);
244                 isCreated = true;
245             }
246             return new RemoteViewsFactoryAdapter(factory, isCreated);
247         }
248     }
249 
250     /**
251      * To be implemented by the derived service to generate appropriate factories for
252      * the data.
253      */
onGetViewFactory(Intent intent)254     public abstract RemoteViewsFactory onGetViewFactory(Intent intent);
255 }
256