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 } onDataSetChangedAsync()148 public synchronized void onDataSetChangedAsync() { 149 onDataSetChanged(); 150 } getCount()151 public synchronized int getCount() { 152 int count = 0; 153 try { 154 count = mFactory.getCount(); 155 } catch (Exception ex) { 156 Thread t = Thread.currentThread(); 157 Thread.getDefaultUncaughtExceptionHandler().uncaughtException(t, ex); 158 } 159 return count; 160 } getViewAt(int position)161 public synchronized RemoteViews getViewAt(int position) { 162 RemoteViews rv = null; 163 try { 164 rv = mFactory.getViewAt(position); 165 if (rv != null) { 166 rv.setIsWidgetCollectionChild(true); 167 } 168 } catch (Exception ex) { 169 Thread t = Thread.currentThread(); 170 Thread.getDefaultUncaughtExceptionHandler().uncaughtException(t, ex); 171 } 172 return rv; 173 } getLoadingView()174 public synchronized RemoteViews getLoadingView() { 175 RemoteViews rv = null; 176 try { 177 rv = mFactory.getLoadingView(); 178 } catch (Exception ex) { 179 Thread t = Thread.currentThread(); 180 Thread.getDefaultUncaughtExceptionHandler().uncaughtException(t, ex); 181 } 182 return rv; 183 } getViewTypeCount()184 public synchronized int getViewTypeCount() { 185 int count = 0; 186 try { 187 count = mFactory.getViewTypeCount(); 188 } catch (Exception ex) { 189 Thread t = Thread.currentThread(); 190 Thread.getDefaultUncaughtExceptionHandler().uncaughtException(t, ex); 191 } 192 return count; 193 } getItemId(int position)194 public synchronized long getItemId(int position) { 195 long id = 0; 196 try { 197 id = mFactory.getItemId(position); 198 } catch (Exception ex) { 199 Thread t = Thread.currentThread(); 200 Thread.getDefaultUncaughtExceptionHandler().uncaughtException(t, ex); 201 } 202 return id; 203 } hasStableIds()204 public synchronized boolean hasStableIds() { 205 boolean hasStableIds = false; 206 try { 207 hasStableIds = mFactory.hasStableIds(); 208 } catch (Exception ex) { 209 Thread t = Thread.currentThread(); 210 Thread.getDefaultUncaughtExceptionHandler().uncaughtException(t, ex); 211 } 212 return hasStableIds; 213 } onDestroy(Intent intent)214 public void onDestroy(Intent intent) { 215 synchronized (sLock) { 216 Intent.FilterComparison fc = new Intent.FilterComparison(intent); 217 if (RemoteViewsService.sRemoteViewFactories.containsKey(fc)) { 218 RemoteViewsFactory factory = RemoteViewsService.sRemoteViewFactories.get(fc); 219 try { 220 factory.onDestroy(); 221 } catch (Exception ex) { 222 Thread t = Thread.currentThread(); 223 Thread.getDefaultUncaughtExceptionHandler().uncaughtException(t, ex); 224 } 225 RemoteViewsService.sRemoteViewFactories.remove(fc); 226 } 227 } 228 } 229 230 private RemoteViewsFactory mFactory; 231 private boolean mIsCreated; 232 } 233 234 @Override onBind(Intent intent)235 public IBinder onBind(Intent intent) { 236 synchronized (sLock) { 237 Intent.FilterComparison fc = new Intent.FilterComparison(intent); 238 RemoteViewsFactory factory = null; 239 boolean isCreated = false; 240 if (!sRemoteViewFactories.containsKey(fc)) { 241 factory = onGetViewFactory(intent); 242 sRemoteViewFactories.put(fc, factory); 243 factory.onCreate(); 244 isCreated = false; 245 } else { 246 factory = sRemoteViewFactories.get(fc); 247 isCreated = true; 248 } 249 return new RemoteViewsFactoryAdapter(factory, isCreated); 250 } 251 } 252 253 /** 254 * To be implemented by the derived service to generate appropriate factories for 255 * the data. 256 */ onGetViewFactory(Intent intent)257 public abstract RemoteViewsFactory onGetViewFactory(Intent intent); 258 } 259