Home | History | Annotate | Download | only in verifier
      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     private 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          */
     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          */
     81         void setInfoResources(int titleId, int messageId, int viewId);
     82 
     83         View getPassButton();
     84 
     85         /**
     86          * Returns a unique identifier for the test.  Usually, this is just the class name.
     87          */
     88         String getTestId();
     89 
     90         /** @return null or details about the test run. */
     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          */
     98         void setTestResultAndFinish(boolean passed);
     99 
    100         /** @return A {@link ReportLog} that is used to record test metric data. */
    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 
    108         public Activity() {
    109            this.reportLog = new CtsVerifierReportLog();
    110         }
    111 
    112         @Override
    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
    123         protected void onPause() {
    124             super.onPause();
    125             if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)) {
    126                 mWakeLock.release();
    127             }
    128         }
    129 
    130         @Override
    131         public void setPassFailButtonClickListeners() {
    132             setPassFailClickListeners(this);
    133         }
    134 
    135         @Override
    136         public void setInfoResources(int titleId, int messageId, int viewId) {
    137             setInfo(this, titleId, messageId, viewId);
    138         }
    139 
    140         @Override
    141         public View getPassButton() {
    142             return getPassButtonView(this);
    143         }
    144 
    145         @Override
    146         public Dialog onCreateDialog(int id, Bundle args) {
    147             return createDialog(this, id, args);
    148         }
    149 
    150         @Override
    151         public String getTestId() {
    152             return getClass().getName();
    153         }
    154 
    155         @Override
    156         public String getTestDetails() {
    157             return null;
    158         }
    159 
    160         @Override
    161         public void setTestResultAndFinish(boolean passed) {
    162             PassFailButtons.setTestResultAndFinishHelper(
    163                     this, getTestId(), getTestDetails(), passed, getReportLog());
    164         }
    165 
    166         @Override
    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 
    174         public ListActivity() {
    175             this.reportLog = new CtsVerifierReportLog();
    176         }
    177 
    178         @Override
    179         public void setPassFailButtonClickListeners() {
    180             setPassFailClickListeners(this);
    181         }
    182 
    183         @Override
    184         public void setInfoResources(int titleId, int messageId, int viewId) {
    185             setInfo(this, titleId, messageId, viewId);
    186         }
    187 
    188         @Override
    189         public View getPassButton() {
    190             return getPassButtonView(this);
    191         }
    192 
    193         @Override
    194         public Dialog onCreateDialog(int id, Bundle args) {
    195             return createDialog(this, id, args);
    196         }
    197 
    198         @Override
    199         public String getTestId() {
    200             return getClass().getName();
    201         }
    202 
    203         @Override
    204         public String getTestDetails() {
    205             return null;
    206         }
    207 
    208         @Override
    209         public void setTestResultAndFinish(boolean passed) {
    210             PassFailButtons.setTestResultAndFinishHelper(
    211                     this, getTestId(), getTestDetails(), passed, getReportLog());
    212         }
    213 
    214         @Override
    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 
    223         public TestListActivity() {
    224             this.reportLog = new CtsVerifierReportLog();
    225         }
    226 
    227         @Override
    228         public void setPassFailButtonClickListeners() {
    229             setPassFailClickListeners(this);
    230         }
    231 
    232         @Override
    233         public void setInfoResources(int titleId, int messageId, int viewId) {
    234             setInfo(this, titleId, messageId, viewId);
    235         }
    236 
    237         @Override
    238         public View getPassButton() {
    239             return getPassButtonView(this);
    240         }
    241 
    242         @Override
    243         public Dialog onCreateDialog(int id, Bundle args) {
    244             return createDialog(this, id, args);
    245         }
    246 
    247         @Override
    248         public String getTestId() {
    249             return getClass().getName();
    250         }
    251 
    252         @Override
    253         public String getTestDetails() {
    254             return null;
    255         }
    256 
    257         @Override
    258         public void setTestResultAndFinish(boolean passed) {
    259             PassFailButtons.setTestResultAndFinishHelper(
    260                     this, getTestId(), getTestDetails(), passed, getReportLog());
    261         }
    262 
    263         @Override
    264         public ReportLog getReportLog() { return reportLog; }
    265     }
    266 
    267     private static <T extends android.app.Activity & PassFailActivity>
    268             void setPassFailClickListeners(final T activity) {
    269         View.OnClickListener clickListener = new View.OnClickListener() {
    270             @Override
    271             public void onClick(View target) {
    272                 setTestResultAndFinish(activity, activity.getTestId(), activity.getTestDetails(),
    273                         activity.getReportLog(), target);
    274             }
    275         };
    276 
    277         View passButton = activity.findViewById(R.id.pass_button);
    278         passButton.setOnClickListener(clickListener);
    279         passButton.setOnLongClickListener(new View.OnLongClickListener() {
    280             @Override
    281             public boolean onLongClick(View view) {
    282                 Toast.makeText(activity, R.string.pass_button_text, Toast.LENGTH_SHORT).show();
    283                 return true;
    284             }
    285         });
    286 
    287         View failButton = activity.findViewById(R.id.fail_button);
    288         failButton.setOnClickListener(clickListener);
    289         failButton.setOnLongClickListener(new View.OnLongClickListener() {
    290             @Override
    291             public boolean onLongClick(View view) {
    292                 Toast.makeText(activity, R.string.fail_button_text, Toast.LENGTH_SHORT).show();
    293                 return true;
    294             }
    295         });
    296     }
    297 
    298     private static void setInfo(final android.app.Activity activity, final int titleId,
    299             final int messageId, final int viewId) {
    300         // Show the middle "info" button and make it show the info dialog when clicked.
    301         View infoButton = activity.findViewById(R.id.info_button);
    302         infoButton.setVisibility(View.VISIBLE);
    303         infoButton.setOnClickListener(new OnClickListener() {
    304             @Override
    305             public void onClick(View view) {
    306                 showInfoDialog(activity, titleId, messageId, viewId);
    307             }
    308         });
    309         infoButton.setOnLongClickListener(new View.OnLongClickListener() {
    310             @Override
    311             public boolean onLongClick(View view) {
    312                 Toast.makeText(activity, R.string.info_button_text, Toast.LENGTH_SHORT).show();
    313                 return true;
    314             }
    315         });
    316 
    317         // Show the info dialog if the user has never seen it before.
    318         if (!hasSeenInfoDialog(activity)) {
    319             showInfoDialog(activity, titleId, messageId, viewId);
    320         }
    321     }
    322 
    323     private static boolean hasSeenInfoDialog(android.app.Activity activity) {
    324         ContentResolver resolver = activity.getContentResolver();
    325         Cursor cursor = null;
    326         try {
    327             cursor = resolver.query(
    328                     TestResultsProvider.getTestNameUri(activity.getClass().getName()),
    329                     new String[] {TestResultsProvider.COLUMN_TEST_INFO_SEEN}, null, null, null);
    330             return cursor.moveToFirst() && cursor.getInt(0) > 0;
    331         } finally {
    332             if (cursor != null) {
    333                 cursor.close();
    334             }
    335         }
    336     }
    337 
    338     private static void showInfoDialog(final android.app.Activity activity, int titleId,
    339             int messageId, int viewId) {
    340         Bundle args = new Bundle();
    341         args.putInt(INFO_DIALOG_TITLE_ID, titleId);
    342         args.putInt(INFO_DIALOG_MESSAGE_ID, messageId);
    343         args.putInt(INFO_DIALOG_VIEW_ID, viewId);
    344         activity.showDialog(INFO_DIALOG_ID, args);
    345     }
    346 
    347     private static Dialog createDialog(final android.app.Activity activity, int id, Bundle args) {
    348         switch (id) {
    349             case INFO_DIALOG_ID:
    350                 return createInfoDialog(activity, id, args);
    351             default:
    352                 throw new IllegalArgumentException("Bad dialog id: " + id);
    353         }
    354     }
    355 
    356     private static Dialog createInfoDialog(final android.app.Activity activity, int id,
    357             Bundle args) {
    358         int viewId = args.getInt(INFO_DIALOG_VIEW_ID);
    359         int titleId = args.getInt(INFO_DIALOG_TITLE_ID);
    360         int messageId = args.getInt(INFO_DIALOG_MESSAGE_ID);
    361 
    362         AlertDialog.Builder builder = new AlertDialog.Builder(activity).setIcon(
    363                 android.R.drawable.ic_dialog_info).setTitle(titleId);
    364         if (viewId > 0) {
    365             LayoutInflater inflater = (LayoutInflater) activity
    366                     .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    367             builder.setView(inflater.inflate(viewId, null));
    368         } else {
    369             builder.setMessage(messageId);
    370         }
    371         builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
    372             @Override
    373             public void onClick(DialogInterface dialog, int which) {
    374                 markSeenInfoDialog(activity);
    375             }
    376         }).setOnCancelListener(new OnCancelListener() {
    377             @Override
    378             public void onCancel(DialogInterface dialog) {
    379                 markSeenInfoDialog(activity);
    380             }
    381         });
    382         return builder.create();
    383     }
    384 
    385     private static void markSeenInfoDialog(android.app.Activity activity) {
    386         ContentResolver resolver = activity.getContentResolver();
    387         ContentValues values = new ContentValues(2);
    388         values.put(TestResultsProvider.COLUMN_TEST_NAME, activity.getClass().getName());
    389         values.put(TestResultsProvider.COLUMN_TEST_INFO_SEEN, 1);
    390         int numUpdated = resolver.update(
    391                 TestResultsProvider.getTestNameUri(activity.getClass().getName()),
    392                 values, null, null);
    393         if (numUpdated == 0) {
    394             resolver.insert(TestResultsProvider.RESULTS_CONTENT_URI, values);
    395         }
    396     }
    397 
    398     /** Set the test result corresponding to the button clicked and finish the activity. */
    399     private static void setTestResultAndFinish(android.app.Activity activity, String testId,
    400             String testDetails, ReportLog reportLog, View target) {
    401         boolean passed;
    402         switch (target.getId()) {
    403             case R.id.pass_button:
    404                 passed = true;
    405                 break;
    406             case R.id.fail_button:
    407                 passed = false;
    408                 break;
    409             default:
    410                 throw new IllegalArgumentException("Unknown id: " + target.getId());
    411         }
    412         setTestResultAndFinishHelper(activity, testId, testDetails, passed, reportLog);
    413     }
    414 
    415     /** Set the test result and finish the activity. */
    416     private 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 
    427     private 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