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