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 com.android.compatibility.common.util.ReportLog; 20 21 import android.app.AlertDialog; 22 import android.app.Dialog; 23 import android.content.ContentResolver; 24 import android.content.ContentValues; 25 import android.content.Context; 26 import android.content.DialogInterface; 27 import android.content.DialogInterface.OnCancelListener; 28 import android.content.pm.PackageManager; 29 import android.database.Cursor; 30 import android.os.Bundle; 31 import android.os.PowerManager; 32 import android.os.PowerManager.WakeLock; 33 import android.view.LayoutInflater; 34 import android.view.View; 35 import android.view.View.OnClickListener; 36 import android.widget.ImageButton; 37 import android.widget.Toast; 38 39 /** 40 * {@link Activity}s to handle clicks to the pass and fail buttons of the pass fail buttons layout. 41 * 42 * <ol> 43 * <li>Include the pass fail buttons layout in your layout: 44 * <pre><include layout="@layout/pass_fail_buttons" /></pre> 45 * </li> 46 * <li>Extend one of the activities and call setPassFailButtonClickListeners after 47 * setting your content view.</li> 48 * <li>Make sure to call setResult(RESULT_CANCEL) in your Activity initially.</li> 49 * <li>Optionally call setInfoTextResources to add an info button that will show a 50 * dialog with instructional text.</li> 51 * </ol> 52 */ 53 public class PassFailButtons { 54 55 private static final int INFO_DIALOG_ID = 1337; 56 57 private static final String INFO_DIALOG_VIEW_ID = "infoDialogViewId"; 58 private static final String INFO_DIALOG_TITLE_ID = "infoDialogTitleId"; 59 private static final String INFO_DIALOG_MESSAGE_ID = "infoDialogMessageId"; 60 61 // Interface mostly for making documentation and refactoring easier... 62 public interface PassFailActivity { 63 64 /** 65 * Hooks up the pass and fail buttons to click listeners that will record the test results. 66 * <p> 67 * Call from {@link Activity#onCreate} after {@link Activity #setContentView(int)}. 68 */ setPassFailButtonClickListeners()69 void setPassFailButtonClickListeners(); 70 71 /** 72 * Adds an initial informational dialog that appears when entering the test activity for 73 * the first time. Also enables the visibility of an "Info" button between the "Pass" and 74 * "Fail" buttons that can be clicked to show the information dialog again. 75 * <p> 76 * Call from {@link Activity#onCreate} after {@link Activity #setContentView(int)}. 77 * 78 * @param titleId for the text shown in the dialog title area 79 * @param messageId for the text shown in the dialog's body area 80 */ setInfoResources(int titleId, int messageId, int viewId)81 void setInfoResources(int titleId, int messageId, int viewId); 82 getPassButton()83 View getPassButton(); 84 85 /** 86 * Returns a unique identifier for the test. Usually, this is just the class name. 87 */ getTestId()88 String getTestId(); 89 90 /** @return null or details about the test run. */ getTestDetails()91 String getTestDetails(); 92 93 /** 94 * Set the result of the test and finish the activity. 95 * 96 * @param passed Whether or not the test passed. 97 */ setTestResultAndFinish(boolean passed)98 void setTestResultAndFinish(boolean passed); 99 100 /** @return A {@link ReportLog} that is used to record test metric data. */ getReportLog()101 ReportLog getReportLog(); 102 } 103 104 public static class Activity extends android.app.Activity implements PassFailActivity { 105 private WakeLock mWakeLock; 106 private final ReportLog reportLog; 107 Activity()108 public Activity() { 109 this.reportLog = new CtsVerifierReportLog(); 110 } 111 112 @Override onResume()113 protected void onResume() { 114 super.onResume(); 115 if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)) { 116 mWakeLock = ((PowerManager) getSystemService(Context.POWER_SERVICE)) 117 .newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, "PassFailButtons"); 118 mWakeLock.acquire(); 119 } 120 } 121 122 @Override onPause()123 protected void onPause() { 124 super.onPause(); 125 if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)) { 126 mWakeLock.release(); 127 } 128 } 129 130 @Override setPassFailButtonClickListeners()131 public void setPassFailButtonClickListeners() { 132 setPassFailClickListeners(this); 133 } 134 135 @Override setInfoResources(int titleId, int messageId, int viewId)136 public void setInfoResources(int titleId, int messageId, int viewId) { 137 setInfo(this, titleId, messageId, viewId); 138 } 139 140 @Override getPassButton()141 public View getPassButton() { 142 return getPassButtonView(this); 143 } 144 145 @Override onCreateDialog(int id, Bundle args)146 public Dialog onCreateDialog(int id, Bundle args) { 147 return createDialog(this, id, args); 148 } 149 150 @Override getTestId()151 public String getTestId() { 152 return getClass().getName(); 153 } 154 155 @Override getTestDetails()156 public String getTestDetails() { 157 return null; 158 } 159 160 @Override setTestResultAndFinish(boolean passed)161 public void setTestResultAndFinish(boolean passed) { 162 PassFailButtons.setTestResultAndFinishHelper( 163 this, getTestId(), getTestDetails(), passed, getReportLog()); 164 } 165 166 @Override getReportLog()167 public ReportLog getReportLog() { return reportLog; } 168 } 169 170 public static class ListActivity extends android.app.ListActivity implements PassFailActivity { 171 172 private final ReportLog reportLog; 173 ListActivity()174 public ListActivity() { 175 this.reportLog = new CtsVerifierReportLog(); 176 } 177 178 @Override setPassFailButtonClickListeners()179 public void setPassFailButtonClickListeners() { 180 setPassFailClickListeners(this); 181 } 182 183 @Override setInfoResources(int titleId, int messageId, int viewId)184 public void setInfoResources(int titleId, int messageId, int viewId) { 185 setInfo(this, titleId, messageId, viewId); 186 } 187 188 @Override getPassButton()189 public View getPassButton() { 190 return getPassButtonView(this); 191 } 192 193 @Override onCreateDialog(int id, Bundle args)194 public Dialog onCreateDialog(int id, Bundle args) { 195 return createDialog(this, id, args); 196 } 197 198 @Override getTestId()199 public String getTestId() { 200 return getClass().getName(); 201 } 202 203 @Override getTestDetails()204 public String getTestDetails() { 205 return null; 206 } 207 208 @Override setTestResultAndFinish(boolean passed)209 public void setTestResultAndFinish(boolean passed) { 210 PassFailButtons.setTestResultAndFinishHelper( 211 this, getTestId(), getTestDetails(), passed, getReportLog()); 212 } 213 214 @Override getReportLog()215 public ReportLog getReportLog() { return reportLog; } 216 } 217 218 public static class TestListActivity extends AbstractTestListActivity 219 implements PassFailActivity { 220 221 private final ReportLog reportLog; 222 TestListActivity()223 public TestListActivity() { 224 this.reportLog = new CtsVerifierReportLog(); 225 } 226 227 @Override setPassFailButtonClickListeners()228 public void setPassFailButtonClickListeners() { 229 setPassFailClickListeners(this); 230 } 231 232 @Override setInfoResources(int titleId, int messageId, int viewId)233 public void setInfoResources(int titleId, int messageId, int viewId) { 234 setInfo(this, titleId, messageId, viewId); 235 } 236 237 @Override getPassButton()238 public View getPassButton() { 239 return getPassButtonView(this); 240 } 241 242 @Override onCreateDialog(int id, Bundle args)243 public Dialog onCreateDialog(int id, Bundle args) { 244 return createDialog(this, id, args); 245 } 246 247 @Override getTestId()248 public String getTestId() { 249 return getClass().getName(); 250 } 251 252 @Override getTestDetails()253 public String getTestDetails() { 254 return null; 255 } 256 257 @Override setTestResultAndFinish(boolean passed)258 public void setTestResultAndFinish(boolean passed) { 259 PassFailButtons.setTestResultAndFinishHelper( 260 this, getTestId(), getTestDetails(), passed, getReportLog()); 261 } 262 263 @Override getReportLog()264 public ReportLog getReportLog() { return reportLog; } 265 updatePassButton()266 public void updatePassButton() { 267 getPassButton().setEnabled(mAdapter.allTestsPassed()); 268 } 269 } 270 271 protected static <T extends android.app.Activity & PassFailActivity> setPassFailClickListeners(final T activity)272 void setPassFailClickListeners(final T activity) { 273 View.OnClickListener clickListener = new View.OnClickListener() { 274 @Override 275 public void onClick(View target) { 276 setTestResultAndFinish(activity, activity.getTestId(), activity.getTestDetails(), 277 activity.getReportLog(), target); 278 } 279 }; 280 281 View passButton = activity.findViewById(R.id.pass_button); 282 passButton.setOnClickListener(clickListener); 283 passButton.setOnLongClickListener(new View.OnLongClickListener() { 284 @Override 285 public boolean onLongClick(View view) { 286 Toast.makeText(activity, R.string.pass_button_text, Toast.LENGTH_SHORT).show(); 287 return true; 288 } 289 }); 290 291 View failButton = activity.findViewById(R.id.fail_button); 292 failButton.setOnClickListener(clickListener); 293 failButton.setOnLongClickListener(new View.OnLongClickListener() { 294 @Override 295 public boolean onLongClick(View view) { 296 Toast.makeText(activity, R.string.fail_button_text, Toast.LENGTH_SHORT).show(); 297 return true; 298 } 299 }); 300 } 301 setInfo(final android.app.Activity activity, final int titleId, final int messageId, final int viewId)302 protected static void setInfo(final android.app.Activity activity, final int titleId, 303 final int messageId, final int viewId) { 304 // Show the middle "info" button and make it show the info dialog when clicked. 305 View infoButton = activity.findViewById(R.id.info_button); 306 infoButton.setVisibility(View.VISIBLE); 307 infoButton.setOnClickListener(new OnClickListener() { 308 @Override 309 public void onClick(View view) { 310 showInfoDialog(activity, titleId, messageId, viewId); 311 } 312 }); 313 infoButton.setOnLongClickListener(new View.OnLongClickListener() { 314 @Override 315 public boolean onLongClick(View view) { 316 Toast.makeText(activity, R.string.info_button_text, Toast.LENGTH_SHORT).show(); 317 return true; 318 } 319 }); 320 321 // Show the info dialog if the user has never seen it before. 322 if (!hasSeenInfoDialog(activity)) { 323 showInfoDialog(activity, titleId, messageId, viewId); 324 } 325 } 326 hasSeenInfoDialog(android.app.Activity activity)327 protected static boolean hasSeenInfoDialog(android.app.Activity activity) { 328 ContentResolver resolver = activity.getContentResolver(); 329 Cursor cursor = null; 330 try { 331 cursor = resolver.query(TestResultsProvider.getTestNameUri(activity), 332 new String[] {TestResultsProvider.COLUMN_TEST_INFO_SEEN}, null, null, null); 333 return cursor.moveToFirst() && cursor.getInt(0) > 0; 334 } finally { 335 if (cursor != null) { 336 cursor.close(); 337 } 338 } 339 } 340 showInfoDialog(final android.app.Activity activity, int titleId, int messageId, int viewId)341 protected static void showInfoDialog(final android.app.Activity activity, int titleId, 342 int messageId, int viewId) { 343 Bundle args = new Bundle(); 344 args.putInt(INFO_DIALOG_TITLE_ID, titleId); 345 args.putInt(INFO_DIALOG_MESSAGE_ID, messageId); 346 args.putInt(INFO_DIALOG_VIEW_ID, viewId); 347 activity.showDialog(INFO_DIALOG_ID, args); 348 } 349 createDialog(final android.app.Activity activity, int id, Bundle args)350 protected static Dialog createDialog(final android.app.Activity activity, int id, Bundle args) { 351 switch (id) { 352 case INFO_DIALOG_ID: 353 return createInfoDialog(activity, id, args); 354 default: 355 throw new IllegalArgumentException("Bad dialog id: " + id); 356 } 357 } 358 createInfoDialog(final android.app.Activity activity, int id, Bundle args)359 protected static Dialog createInfoDialog(final android.app.Activity activity, int id, 360 Bundle args) { 361 int viewId = args.getInt(INFO_DIALOG_VIEW_ID); 362 int titleId = args.getInt(INFO_DIALOG_TITLE_ID); 363 int messageId = args.getInt(INFO_DIALOG_MESSAGE_ID); 364 365 AlertDialog.Builder builder = new AlertDialog.Builder(activity).setIcon( 366 android.R.drawable.ic_dialog_info).setTitle(titleId); 367 if (viewId > 0) { 368 LayoutInflater inflater = (LayoutInflater) activity 369 .getSystemService(Context.LAYOUT_INFLATER_SERVICE); 370 builder.setView(inflater.inflate(viewId, null)); 371 } else { 372 builder.setMessage(messageId); 373 } 374 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { 375 @Override 376 public void onClick(DialogInterface dialog, int which) { 377 markSeenInfoDialog(activity); 378 } 379 }).setOnCancelListener(new OnCancelListener() { 380 @Override 381 public void onCancel(DialogInterface dialog) { 382 markSeenInfoDialog(activity); 383 } 384 }); 385 return builder.create(); 386 } 387 markSeenInfoDialog(android.app.Activity activity)388 protected static void markSeenInfoDialog(android.app.Activity activity) { 389 ContentResolver resolver = activity.getContentResolver(); 390 ContentValues values = new ContentValues(2); 391 values.put(TestResultsProvider.COLUMN_TEST_NAME, activity.getClass().getName()); 392 values.put(TestResultsProvider.COLUMN_TEST_INFO_SEEN, 1); 393 int numUpdated = resolver.update( 394 TestResultsProvider.getTestNameUri(activity), values, null, null); 395 if (numUpdated == 0) { 396 resolver.insert(TestResultsProvider.getResultContentUri(activity), values); 397 } 398 } 399 400 /** Set the test result corresponding to the button clicked and finish the activity. */ setTestResultAndFinish(android.app.Activity activity, String testId, String testDetails, ReportLog reportLog, View target)401 protected static void setTestResultAndFinish(android.app.Activity activity, String testId, 402 String testDetails, ReportLog reportLog, View target) { 403 boolean passed; 404 if (target.getId() == R.id.pass_button) { 405 passed = true; 406 } else if (target.getId() == R.id.fail_button) { 407 passed = false; 408 } else { 409 throw new IllegalArgumentException("Unknown id: " + target.getId()); 410 } 411 412 setTestResultAndFinishHelper(activity, testId, testDetails, passed, reportLog); 413 } 414 415 /** Set the test result and finish the activity. */ setTestResultAndFinishHelper(android.app.Activity activity, String testId, String testDetails, boolean passed, ReportLog reportLog)416 protected static void setTestResultAndFinishHelper(android.app.Activity activity, String testId, 417 String testDetails, boolean passed, ReportLog reportLog) { 418 if (passed) { 419 TestResult.setPassedResult(activity, testId, testDetails, reportLog); 420 } else { 421 TestResult.setFailedResult(activity, testId, testDetails, reportLog); 422 } 423 424 activity.finish(); 425 } 426 getPassButtonView(android.app.Activity activity)427 protected static ImageButton getPassButtonView(android.app.Activity activity) { 428 return (ImageButton) activity.findViewById(R.id.pass_button); 429 } 430 431 public static class CtsVerifierReportLog extends ReportLog { 432 433 } 434 } 435