1 /*
2  * Copyright (C) 2017 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 package android.autofillservice.cts.activities;
17 
18 import static com.google.common.truth.Truth.assertWithMessage;
19 
20 import android.autofillservice.cts.R;
21 import android.autofillservice.cts.testcore.MultipleTimesTextWatcher;
22 import android.autofillservice.cts.testcore.OneTimeDateListener;
23 import android.autofillservice.cts.testcore.Visitor;
24 import android.content.Intent;
25 import android.os.Bundle;
26 import android.widget.Button;
27 import android.widget.DatePicker;
28 import android.widget.EditText;
29 
30 import java.util.concurrent.CountDownLatch;
31 import java.util.concurrent.TimeUnit;
32 
33 /**
34  * Base class for an activity that has the following fields:
35  *
36  * <ul>
37  *   <li>A DatePicker (id: date_picker)
38  *   <li>An EditText that is filled with the DatePicker when it changes (id: output)
39  *   <li>An OK button that finishes it and navigates to the {@link WelcomeActivity}
40  * </ul>
41  *
42  * <p>It's abstract because the sub-class must provide the view id, so it can support multiple
43  * UI types (like calendar and spinner).
44  */
45 public abstract class AbstractDatePickerActivity extends AbstractAutoFillActivity {
46 
47     private static final long OK_TIMEOUT_MS = 1000;
48 
49     public static final String ID_DATE_PICKER = "date_picker";
50     public static final String ID_OUTPUT = "output";
51 
52     private DatePicker mDatePicker;
53     private EditText mOutput;
54     private Button mOk;
55 
56     private FillExpectation mExpectation;
57     private CountDownLatch mOkLatch;
58 
getContentView()59     protected abstract int getContentView();
60 
61     @Override
onCreate(Bundle savedInstanceState)62     protected void onCreate(Bundle savedInstanceState) {
63         super.onCreate(savedInstanceState);
64 
65         setContentView(getContentView());
66 
67         mDatePicker = (DatePicker) findViewById(R.id.date_picker);
68 
69         mDatePicker.setOnDateChangedListener((v, y, m, d) -> updateOutputWithDate(y, m, d));
70 
71         mOutput = (EditText) findViewById(R.id.output);
72         mOk = (Button) findViewById(R.id.ok);
73         mOk.setOnClickListener((v) -> ok());
74     }
75 
getDatePicker()76     public DatePicker getDatePicker() {
77         return mDatePicker;
78     }
79 
updateOutputWithDate(int year, int month, int day)80     private void updateOutputWithDate(int year, int month, int day) {
81         final String date = year + "/" + month + "/" + day;
82         mOutput.setText(date);
83     }
84 
ok()85     private void ok() {
86         final Intent intent = new Intent(this, WelcomeActivity.class);
87         intent.putExtra(WelcomeActivity.EXTRA_MESSAGE, "Good news everyone! The world didn't end!");
88         startActivity(intent);
89         if (mOkLatch != null) {
90             // Latch is not set when activity launched outside tests
91             mOkLatch.countDown();
92         }
93         finish();
94     }
95 
96     /**
97      * Sets the expectation for an auto-fill request, so it can be asserted through
98      * {@link #assertAutoFilled()} later.
99      */
expectAutoFill(String output, int year, int month, int day)100     public void expectAutoFill(String output, int year, int month, int day) {
101         mExpectation = new FillExpectation(output, year, month, day);
102         mOutput.addTextChangedListener(mExpectation.outputWatcher);
103         mDatePicker.setOnDateChangedListener((v, y, m, d) -> {
104             updateOutputWithDate(y, m, d);
105             mExpectation.dateListener.onDateChanged(v, y, m, d);
106         });
107     }
108 
109     /**
110      * Asserts the activity was auto-filled with the values passed to
111      * {@link #expectAutoFill(String, int, int, int)}.
112      */
assertAutoFilled()113     public void assertAutoFilled() throws Exception {
114         assertWithMessage("expectAutoFill() not called").that(mExpectation).isNotNull();
115         mExpectation.outputWatcher.assertAutoFilled();
116         mExpectation.dateListener.assertAutoFilled();
117     }
118 
119     /**
120      * Visits the {@code output} in the UiThread.
121      */
onOutput(Visitor<EditText> v)122     public void onOutput(Visitor<EditText> v) {
123         syncRunOnUiThread(() -> v.visit(mOutput));
124     }
125 
126     /**
127      * Sets the date in the {@link DatePicker}.
128      */
setDate(int year, int month, int day)129     public void setDate(int year, int month, int day) {
130         syncRunOnUiThread(() -> mDatePicker.updateDate(year, month, day));
131     }
132 
133     /**
134      * Taps the ok button in the UI thread.
135      */
tapOk()136     public void tapOk() throws Exception {
137         mOkLatch = new CountDownLatch(1);
138         syncRunOnUiThread(() -> mOk.performClick());
139         boolean called = mOkLatch.await(OK_TIMEOUT_MS, TimeUnit.MILLISECONDS);
140         assertWithMessage("Timeout (%s ms) waiting for OK action", OK_TIMEOUT_MS)
141                 .that(called).isTrue();
142     }
143 
144     /**
145      * Holder for the expected auto-fill values.
146      */
147     private final class FillExpectation {
148         private final MultipleTimesTextWatcher outputWatcher;
149         private final OneTimeDateListener dateListener;
150 
FillExpectation(String output, int year, int month, int day)151         private FillExpectation(String output, int year, int month, int day) {
152             // Output is called twice: by the DateChangeListener and by auto-fill.
153             outputWatcher = new MultipleTimesTextWatcher("output", 2, mOutput, output);
154             dateListener = new OneTimeDateListener("datePicker", mDatePicker, year, month, day);
155         }
156     }
157 }
158