1 package com.xtremelabs.robolectric.shadows; 2 3 import android.app.Activity; 4 import android.app.Application; 5 import android.appwidget.AppWidgetManager; 6 import android.appwidget.AppWidgetProvider; 7 import android.content.ComponentName; 8 import android.content.Context; 9 import android.view.View; 10 import android.widget.RemoteViews; 11 import com.xtremelabs.robolectric.internal.AppSingletonizer; 12 import com.xtremelabs.robolectric.internal.Implementation; 13 import com.xtremelabs.robolectric.internal.Implements; 14 import com.xtremelabs.robolectric.internal.RealObject; 15 16 import java.util.ArrayList; 17 import java.util.HashMap; 18 import java.util.List; 19 import java.util.Map; 20 21 import static com.xtremelabs.robolectric.Robolectric.newInstanceOf; 22 import static com.xtremelabs.robolectric.Robolectric.shadowOf; 23 24 @SuppressWarnings({"UnusedDeclaration"}) 25 @Implements(AppWidgetManager.class) 26 public class ShadowAppWidgetManager { 27 private static AppSingletonizer<AppWidgetManager> instances = new AppSingletonizer<AppWidgetManager>(AppWidgetManager.class) { 28 @Override 29 protected AppWidgetManager get(ShadowApplication shadowApplication) { 30 return shadowApplication.appWidgetManager; 31 } 32 33 @Override 34 protected void set(ShadowApplication shadowApplication, AppWidgetManager instance) { 35 shadowApplication.appWidgetManager = instance; 36 } 37 38 @Override 39 protected AppWidgetManager createInstance(Application applicationContext) { 40 AppWidgetManager appWidgetManager = super.createInstance(applicationContext); 41 shadowOf(appWidgetManager).context = applicationContext; 42 return appWidgetManager; 43 } 44 }; 45 46 @RealObject 47 private AppWidgetManager realAppWidgetManager; 48 49 private Context context; 50 private Map<Integer, WidgetInfo> widgetInfos = new HashMap<Integer, WidgetInfo>(); 51 private int nextWidgetId = 1; 52 private boolean alwaysRecreateViewsDuringUpdate = false; 53 bind(AppWidgetManager appWidgetManager, Context context)54 private static void bind(AppWidgetManager appWidgetManager, Context context) { 55 // todo: implement 56 } 57 58 59 /** 60 * Finds or creates an {@code AppWidgetManager} for the given {@code context} 61 * 62 * @param context the {@code context} for which to produce an assoicated {@code AppWidgetManager} 63 * @return the {@code AppWidgetManager} associated with the given {@code context} 64 */ 65 @Implementation getInstance(Context context)66 public static AppWidgetManager getInstance(Context context) { 67 return instances.getInstance(context); 68 } 69 70 @Implementation updateAppWidget(int[] appWidgetIds, RemoteViews views)71 public void updateAppWidget(int[] appWidgetIds, RemoteViews views) { 72 for (int appWidgetId : appWidgetIds) { 73 updateAppWidget(appWidgetId, views); 74 } 75 } 76 77 /** 78 * Simulates updating an {@code AppWidget} with a new set of views 79 * 80 * @param appWidgetId id of widget 81 * @param views views to update 82 */ 83 @Implementation updateAppWidget(int appWidgetId, RemoteViews views)84 public void updateAppWidget(int appWidgetId, RemoteViews views) { 85 WidgetInfo widgetInfo = getWidgetInfo(appWidgetId); 86 int layoutId = views.getLayoutId(); 87 if (widgetInfo.layoutId != layoutId || alwaysRecreateViewsDuringUpdate) { 88 widgetInfo.view = createWidgetView(layoutId); 89 widgetInfo.layoutId = layoutId; 90 } 91 widgetInfo.lastRemoteViews = views; 92 views.reapply(context, widgetInfo.view); 93 } 94 95 @Implementation getAppWidgetIds(ComponentName provider)96 public int[] getAppWidgetIds(ComponentName provider) { 97 List<Integer> idList = new ArrayList<Integer>(); 98 for (int id : widgetInfos.keySet()) { 99 WidgetInfo widgetInfo = widgetInfos.get(id); 100 String widgetClass = widgetInfo.appWidgetProvider.getClass().getName(); 101 String widgetPackage = widgetInfo.appWidgetProvider.getClass().getPackage().getName(); 102 if (provider.getClassName().equals(widgetClass) && provider.getPackageName().equals(widgetPackage)) { 103 idList.add(id); 104 } 105 } 106 int ids[] = new int[idList.size()]; 107 for (int i = 0; i < idList.size(); i++) { 108 ids[i] = idList.get(i); 109 } 110 return ids; 111 } 112 113 /** 114 * Triggers a reapplication of the most recent set of actions against the widget, which is what happens when the 115 * phone is rotated. Does not attempt to simulate a change in screen geometry. 116 * 117 * @param appWidgetId the ID of the widget to be affected 118 */ reconstructWidgetViewAsIfPhoneWasRotated(int appWidgetId)119 public void reconstructWidgetViewAsIfPhoneWasRotated(int appWidgetId) { 120 WidgetInfo widgetInfo = getWidgetInfo(appWidgetId); 121 widgetInfo.view = createWidgetView(widgetInfo.layoutId); 122 widgetInfo.lastRemoteViews.reapply(context, widgetInfo.view); 123 } 124 125 /** 126 * Creates a widget by inflating its layout. 127 * 128 * @param appWidgetProviderClass the app widget provider class 129 * @param widgetLayoutId id of the layout to inflate 130 * @return the ID of the new widget 131 */ createWidget(Class<? extends AppWidgetProvider> appWidgetProviderClass, int widgetLayoutId)132 public int createWidget(Class<? extends AppWidgetProvider> appWidgetProviderClass, int widgetLayoutId) { 133 return createWidgets(appWidgetProviderClass, widgetLayoutId, 1)[0]; 134 } 135 136 /** 137 * Creates a bunch of widgets by inflating the same layout multiple times. 138 * 139 * @param appWidgetProviderClass the app widget provider class 140 * @param widgetLayoutId id of the layout to inflate 141 * @param howManyToCreate number of new widgets to create 142 * @return the IDs of the new widgets 143 */ createWidgets(Class<? extends AppWidgetProvider> appWidgetProviderClass, int widgetLayoutId, int howManyToCreate)144 public int[] createWidgets(Class<? extends AppWidgetProvider> appWidgetProviderClass, int widgetLayoutId, int howManyToCreate) { 145 AppWidgetProvider appWidgetProvider = newInstanceOf(appWidgetProviderClass); 146 147 int[] newWidgetIds = new int[howManyToCreate]; 148 for (int i = 0; i < howManyToCreate; i++) { 149 View widgetView = createWidgetView(widgetLayoutId); 150 151 int myWidgetId = nextWidgetId++; 152 widgetInfos.put(myWidgetId, new WidgetInfo(widgetView, widgetLayoutId, appWidgetProvider)); 153 newWidgetIds[i] = myWidgetId; 154 } 155 156 appWidgetProvider.onUpdate(context, realAppWidgetManager, newWidgetIds); 157 return newWidgetIds; 158 } 159 createWidgetProvider(Class<? extends AppWidgetProvider> appWidgetProviderClass, int... newWidgetIds)160 private void createWidgetProvider(Class<? extends AppWidgetProvider> appWidgetProviderClass, int... newWidgetIds) { 161 AppWidgetProvider appWidgetProvider = newInstanceOf(appWidgetProviderClass); 162 appWidgetProvider.onUpdate(context, realAppWidgetManager, newWidgetIds); 163 } 164 createWidgetView(int widgetLayoutId)165 private View createWidgetView(int widgetLayoutId) { 166 return new Activity().getLayoutInflater().inflate(widgetLayoutId, null); 167 } 168 169 /** 170 * Non-Android accessor. 171 * 172 * @param widgetId id of the desired widget 173 * @return the widget associated with {@code widgetId} 174 */ getViewFor(int widgetId)175 public View getViewFor(int widgetId) { 176 return getWidgetInfo(widgetId).view; 177 } 178 179 /** 180 * Non-Android accessor. 181 * 182 * @param widgetId id of the widget whose provider is to be returned 183 * @return the {@code AppWidgetProvider} associated with {@code widgetId} 184 */ getAppWidgetProviderFor(int widgetId)185 public AppWidgetProvider getAppWidgetProviderFor(int widgetId) { 186 return getWidgetInfo(widgetId).appWidgetProvider; 187 } 188 189 /** 190 * Non-Android mechanism that enables testing of widget behavior when all of the views are recreated on every 191 * update. This is useful for ensuring that your widget will behave correctly even if it is restarted by the OS 192 * between events. 193 * 194 * @param alwaysRecreate whether or not to always recreate the views 195 */ setAlwaysRecreateViewsDuringUpdate(boolean alwaysRecreate)196 public void setAlwaysRecreateViewsDuringUpdate(boolean alwaysRecreate) { 197 alwaysRecreateViewsDuringUpdate = alwaysRecreate; 198 } 199 200 /** 201 * Non-Android accessor. 202 * 203 * @return the state of the{@code alwaysRecreateViewsDuringUpdate} flag 204 */ getAlwaysRecreateViewsDuringUpdate()205 public boolean getAlwaysRecreateViewsDuringUpdate() { 206 return alwaysRecreateViewsDuringUpdate; 207 } 208 getWidgetInfo(int widgetId)209 private WidgetInfo getWidgetInfo(int widgetId) { 210 return widgetInfos.get(widgetId); 211 } 212 213 private class WidgetInfo { 214 private View view; 215 private int layoutId; 216 private AppWidgetProvider appWidgetProvider; 217 private RemoteViews lastRemoteViews; 218 WidgetInfo(View view, int layoutId, AppWidgetProvider appWidgetProvider)219 public WidgetInfo(View view, int layoutId, AppWidgetProvider appWidgetProvider) { 220 this.view = view; 221 this.layoutId = layoutId; 222 this.appWidgetProvider = appWidgetProvider; 223 } 224 } 225 } 226