package com.xtremelabs.robolectric.shadows; import android.app.Activity; import android.app.Application; import android.appwidget.AppWidgetManager; import android.appwidget.AppWidgetProvider; import android.content.ComponentName; import android.content.Context; import android.view.View; import android.widget.RemoteViews; import com.xtremelabs.robolectric.internal.AppSingletonizer; import com.xtremelabs.robolectric.internal.Implementation; import com.xtremelabs.robolectric.internal.Implements; import com.xtremelabs.robolectric.internal.RealObject; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import static com.xtremelabs.robolectric.Robolectric.newInstanceOf; import static com.xtremelabs.robolectric.Robolectric.shadowOf; @SuppressWarnings({"UnusedDeclaration"}) @Implements(AppWidgetManager.class) public class ShadowAppWidgetManager { private static AppSingletonizer instances = new AppSingletonizer(AppWidgetManager.class) { @Override protected AppWidgetManager get(ShadowApplication shadowApplication) { return shadowApplication.appWidgetManager; } @Override protected void set(ShadowApplication shadowApplication, AppWidgetManager instance) { shadowApplication.appWidgetManager = instance; } @Override protected AppWidgetManager createInstance(Application applicationContext) { AppWidgetManager appWidgetManager = super.createInstance(applicationContext); shadowOf(appWidgetManager).context = applicationContext; return appWidgetManager; } }; @RealObject private AppWidgetManager realAppWidgetManager; private Context context; private Map widgetInfos = new HashMap(); private int nextWidgetId = 1; private boolean alwaysRecreateViewsDuringUpdate = false; private static void bind(AppWidgetManager appWidgetManager, Context context) { // todo: implement } /** * Finds or creates an {@code AppWidgetManager} for the given {@code context} * * @param context the {@code context} for which to produce an assoicated {@code AppWidgetManager} * @return the {@code AppWidgetManager} associated with the given {@code context} */ @Implementation public static AppWidgetManager getInstance(Context context) { return instances.getInstance(context); } @Implementation public void updateAppWidget(int[] appWidgetIds, RemoteViews views) { for (int appWidgetId : appWidgetIds) { updateAppWidget(appWidgetId, views); } } /** * Simulates updating an {@code AppWidget} with a new set of views * * @param appWidgetId id of widget * @param views views to update */ @Implementation public void updateAppWidget(int appWidgetId, RemoteViews views) { WidgetInfo widgetInfo = getWidgetInfo(appWidgetId); int layoutId = views.getLayoutId(); if (widgetInfo.layoutId != layoutId || alwaysRecreateViewsDuringUpdate) { widgetInfo.view = createWidgetView(layoutId); widgetInfo.layoutId = layoutId; } widgetInfo.lastRemoteViews = views; views.reapply(context, widgetInfo.view); } @Implementation public int[] getAppWidgetIds(ComponentName provider) { List idList = new ArrayList(); for (int id : widgetInfos.keySet()) { WidgetInfo widgetInfo = widgetInfos.get(id); String widgetClass = widgetInfo.appWidgetProvider.getClass().getName(); String widgetPackage = widgetInfo.appWidgetProvider.getClass().getPackage().getName(); if (provider.getClassName().equals(widgetClass) && provider.getPackageName().equals(widgetPackage)) { idList.add(id); } } int ids[] = new int[idList.size()]; for (int i = 0; i < idList.size(); i++) { ids[i] = idList.get(i); } return ids; } /** * Triggers a reapplication of the most recent set of actions against the widget, which is what happens when the * phone is rotated. Does not attempt to simulate a change in screen geometry. * * @param appWidgetId the ID of the widget to be affected */ public void reconstructWidgetViewAsIfPhoneWasRotated(int appWidgetId) { WidgetInfo widgetInfo = getWidgetInfo(appWidgetId); widgetInfo.view = createWidgetView(widgetInfo.layoutId); widgetInfo.lastRemoteViews.reapply(context, widgetInfo.view); } /** * Creates a widget by inflating its layout. * * @param appWidgetProviderClass the app widget provider class * @param widgetLayoutId id of the layout to inflate * @return the ID of the new widget */ public int createWidget(Class appWidgetProviderClass, int widgetLayoutId) { return createWidgets(appWidgetProviderClass, widgetLayoutId, 1)[0]; } /** * Creates a bunch of widgets by inflating the same layout multiple times. * * @param appWidgetProviderClass the app widget provider class * @param widgetLayoutId id of the layout to inflate * @param howManyToCreate number of new widgets to create * @return the IDs of the new widgets */ public int[] createWidgets(Class appWidgetProviderClass, int widgetLayoutId, int howManyToCreate) { AppWidgetProvider appWidgetProvider = newInstanceOf(appWidgetProviderClass); int[] newWidgetIds = new int[howManyToCreate]; for (int i = 0; i < howManyToCreate; i++) { View widgetView = createWidgetView(widgetLayoutId); int myWidgetId = nextWidgetId++; widgetInfos.put(myWidgetId, new WidgetInfo(widgetView, widgetLayoutId, appWidgetProvider)); newWidgetIds[i] = myWidgetId; } appWidgetProvider.onUpdate(context, realAppWidgetManager, newWidgetIds); return newWidgetIds; } private void createWidgetProvider(Class appWidgetProviderClass, int... newWidgetIds) { AppWidgetProvider appWidgetProvider = newInstanceOf(appWidgetProviderClass); appWidgetProvider.onUpdate(context, realAppWidgetManager, newWidgetIds); } private View createWidgetView(int widgetLayoutId) { return new Activity().getLayoutInflater().inflate(widgetLayoutId, null); } /** * Non-Android accessor. * * @param widgetId id of the desired widget * @return the widget associated with {@code widgetId} */ public View getViewFor(int widgetId) { return getWidgetInfo(widgetId).view; } /** * Non-Android accessor. * * @param widgetId id of the widget whose provider is to be returned * @return the {@code AppWidgetProvider} associated with {@code widgetId} */ public AppWidgetProvider getAppWidgetProviderFor(int widgetId) { return getWidgetInfo(widgetId).appWidgetProvider; } /** * Non-Android mechanism that enables testing of widget behavior when all of the views are recreated on every * update. This is useful for ensuring that your widget will behave correctly even if it is restarted by the OS * between events. * * @param alwaysRecreate whether or not to always recreate the views */ public void setAlwaysRecreateViewsDuringUpdate(boolean alwaysRecreate) { alwaysRecreateViewsDuringUpdate = alwaysRecreate; } /** * Non-Android accessor. * * @return the state of the{@code alwaysRecreateViewsDuringUpdate} flag */ public boolean getAlwaysRecreateViewsDuringUpdate() { return alwaysRecreateViewsDuringUpdate; } private WidgetInfo getWidgetInfo(int widgetId) { return widgetInfos.get(widgetId); } private class WidgetInfo { private View view; private int layoutId; private AppWidgetProvider appWidgetProvider; private RemoteViews lastRemoteViews; public WidgetInfo(View view, int layoutId, AppWidgetProvider appWidgetProvider) { this.view = view; this.layoutId = layoutId; this.appWidgetProvider = appWidgetProvider; } } }