Home | History | Annotate | Download | only in shadows
      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 
     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
     66     public static AppWidgetManager getInstance(Context context) {
     67         return instances.getInstance(context);
     68     }
     69 
     70     @Implementation
     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
     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
     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      */
    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      */
    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      */
    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 
    160     private void createWidgetProvider(Class<? extends AppWidgetProvider> appWidgetProviderClass, int... newWidgetIds) {
    161         AppWidgetProvider appWidgetProvider = newInstanceOf(appWidgetProviderClass);
    162         appWidgetProvider.onUpdate(context, realAppWidgetManager, newWidgetIds);
    163     }
    164 
    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      */
    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      */
    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      */
    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      */
    205     public boolean getAlwaysRecreateViewsDuringUpdate() {
    206         return alwaysRecreateViewsDuringUpdate;
    207     }
    208 
    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 
    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