| /* |
| * Copyright (C) 2013 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.cts.verifier.widget; |
| |
| import java.util.HashMap; |
| |
| import android.app.PendingIntent; |
| import android.appwidget.AppWidgetManager; |
| import android.appwidget.AppWidgetProvider; |
| import android.appwidget.AppWidgetProviderInfo; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.net.Uri; |
| import android.os.Bundle; |
| import android.util.Pair; |
| import android.view.View; |
| import android.widget.RemoteViews; |
| |
| import com.android.cts.verifier.R; |
| |
| /** |
| * The weather widget's AppWidgetProvider. |
| */ |
| public class WidgetCtsProvider extends AppWidgetProvider { |
| class TextData { |
| String title; |
| String instruction; |
| String dataP; |
| String dataL; |
| |
| public TextData(String t, String i, String dp, String dl) { |
| title = t; |
| instruction = i; |
| dataL = dl; |
| dataP = dp; |
| } |
| } |
| |
| public static String PASS = "com.example.android.widgetcts.PASS"; |
| public static String FAIL = "com.example.android.widgetcts.FAIL"; |
| |
| public static final int STATE_BEGIN = 0; |
| public static final int STATE_VERIFY_SIZE_CALLBACK = 1; |
| public static final int STATE_VERIFY_RESIZE = 2; |
| public static final int STATE_VERIFY_COLLECTIONS = 3; |
| public static final int STATE_VERIFY_HOME_OR_KEYGUARD_CALLBACK = 4; |
| public static final int STATE_COMPLETE = 5; |
| |
| // If relevant, we want to verify the size callback first, before any |
| // resizing. |
| static HashMap<Integer, Integer> sStateMap = new HashMap<Integer, Integer>(); |
| static HashMap<Integer, Integer> sTestCount = new HashMap<Integer, Integer>(); |
| static HashMap<Integer, Integer> sPassCount = new HashMap<Integer, Integer>(); |
| |
| private static int sSDKLevel = android.os.Build.VERSION.SDK_INT; |
| |
| public WidgetCtsProvider() { |
| } |
| |
| @Override |
| public void onReceive(Context ctx, Intent intent) { |
| final String action = intent.getAction(); |
| if (action.equals(PASS) || action.equals(FAIL)) { |
| boolean pass = action.equals(PASS); |
| |
| int widgetId = (Integer) intent.getExtras().getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, |
| -1); |
| |
| if (sStateMap.get(widgetId) != STATE_BEGIN && sStateMap.get(widgetId) |
| != STATE_COMPLETE) { |
| if (!sTestCount.containsKey(widgetId)) { |
| sTestCount.put(widgetId, 0); |
| } |
| if (!sPassCount.containsKey(widgetId)) { |
| sPassCount.put(widgetId, 0); |
| } |
| |
| sPassCount.put(widgetId, sPassCount.get(widgetId) + (pass ? 1 : 0)); |
| sTestCount.put(widgetId, sTestCount.get(widgetId) + 1); |
| } |
| gotoNextTest(widgetId); |
| |
| AppWidgetManager mgr = AppWidgetManager.getInstance(ctx); |
| Bundle options = getAppWidgetOptions(mgr, widgetId); |
| updateWidget(ctx, widgetId, mgr, options); |
| } |
| super.onReceive(ctx, intent); |
| } |
| |
| @Override |
| public void onDeleted(Context ctx, int[] ids) { |
| for (int i = 0; i < ids.length; i++) { |
| sStateMap.remove(ids[i]); |
| } |
| } |
| |
| Bundle getAppWidgetOptions(AppWidgetManager mgr, int widgetId) { |
| if (sSDKLevel >= android.os.Build.VERSION_CODES.JELLY_BEAN) { |
| return mgr.getAppWidgetOptions(widgetId); |
| } |
| return null; |
| } |
| |
| void gotoNextTest(int widgetId) { |
| int state = sStateMap.get(widgetId); |
| boolean foundNextTest = false; |
| do { |
| state = state == STATE_COMPLETE ? state : state + 1; |
| foundNextTest = shouldPerformTest(state); |
| } while (state < STATE_COMPLETE && !foundNextTest); |
| sStateMap.put(widgetId, state); |
| } |
| |
| private boolean shouldPerformTest(int state) { |
| if (state == STATE_VERIFY_SIZE_CALLBACK |
| && sSDKLevel < android.os.Build.VERSION_CODES.JELLY_BEAN) { |
| return false; |
| } else if (state == STATE_VERIFY_RESIZE |
| && sSDKLevel < android.os.Build.VERSION_CODES.HONEYCOMB) { |
| return false; |
| } else if (state == STATE_VERIFY_COLLECTIONS |
| && sSDKLevel < android.os.Build.VERSION_CODES.HONEYCOMB) { |
| return false; |
| } else if (state == STATE_VERIFY_HOME_OR_KEYGUARD_CALLBACK |
| && sSDKLevel < android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) { |
| return false; |
| } |
| // TODO: revert when b/228227212 is fixed (underlying cause of b/204831731) |
| return false; |
| } |
| |
| @Override |
| public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { |
| for (int i = 0; i < appWidgetIds.length; i++) { |
| int id = appWidgetIds[i]; |
| if (!sStateMap.containsKey(id)) { |
| sStateMap.put(id, STATE_BEGIN); |
| } |
| updateWidget(context, appWidgetIds[i], appWidgetManager, null); |
| } |
| } |
| |
| @Override |
| public void onAppWidgetOptionsChanged(Context context, AppWidgetManager appWidgetManager, |
| int appWidgetId, Bundle newOptions) { |
| updateWidget(context, appWidgetId, appWidgetManager, newOptions); |
| } |
| |
| private TextData getInstruction(int state, Bundle options, int widgetId) { |
| String title = null; |
| String instruction = null; |
| String dataL = null; |
| String dataP = null; |
| |
| int widthP = -1; |
| int heightP = -1; |
| int widthL = -1; |
| int heightL = -1; |
| int category = -1; |
| |
| // We use nullness of options as a proxy for an sdk check >= JB |
| if (options != null) { |
| widthP = options.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, -1); |
| heightP = options.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT, -1); |
| widthL = options.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH, -1); |
| heightL = options.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT, -1); |
| category = options.getInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY, -1); |
| } |
| |
| int step = 1; |
| if (sTestCount.containsKey(widgetId)) { |
| step = sTestCount.get(widgetId) + 1; |
| } |
| if (state == STATE_BEGIN) { |
| instruction = "This is a test of the widget framework"; |
| } else if (state == STATE_VERIFY_SIZE_CALLBACK) { |
| title = "Step " + step + ": Verify dimensions"; |
| instruction = "Verify that the width and height indicated below constitute reasonable" |
| + " approximations of the widget's actual size:"; |
| dataP = "Width: " + widthP + " Height: " + heightP; |
| dataL = "Width: " + widthL + " Height: " + heightL; |
| } else if (state == STATE_VERIFY_RESIZE) { |
| title = "Step " + step + ": Verify resizeability"; |
| instruction = "Verify that there is a functional affordance which allows this widget" |
| + " to be resized. For example, when picked up and dropped, there may be a " |
| + " frame with handles. (This is not a requirement for widgets hosted on " |
| + " a tablet keyguard)."; |
| if (sSDKLevel >= android.os.Build.VERSION_CODES.JELLY_BEAN) { |
| instruction = instruction |
| + " Also, verify that after resize, the width and height below " |
| + "are updated accordingly."; |
| dataP = "Width: " + widthP + " Height: " + heightP; |
| dataL = "Width: " + widthL + " Height: " + heightL; |
| } |
| } else if (state == STATE_VERIFY_COLLECTIONS) { |
| title = "Step " + step + ": Verify collections"; |
| instruction = "Verify that the widget contains a scrollable list of numbers from 1" |
| + " to " + WidgetCtsService.NUM_ITEMS; |
| } else if (state == STATE_VERIFY_HOME_OR_KEYGUARD_CALLBACK) { |
| title = "Step " + step + ": Verify category"; |
| instruction = "Verify that the text below accurately reflects whether this widget is" |
| + " on the home screen or the lock screen. "; |
| if (category == AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD) { |
| dataL = dataP = "Widget is reportedly on: KEYGUARD"; |
| } else if (category == AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN) { |
| dataL = dataP = "Widget is reportedly on: HOME SCREEN"; |
| } else { |
| dataL = dataP = "Error."; |
| } |
| } else if (state == STATE_COMPLETE) { |
| title = "Test Complete"; |
| instruction = "This completes the test of the widget framework. " + |
| "Remove and re-add this widget to restart the test."; |
| dataL = dataP = sPassCount.get(widgetId) + " of " + sTestCount.get(widgetId) |
| + " tests passed successfully."; |
| } |
| return new TextData(title, instruction, dataP, dataL); |
| } |
| |
| private void updateWidget(Context context, int appWidgetId, AppWidgetManager appWidgetManager, |
| Bundle newOptions) { |
| // Pull them from the manager |
| if (newOptions == null) { |
| newOptions = getAppWidgetOptions(appWidgetManager, appWidgetId); |
| } |
| |
| int baseLayout = R.layout.widget_layout; |
| |
| RemoteViews rv = new RemoteViews(context.getPackageName(), baseLayout); |
| int state = sStateMap.get(appWidgetId); |
| |
| TextData text = getInstruction(state, newOptions, appWidgetId); |
| rv.setTextViewText(R.id.instruction, text.instruction); |
| |
| // Update the title |
| if (text.title != null) { |
| rv.setTextViewText(R.id.title, text.title); |
| } |
| |
| if (state == STATE_VERIFY_COLLECTIONS) { |
| // Specify the service to provide data for the collection widget. |
| // Note that we need to |
| // embed the appWidgetId via the data otherwise it will be ignored. |
| final Intent intent = new Intent(context, WidgetCtsService.class); |
| intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); |
| intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME))); |
| rv.setViewVisibility(R.id.list, View.VISIBLE); |
| rv.setRemoteAdapter(appWidgetId, R.id.list, intent); |
| } else { |
| rv.setViewVisibility(R.id.list, View.GONE); |
| } |
| |
| if (state == STATE_BEGIN) { |
| rv.setViewVisibility(R.id.fail, View.GONE); |
| rv.setTextViewText(R.id.pass, "Start Test"); |
| } else if (state == STATE_COMPLETE) { |
| rv.setViewVisibility(R.id.fail, View.GONE); |
| rv.setViewVisibility(R.id.pass, View.GONE); |
| } else { |
| rv.setViewVisibility(R.id.fail, View.VISIBLE); |
| rv.setTextViewText(R.id.pass, "Pass"); |
| } |
| |
| final Intent pass = new Intent(context, WidgetCtsProvider.class); |
| pass.setAction(WidgetCtsProvider.PASS); |
| pass.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); |
| pass.setData(Uri.parse(pass.toUri(Intent.URI_INTENT_SCHEME))); |
| final PendingIntent passPendingIntent = PendingIntent.getBroadcast(context, 0, pass, |
| PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE_UNAUDITED); |
| |
| final Intent fail = new Intent(context, WidgetCtsProvider.class); |
| fail.setAction(WidgetCtsProvider.FAIL); |
| fail.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); |
| fail.setData(Uri.parse(fail.toUri(Intent.URI_INTENT_SCHEME))); |
| final PendingIntent failPendingIntent = PendingIntent.getBroadcast(context, 0, fail, |
| PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE_UNAUDITED); |
| |
| rv.setOnClickPendingIntent(R.id.pass, passPendingIntent); |
| rv.setOnClickPendingIntent(R.id.fail, failPendingIntent); |
| |
| RemoteViews rvL = null; |
| if (text.dataP != null && !text.dataP.equals(text.dataL)) { |
| rvL = rv.clone(); |
| |
| System.out.println("hmmmm ok, made it innnnn"); |
| if (text.dataL != null) { |
| rvL.setViewVisibility(R.id.data, View.VISIBLE); |
| rvL.setTextViewText(R.id.data, text.dataL); |
| } else { |
| rvL.setViewVisibility(R.id.data, View.GONE); |
| } |
| } |
| |
| // Update the data |
| if (text.dataP != null) { |
| rv.setViewVisibility(R.id.data, View.VISIBLE); |
| rv.setTextViewText(R.id.data, text.dataP); |
| } else { |
| rv.setViewVisibility(R.id.data, View.GONE); |
| } |
| |
| RemoteViews rvFinal = rv; |
| if (rvL != null) { |
| rvFinal = new RemoteViews(rvL, rv); |
| } |
| |
| appWidgetManager.updateAppWidget(appWidgetId, rvFinal); |
| } |
| } |