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