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