1 /* 2 * Copyright (C) 2010 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; 18 19 import android.app.AlertDialog; 20 import android.app.Dialog; 21 import android.content.ContentResolver; 22 import android.content.ContentValues; 23 import android.content.Context; 24 import android.content.DialogInterface; 25 import android.content.DialogInterface.OnCancelListener; 26 import android.database.Cursor; 27 import android.os.Bundle; 28 import android.view.LayoutInflater; 29 import android.view.View; 30 import android.view.View.OnClickListener; 31 import android.widget.Button; 32 33 /** 34 * {@link Activity}s to handle clicks to the pass and fail buttons of the pass fail buttons layout. 35 * 36 * <ol> 37 * <li>Include the pass fail buttons layout in your layout: 38 * <pre><include layout="@layout/pass_fail_buttons" /></pre> 39 * </li> 40 * <li>Extend one of the activities and call setPassFailButtonClickListeners after 41 * setting your content view.</li> 42 * <li>Make sure to call setResult(RESULT_CANCEL) in your Activity initially.</li> 43 * <li>Optionally call setInfoTextResources to add an info button that will show a 44 * dialog with instructional text.</li> 45 * </ol> 46 */ 47 public class PassFailButtons { 48 49 private static final int INFO_DIALOG_ID = 1337; 50 51 private static final String INFO_DIALOG_VIEW_ID = "infoDialogViewId"; 52 private static final String INFO_DIALOG_TITLE_ID = "infoDialogTitleId"; 53 private static final String INFO_DIALOG_MESSAGE_ID = "infoDialogMessageId"; 54 55 // Interface mostly for making documentation and refactoring easier... 56 private interface PassFailActivity { 57 58 /** 59 * Hooks up the pass and fail buttons to click listeners that will record the test results. 60 * <p> 61 * Call from {@link Activity#onCreate} after {@link Activity #setContentView(int)}. 62 */ 63 void setPassFailButtonClickListeners(); 64 65 /** 66 * Adds an initial informational dialog that appears when entering the test activity for 67 * the first time. Also enables the visibility of an "Info" button between the "Pass" and 68 * "Fail" buttons that can be clicked to show the information dialog again. 69 * <p> 70 * Call from {@link Activity#onCreate} after {@link Activity #setContentView(int)}. 71 * 72 * @param titleId for the text shown in the dialog title area 73 * @param messageId for the text shown in the dialog's body area 74 */ 75 void setInfoResources(int titleId, int messageId, int viewId); 76 77 Button getPassButton(); 78 79 /** 80 * Returns a unique identifier for the test. Usually, this is just the class name. 81 */ 82 String getTestId(); 83 84 /** @return null or details about the test run. */ 85 String getTestDetails(); 86 } 87 88 public static class Activity extends android.app.Activity implements PassFailActivity { 89 90 @Override 91 public void setPassFailButtonClickListeners() { 92 setPassFailClickListeners(this); 93 } 94 95 @Override 96 public void setInfoResources(int titleId, int messageId, int viewId) { 97 setInfo(this, titleId, messageId, viewId); 98 } 99 100 @Override 101 public Button getPassButton() { 102 return getPassButtonView(this); 103 } 104 105 @Override 106 public Dialog onCreateDialog(int id, Bundle args) { 107 return createDialog(this, id, args); 108 } 109 110 @Override 111 public String getTestId() { 112 return getClass().getName(); 113 } 114 115 @Override 116 public String getTestDetails() { 117 return null; 118 } 119 } 120 121 public static class ListActivity extends android.app.ListActivity implements PassFailActivity { 122 123 @Override 124 public void setPassFailButtonClickListeners() { 125 setPassFailClickListeners(this); 126 } 127 128 @Override 129 public void setInfoResources(int titleId, int messageId, int viewId) { 130 setInfo(this, titleId, messageId, viewId); 131 } 132 133 @Override 134 public Button getPassButton() { 135 return getPassButtonView(this); 136 } 137 138 @Override 139 public Dialog onCreateDialog(int id, Bundle args) { 140 return createDialog(this, id, args); 141 } 142 143 @Override 144 public String getTestId() { 145 return getClass().getName(); 146 } 147 148 @Override 149 public String getTestDetails() { 150 return null; 151 } 152 } 153 154 public static class TestListActivity extends AbstractTestListActivity 155 implements PassFailActivity { 156 157 @Override 158 public void setPassFailButtonClickListeners() { 159 setPassFailClickListeners(this); 160 } 161 162 @Override 163 public void setInfoResources(int titleId, int messageId, int viewId) { 164 setInfo(this, titleId, messageId, viewId); 165 } 166 167 @Override 168 public Button getPassButton() { 169 return getPassButtonView(this); 170 } 171 172 @Override 173 public Dialog onCreateDialog(int id, Bundle args) { 174 return createDialog(this, id, args); 175 } 176 177 @Override 178 public String getTestId() { 179 return getClass().getName(); 180 } 181 182 @Override 183 public String getTestDetails() { 184 return null; 185 } 186 } 187 188 private static <T extends android.app.Activity & PassFailActivity> 189 void setPassFailClickListeners(final T activity) { 190 View.OnClickListener clickListener = new View.OnClickListener() { 191 @Override 192 public void onClick(View target) { 193 setTestResultAndFinish(activity, activity.getTestId(), activity.getTestDetails(), 194 target); 195 } 196 }; 197 198 activity.findViewById(R.id.pass_button).setOnClickListener(clickListener); 199 activity.findViewById(R.id.fail_button).setOnClickListener(clickListener); 200 } 201 202 private static void setInfo(final android.app.Activity activity, final int titleId, 203 final int messageId, final int viewId) { 204 // Show the middle "info" button and make it show the info dialog when clicked. 205 View infoButton = activity.findViewById(R.id.info_button); 206 infoButton.setVisibility(View.VISIBLE); 207 infoButton.setOnClickListener(new OnClickListener() { 208 @Override 209 public void onClick(View view) { 210 showInfoDialog(activity, titleId, messageId, viewId); 211 } 212 }); 213 214 // Show the info dialog if the user has never seen it before. 215 if (!hasSeenInfoDialog(activity)) { 216 showInfoDialog(activity, titleId, messageId, viewId); 217 } 218 } 219 220 private static boolean hasSeenInfoDialog(android.app.Activity activity) { 221 ContentResolver resolver = activity.getContentResolver(); 222 Cursor cursor = null; 223 try { 224 cursor = resolver.query( 225 TestResultsProvider.getTestNameUri(activity.getClass().getName()), 226 new String[] {TestResultsProvider.COLUMN_TEST_INFO_SEEN}, null, null, null); 227 return cursor.moveToFirst() && cursor.getInt(0) > 0; 228 } finally { 229 if (cursor != null) { 230 cursor.close(); 231 } 232 } 233 } 234 235 private static void showInfoDialog(final android.app.Activity activity, int titleId, 236 int messageId, int viewId) { 237 Bundle args = new Bundle(); 238 args.putInt(INFO_DIALOG_TITLE_ID, titleId); 239 args.putInt(INFO_DIALOG_MESSAGE_ID, messageId); 240 args.putInt(INFO_DIALOG_VIEW_ID, viewId); 241 activity.showDialog(INFO_DIALOG_ID, args); 242 } 243 244 private static Dialog createDialog(final android.app.Activity activity, int id, Bundle args) { 245 switch (id) { 246 case INFO_DIALOG_ID: 247 return createInfoDialog(activity, id, args); 248 default: 249 throw new IllegalArgumentException("Bad dialog id: " + id); 250 } 251 } 252 253 private static Dialog createInfoDialog(final android.app.Activity activity, int id, 254 Bundle args) { 255 int viewId = args.getInt(INFO_DIALOG_VIEW_ID); 256 int titleId = args.getInt(INFO_DIALOG_TITLE_ID); 257 int messageId = args.getInt(INFO_DIALOG_MESSAGE_ID); 258 259 AlertDialog.Builder builder = new AlertDialog.Builder(activity).setIcon( 260 android.R.drawable.ic_dialog_info).setTitle(titleId); 261 if (viewId > 0) { 262 LayoutInflater inflater = (LayoutInflater) activity 263 .getSystemService(Context.LAYOUT_INFLATER_SERVICE); 264 builder.setView(inflater.inflate(viewId, null)); 265 } else { 266 builder.setMessage(messageId); 267 } 268 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { 269 @Override 270 public void onClick(DialogInterface dialog, int which) { 271 markSeenInfoDialog(activity); 272 } 273 }).setOnCancelListener(new OnCancelListener() { 274 @Override 275 public void onCancel(DialogInterface dialog) { 276 markSeenInfoDialog(activity); 277 } 278 }); 279 return builder.create(); 280 } 281 282 private static void markSeenInfoDialog(android.app.Activity activity) { 283 ContentResolver resolver = activity.getContentResolver(); 284 ContentValues values = new ContentValues(2); 285 values.put(TestResultsProvider.COLUMN_TEST_NAME, activity.getClass().getName()); 286 values.put(TestResultsProvider.COLUMN_TEST_INFO_SEEN, 1); 287 int numUpdated = resolver.update( 288 TestResultsProvider.getTestNameUri(activity.getClass().getName()), 289 values, null, null); 290 if (numUpdated == 0) { 291 resolver.insert(TestResultsProvider.RESULTS_CONTENT_URI, values); 292 } 293 } 294 295 /** Set the test result corresponding to the button clicked and finish the activity. */ 296 private static void setTestResultAndFinish(android.app.Activity activity, String testId, 297 String testDetails, View target) { 298 boolean passed; 299 switch (target.getId()) { 300 case R.id.pass_button: 301 passed = true; 302 break; 303 case R.id.fail_button: 304 passed = false; 305 break; 306 default: 307 throw new IllegalArgumentException("Unknown id: " + target.getId()); 308 } 309 setTestResultAndFinish(activity, testId, testDetails, passed); 310 } 311 312 /** Set the test result and finish the activity. */ 313 public static void setTestResultAndFinish(android.app.Activity activity, String testId, 314 String testDetails, boolean passed) { 315 if (passed) { 316 TestResult.setPassedResult(activity, testId, testDetails); 317 } else { 318 TestResult.setFailedResult(activity, testId, testDetails); 319 } 320 321 activity.finish(); 322 } 323 324 private static Button getPassButtonView(android.app.Activity activity) { 325 return (Button) activity.findViewById(R.id.pass_button); 326 } 327 } 328