Home | History | Annotate | Download | only in widget
      1 /*
      2  * Copyright (C) 2013 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 com.android.cts.verifier.widget;
     18 
     19 import java.util.HashMap;
     20 
     21 import android.app.PendingIntent;
     22 import android.appwidget.AppWidgetManager;
     23 import android.appwidget.AppWidgetProvider;
     24 import android.appwidget.AppWidgetProviderInfo;
     25 import android.content.Context;
     26 import android.content.Intent;
     27 import android.net.Uri;
     28 import android.os.Bundle;
     29 import android.util.Pair;
     30 import android.view.View;
     31 import android.widget.RemoteViews;
     32 
     33 import com.android.cts.verifier.R;
     34 
     35 /**
     36  * The weather widget's AppWidgetProvider.
     37  */
     38 public class WidgetCtsProvider extends AppWidgetProvider {
     39     class TextData {
     40         String title;
     41         String instruction;
     42         String dataP;
     43         String dataL;
     44 
     45         public TextData(String t, String i, String dp, String dl) {
     46             title = t;
     47             instruction = i;
     48             dataL = dl;
     49             dataP = dp;
     50         }
     51     }
     52 
     53     public static String PASS = "com.example.android.widgetcts.PASS";
     54     public static String FAIL = "com.example.android.widgetcts.FAIL";
     55 
     56     public static final int STATE_BEGIN = 0;
     57     public static final int STATE_VERIFY_SIZE_CALLBACK = 1;
     58     public static final int STATE_VERIFY_RESIZE = 2;
     59     public static final int STATE_VERIFY_COLLECTIONS = 3;
     60     public static final int STATE_VERIFY_HOME_OR_KEYGUARD_CALLBACK = 4;
     61     public static final int STATE_COMPLETE = 5;
     62 
     63     // If relevant, we want to verify the size callback first, before any
     64     // resizing.
     65     static HashMap<Integer, Integer> sStateMap = new HashMap<Integer, Integer>();
     66     static HashMap<Integer, Integer> sTestCount = new HashMap<Integer, Integer>();
     67     static HashMap<Integer, Integer> sPassCount = new HashMap<Integer, Integer>();
     68 
     69     private static int sSDKLevel = android.os.Build.VERSION.SDK_INT;
     70 
     71     public WidgetCtsProvider() {
     72     }
     73 
     74     @Override
     75     public void onReceive(Context ctx, Intent intent) {
     76         final String action = intent.getAction();
     77         if (action.equals(PASS) || action.equals(FAIL)) {
     78             boolean pass = action.equals(PASS);
     79 
     80             int widgetId = (Integer) intent.getExtras().getInt(AppWidgetManager.EXTRA_APPWIDGET_ID,
     81                     -1);
     82 
     83             if (sStateMap.get(widgetId) != STATE_BEGIN && sStateMap.get(widgetId)
     84                     != STATE_COMPLETE) {
     85                 if (!sTestCount.containsKey(widgetId)) {
     86                     sTestCount.put(widgetId, 0);
     87                 }
     88                 if (!sPassCount.containsKey(widgetId)) {
     89                     sPassCount.put(widgetId, 0);
     90                 }
     91 
     92                 sPassCount.put(widgetId, sPassCount.get(widgetId) + (pass ? 1 : 0));
     93                 sTestCount.put(widgetId, sTestCount.get(widgetId) + 1);
     94             }
     95             gotoNextTest(widgetId);
     96 
     97             AppWidgetManager mgr = AppWidgetManager.getInstance(ctx);
     98             Bundle options = getAppWidgetOptions(mgr, widgetId);
     99             updateWidget(ctx, widgetId, mgr, options);
    100         }
    101         super.onReceive(ctx, intent);
    102     }
    103 
    104     @Override
    105     public void onDeleted(Context ctx, int[] ids) {
    106         for (int i = 0; i < ids.length; i++) {
    107             sStateMap.remove(ids[i]);
    108         }
    109     }
    110 
    111     Bundle getAppWidgetOptions(AppWidgetManager mgr, int widgetId) {
    112         if (sSDKLevel >= android.os.Build.VERSION_CODES.JELLY_BEAN) {
    113             return mgr.getAppWidgetOptions(widgetId);
    114         }
    115         return null;
    116     }
    117 
    118     void gotoNextTest(int widgetId) {
    119         int state = sStateMap.get(widgetId);
    120         boolean foundNextTest = false;
    121         do {
    122             state = state == STATE_COMPLETE ? state : state + 1;
    123             foundNextTest = shouldPerformTest(state);
    124         } while (state < STATE_COMPLETE && !foundNextTest);
    125         sStateMap.put(widgetId, state);
    126     }
    127 
    128     private boolean shouldPerformTest(int state) {
    129         if (state == STATE_VERIFY_SIZE_CALLBACK
    130                 && sSDKLevel < android.os.Build.VERSION_CODES.JELLY_BEAN) {
    131             return false;
    132         } else if (state == STATE_VERIFY_RESIZE
    133                 && sSDKLevel < android.os.Build.VERSION_CODES.HONEYCOMB) {
    134             return false;
    135         } else if (state == STATE_VERIFY_COLLECTIONS
    136                 && sSDKLevel < android.os.Build.VERSION_CODES.HONEYCOMB) {
    137             return false;
    138         } else if (state == STATE_VERIFY_HOME_OR_KEYGUARD_CALLBACK
    139                 && sSDKLevel < android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) {
    140             return false;
    141         }
    142         return true;
    143     }
    144 
    145     @Override
    146     public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
    147         for (int i = 0; i < appWidgetIds.length; i++) {
    148             int id = appWidgetIds[i];
    149             if (!sStateMap.containsKey(id)) {
    150                 sStateMap.put(id, STATE_BEGIN);
    151             }
    152             updateWidget(context, appWidgetIds[i], appWidgetManager, null);
    153         }
    154     }
    155 
    156     @Override
    157     public void onAppWidgetOptionsChanged(Context context, AppWidgetManager appWidgetManager,
    158             int appWidgetId, Bundle newOptions) {
    159         updateWidget(context, appWidgetId, appWidgetManager, newOptions);
    160     }
    161 
    162     private TextData getInstruction(int state, Bundle options, int widgetId) {
    163         String title = null;
    164         String instruction = null;
    165         String dataL = null;
    166         String dataP = null;
    167 
    168         int widthP = -1;
    169         int heightP = -1;
    170         int widthL = -1;
    171         int heightL = -1;
    172         int category = -1;
    173 
    174         // We use nullness of options as a proxy for an sdk check >= JB
    175         if (options != null) {
    176             widthP = options.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, -1);
    177             heightP = options.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT, -1);
    178             widthL = options.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH, -1);
    179             heightL = options.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT, -1);
    180             category = options.getInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY, -1);
    181         }
    182 
    183         int step = 1;
    184         if (sTestCount.containsKey(widgetId)) {
    185             step = sTestCount.get(widgetId) + 1;
    186         }
    187         if (state == STATE_BEGIN) {
    188             instruction = "This is a test of the widget framework";
    189         } else if (state == STATE_VERIFY_SIZE_CALLBACK) {
    190             title = "Step " + step + ": Verify dimensions";
    191             instruction = "Verify that the width and height indicated below constitute reasonable"
    192                     + " approximations of the widget's actual size:";
    193             dataP = "Width: " + widthP + "     Height: " + heightP;
    194             dataL = "Width: " + widthL + "     Height: " + heightL;
    195         } else if (state == STATE_VERIFY_RESIZE) {
    196             title = "Step " + step + ": Verify resizeability";
    197             instruction = "Verify that there is a functional affordance which allows this widget"
    198                     + " to be resized. For example, when picked up and dropped, there may be a "
    199                     + " frame with handles. (This is not a requirement for widgets hosted on "
    200                     + " a tablet keyguard).";
    201             if (sSDKLevel >= android.os.Build.VERSION_CODES.JELLY_BEAN) {
    202                 instruction = instruction
    203                         + " Also, verify that after resize, the width and height below "
    204                         + "are updated accordingly.";
    205                 dataP = "Width: " + widthP + "     Height: " + heightP;
    206                 dataL = "Width: " + widthL + "     Height: " + heightL;
    207             }
    208         } else if (state == STATE_VERIFY_COLLECTIONS) {
    209             title = "Step " + step + ": Verify collections";
    210             instruction = "Verify that the widget contains a scrollable list of numbers from 1"
    211                     + " to " + WidgetCtsService.NUM_ITEMS;
    212         } else if (state == STATE_VERIFY_HOME_OR_KEYGUARD_CALLBACK) {
    213             title = "Step " + step + ": Verify category";
    214             instruction = "Verify that the text below accurately reflects whether this widget is"
    215                     + " on the home screen or the lock screen. ";
    216             if (category == AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD) {
    217                 dataL = dataP = "Widget is reportedly on: KEYGUARD";
    218             } else if (category == AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN) {
    219                 dataL = dataP = "Widget is reportedly on: HOME SCREEN";
    220             } else {
    221                 dataL = dataP = "Error.";
    222             }
    223         } else if (state == STATE_COMPLETE) {
    224             title = "Test Complete";
    225             instruction = "This completes the test of the widget framework. " +
    226                     "Remove and re-add this widget to restart the test.";
    227             dataL = dataP = sPassCount.get(widgetId) + " of " + sTestCount.get(widgetId)
    228                     + " tests passed successfully.";
    229         }
    230         return new TextData(title, instruction, dataP, dataL);
    231     }
    232 
    233     private void updateWidget(Context context, int appWidgetId, AppWidgetManager appWidgetManager,
    234             Bundle newOptions) {
    235         // Pull them from the manager
    236         if (newOptions == null) {
    237             newOptions = getAppWidgetOptions(appWidgetManager, appWidgetId);
    238         }
    239 
    240         int baseLayout = R.layout.widget_layout;
    241 
    242         RemoteViews rv = new RemoteViews(context.getPackageName(), baseLayout);
    243         int state = sStateMap.get(appWidgetId);
    244 
    245         TextData text = getInstruction(state, newOptions, appWidgetId);
    246         rv.setTextViewText(R.id.instruction, text.instruction);
    247 
    248         // Update the title
    249         if (text.title != null) {
    250             rv.setTextViewText(R.id.title, text.title);
    251         }
    252 
    253         if (state == STATE_VERIFY_COLLECTIONS) {
    254             // Specify the service to provide data for the collection widget.
    255             // Note that we need to
    256             // embed the appWidgetId via the data otherwise it will be ignored.
    257             final Intent intent = new Intent(context, WidgetCtsService.class);
    258             intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
    259             intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
    260             rv.setViewVisibility(R.id.list, View.VISIBLE);
    261             rv.setRemoteAdapter(appWidgetId, R.id.list, intent);
    262         } else {
    263             rv.setViewVisibility(R.id.list, View.GONE);
    264         }
    265 
    266         if (state == STATE_BEGIN) {
    267             rv.setViewVisibility(R.id.fail, View.GONE);
    268             rv.setTextViewText(R.id.pass, "Start Test");
    269         } else if (state == STATE_COMPLETE) {
    270             rv.setViewVisibility(R.id.fail, View.GONE);
    271             rv.setViewVisibility(R.id.pass, View.GONE);
    272         } else {
    273             rv.setViewVisibility(R.id.fail, View.VISIBLE);
    274             rv.setTextViewText(R.id.pass, "Pass");
    275         }
    276 
    277         final Intent pass = new Intent(context, WidgetCtsProvider.class);
    278         pass.setAction(WidgetCtsProvider.PASS);
    279         pass.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
    280         pass.setData(Uri.parse(pass.toUri(Intent.URI_INTENT_SCHEME)));
    281         final PendingIntent passPendingIntent = PendingIntent.getBroadcast(context, 0, pass,
    282                 PendingIntent.FLAG_UPDATE_CURRENT);
    283 
    284         final Intent fail = new Intent(context, WidgetCtsProvider.class);
    285         fail.setAction(WidgetCtsProvider.FAIL);
    286         fail.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
    287         fail.setData(Uri.parse(fail.toUri(Intent.URI_INTENT_SCHEME)));
    288         final PendingIntent failPendingIntent = PendingIntent.getBroadcast(context, 0, fail,
    289                 PendingIntent.FLAG_UPDATE_CURRENT);
    290 
    291         rv.setOnClickPendingIntent(R.id.pass, passPendingIntent);
    292         rv.setOnClickPendingIntent(R.id.fail, failPendingIntent);
    293 
    294         RemoteViews rvL = null;
    295         if (text.dataP != null && !text.dataP.equals(text.dataL)) {
    296             rvL = rv.clone();
    297 
    298             System.out.println("hmmmm ok, made it innnnn");
    299             if (text.dataL != null) {
    300                 rvL.setViewVisibility(R.id.data, View.VISIBLE);
    301                 rvL.setTextViewText(R.id.data, text.dataL);
    302             } else {
    303                 rvL.setViewVisibility(R.id.data, View.GONE);
    304             }
    305         }
    306 
    307         // Update the data
    308         if (text.dataP != null) {
    309             rv.setViewVisibility(R.id.data, View.VISIBLE);
    310             rv.setTextViewText(R.id.data, text.dataP);
    311         } else {
    312             rv.setViewVisibility(R.id.data, View.GONE);
    313         }
    314 
    315         RemoteViews rvFinal = rv;
    316         if (rvL != null) {
    317             rvFinal = new RemoteViews(rvL, rv);
    318         }
    319 
    320         appWidgetManager.updateAppWidget(appWidgetId, rvFinal);
    321     }
    322 }
    323