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