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