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