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