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 TextData(String t, String i, String dp, String dl)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 WidgetCtsProvider()71 public WidgetCtsProvider() { 72 } 73 74 @Override onReceive(Context ctx, Intent intent)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 onDeleted(Context ctx, int[] ids)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 getAppWidgetOptions(AppWidgetManager mgr, int widgetId)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 gotoNextTest(int widgetId)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 shouldPerformTest(int state)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 onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds)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 onAppWidgetOptionsChanged(Context context, AppWidgetManager appWidgetManager, int appWidgetId, Bundle newOptions)157 public void onAppWidgetOptionsChanged(Context context, AppWidgetManager appWidgetManager, 158 int appWidgetId, Bundle newOptions) { 159 updateWidget(context, appWidgetId, appWidgetManager, newOptions); 160 } 161 getInstruction(int state, Bundle options, int widgetId)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 updateWidget(Context context, int appWidgetId, AppWidgetManager appWidgetManager, Bundle newOptions)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