Home | History | Annotate | Download | only in verifier
      1 /*
      2  * Copyright (C) 2015 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.content.ActivityNotFoundException;
     21 import android.content.Context;
     22 import android.content.DialogInterface;
     23 import android.content.Intent;
     24 import android.content.pm.PackageManager;
     25 import android.database.DataSetObserver;
     26 import android.os.Bundle;
     27 import android.util.Log;
     28 import android.view.LayoutInflater;
     29 import android.view.MotionEvent;
     30 import android.view.View;
     31 import android.view.View.OnClickListener;
     32 import android.widget.Button;
     33 import android.widget.ImageView;
     34 import android.widget.ListView;
     35 import android.widget.TextView;
     36 import android.widget.Toast;
     37 
     38 import com.android.cts.verifier.R;
     39 
     40 /**
     41  * Test list activity that supports showing dialogs with pass/fail buttons instead of
     42  * starting new activities.
     43  * In addition to that dialogs have a 'go' button that can be configured to launch an intent.
     44  * Instructions are shown on top of the screen and a test preparation button is provided.
     45  */
     46 public abstract class DialogTestListActivity extends PassFailButtons.TestListActivity {
     47     private final String TAG = "DialogTestListActivity";
     48     private final int mLayoutId;
     49     private final int mTitleStringId;
     50     private final int mInfoStringId;
     51     private final int mInstructionsStringId;
     52 
     53     protected Button mPrepareTestButton;
     54     protected ListView mTestFeaturesList;
     55 
     56     protected int mCurrentTestPosition;
     57 
     58     protected DialogTestListActivity(int layoutId, int titleStringId, int infoStringId,
     59             int instructionsStringId) {
     60         mLayoutId = layoutId;
     61         mTitleStringId = titleStringId;
     62         mInfoStringId = infoStringId;
     63         mInstructionsStringId = instructionsStringId;
     64     }
     65 
     66     @Override
     67     protected void onCreate(Bundle savedInstanceState) {
     68         super.onCreate(savedInstanceState);
     69 
     70         setContentView(mLayoutId);
     71         setInfoResources(mTitleStringId, mInfoStringId, -1);
     72         setPassFailButtonClickListeners();
     73         getPassButton().setEnabled(false);
     74         setResult(RESULT_CANCELED);
     75 
     76         ArrayTestListAdapter adapter = new ArrayTestListAdapter(this);
     77 
     78         setupTests(adapter);
     79 
     80         adapter.registerDataSetObserver(new DataSetObserver() {
     81             @Override
     82             public void onChanged() {
     83                 updatePassButton();
     84             }
     85         });
     86 
     87         setTestListAdapter(adapter);
     88 
     89         mCurrentTestPosition = 0;
     90 
     91         TextView instructionTextView = (TextView) findViewById(R.id.test_instructions);
     92         instructionTextView.setText(mInstructionsStringId);
     93         mPrepareTestButton = (Button) findViewById(R.id.prepare_test_button);
     94         mTestFeaturesList = (ListView) findViewById(android.R.id.list);
     95         if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)) {
     96             mTestFeaturesList.setOnTouchListener((View v, MotionEvent e) -> {
     97                 switch (e.getAction()) {
     98                     case MotionEvent.ACTION_DOWN:
     99                         v.getParent().requestDisallowInterceptTouchEvent(true);
    100                         break;
    101                     case MotionEvent.ACTION_UP:
    102                         v.getParent().requestDisallowInterceptTouchEvent(false);
    103                         break;
    104                     default:
    105                 }
    106                 return false;
    107             });
    108         }
    109     }
    110 
    111     /**
    112      * Subclasses must add their tests items to the provided adapter(usually instances of
    113      * {@link DialogTestListItem} or {@link DialogTestListItemWithIcon} but any class deriving from
    114      * {@link TestListAdapter.TestListItem} will do).
    115      *
    116      * @param adapter The adapter to add test items to.
    117      */
    118     protected abstract void setupTests(ArrayTestListAdapter adapter);
    119 
    120     public class DefaultTestCallback implements DialogTestListItem.TestCallback {
    121         final private DialogTestListItem mTest;
    122 
    123         public DefaultTestCallback(DialogTestListItem test) {
    124             mTest = test;
    125         }
    126 
    127         @Override
    128         public void onPass() {
    129             clearRemainingState(mTest);
    130             setTestResult(mTest, TestResult.TEST_RESULT_PASSED);
    131         }
    132 
    133         @Override
    134         public void onFail() {
    135             clearRemainingState(mTest);
    136             setTestResult(mTest, TestResult.TEST_RESULT_FAILED);
    137         }
    138     }
    139 
    140     public void showManualTestDialog(final DialogTestListItem test) {
    141         showManualTestDialog(test, new DefaultTestCallback(test));
    142     }
    143 
    144     public void showManualTestDialog(final DialogTestListItem test,
    145             final DialogTestListItem.TestCallback callback) {
    146         AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this)
    147                 .setIcon(android.R.drawable.ic_dialog_info)
    148                 .setTitle(mTitleStringId)
    149                 .setNeutralButton(R.string.go_button_text, null)
    150                 .setPositiveButton(R.string.pass_button_text, new AlertDialog.OnClickListener() {
    151                     @Override
    152                     public void onClick(DialogInterface dialog, int which) {
    153                         callback.onPass();
    154                     }
    155                 })
    156                 .setNegativeButton(R.string.fail_button_text, new AlertDialog.OnClickListener() {
    157                     @Override
    158                     public void onClick(DialogInterface dialog, int which) {
    159                         callback.onFail();
    160                     }
    161                 });
    162         View customView = test.getCustomView();
    163         if (customView != null) {
    164             dialogBuilder.setView(customView);
    165         } else {
    166             dialogBuilder.setMessage(test.getManualTestInstruction());
    167         }
    168         final AlertDialog dialog = dialogBuilder.show();
    169         // Note: Setting the OnClickListener on the Dialog rather than the Builder, prevents the
    170         // dialog being dismissed on onClick.
    171         dialog.getButton(AlertDialog.BUTTON_NEUTRAL).setOnClickListener(new OnClickListener() {
    172             @Override
    173             public void onClick(View v) {
    174                 if (!startTestIntent(test)) {
    175                     dialog.dismiss();
    176                 }
    177             }
    178         });
    179     }
    180 
    181     @Override
    182     protected void handleItemClick(ListView l, View v, int position, long id) {
    183         TestListAdapter.TestListItem test = (TestListAdapter.TestListItem) getListAdapter()
    184                 .getItem(position);
    185         if (test instanceof DialogTestListItem) {
    186             mCurrentTestPosition = position;
    187             ((DialogTestListItem) test).performTest(this);
    188         } else {
    189             try {
    190                 super.handleItemClick(l, v, position, id);
    191             } catch (ActivityNotFoundException e) {
    192                 Log.d(TAG, "handleItemClick() threw exception: ", e);
    193                 setTestResult(test, TestResult.TEST_RESULT_FAILED);
    194                 showToast(R.string.test_failed_cannot_start_intent);
    195             }
    196         }
    197     }
    198 
    199 
    200     /**
    201      * Start a test's manual intent
    202      *
    203      * @param test The test the manual intent of which is to be started.
    204      * @return true if activity could be started successfully, false otherwise.
    205      */
    206     boolean startTestIntent(final DialogTestListItem test) {
    207         final Intent intent = test.intent;
    208         try {
    209             startActivity(intent);
    210         } catch (ActivityNotFoundException e) {
    211             Log.w(TAG, "Cannot start activity.", e);
    212             Toast.makeText(this, "Cannot start " + intent, Toast.LENGTH_LONG).show();
    213             setTestResult(test, TestResult.TEST_RESULT_FAILED);
    214             return false;
    215         }
    216         return true;
    217     }
    218 
    219     protected void clearRemainingState(final DialogTestListItem test) {
    220         // do nothing, override in subclass if needed
    221     }
    222 
    223     protected void setTestResult(TestListAdapter.TestListItem test, int result) {
    224         // Bundle result in an intent to feed into handleLaunchTestResult
    225         Intent resultIntent = new Intent();
    226         TestResult.addResultData(resultIntent, result, test.testName, /* testDetails */ null,
    227                 /* reportLog */ null);
    228         handleLaunchTestResult(RESULT_OK, resultIntent);
    229         getListView().smoothScrollToPosition(mCurrentTestPosition + 1);
    230     }
    231 
    232     protected void setTestResult(String testName, int result) {
    233         // Bundle result in an intent to feed into handleLaunchTestResult
    234         Intent resultIntent = new Intent();
    235         TestResult.addResultData(resultIntent, result, testName, /* testDetails */ null,
    236                 /* reportLog */ null);
    237         handleLaunchTestResult(RESULT_OK, resultIntent);
    238         getListView().smoothScrollToPosition(mCurrentTestPosition + 1);
    239     }
    240 
    241     protected void showToast(int messageId) {
    242         String message = getString(messageId);
    243         Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
    244     }
    245 
    246     protected static class DialogTestListItem extends TestListAdapter.TestListItem {
    247 
    248         public interface TestCallback {
    249             void onPass();
    250 
    251             void onFail();
    252         }
    253 
    254         private String mManualInstruction;
    255 
    256         public DialogTestListItem(Context context, String nameId, String testId) {
    257             super(nameId, testId, null, null, null, null);
    258         }
    259 
    260         public DialogTestListItem(Context context, int nameResId, String testId) {
    261             super(context.getString(nameResId), testId, null, null, null, null);
    262         }
    263 
    264         public DialogTestListItem(Context context, int nameResId, String testId,
    265                 int testInstructionResId, Intent testIntent) {
    266             super(context.getString(nameResId), testId, testIntent, null, null, null);
    267             mManualInstruction = context.getString(testInstructionResId);
    268         }
    269 
    270         public void performTest(DialogTestListActivity activity) {
    271             activity.showManualTestDialog(this);
    272         }
    273 
    274         public String getManualTestInstruction() {
    275             return mManualInstruction;
    276         }
    277 
    278         public Intent getManualTestIntent() {
    279             return intent;
    280         }
    281 
    282         public View getCustomView() {
    283             return null;
    284         }
    285 
    286         @Override
    287         boolean isTest() {
    288             return true;
    289         }
    290     }
    291 
    292     protected static class DialogTestListItemWithIcon extends DialogTestListItem {
    293 
    294         private final int mImageResId;
    295         private final Context mContext;
    296 
    297         public DialogTestListItemWithIcon(Context context, int nameResId, String testId,
    298                 int testInstructionResId, Intent testIntent, int imageResId) {
    299             super(context, nameResId, testId, testInstructionResId, testIntent);
    300             mContext = context;
    301             mImageResId = imageResId;
    302         }
    303 
    304         @Override
    305         public View getCustomView() {
    306             LayoutInflater layoutInflater = LayoutInflater.from(mContext);
    307             View view = layoutInflater.inflate(R.layout.dialog_custom_view,
    308                     null /* root */);
    309             ((ImageView) view.findViewById(R.id.sample_icon)).setImageResource(mImageResId);
    310             ((TextView) view.findViewById(R.id.message)).setText(getManualTestInstruction());
    311             return view;
    312         }
    313     }
    314 }
    315