1/*
2 * Copyright (C) 2008 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
17package android.widget;
18
19import android.app.Activity;
20import android.app.Instrumentation;
21import android.os.SystemClock;
22import android.test.ActivityInstrumentationTestCase2;
23import android.view.KeyEvent;
24import android.view.View;
25
26import com.android.frameworks.coretests.R;
27
28/**
29 * Test {@link DatePicker} focus changes.
30 */
31public class DatePickerFocusTest extends ActivityInstrumentationTestCase2<DatePickerActivity> {
32
33    private Activity mActivity;
34    private Instrumentation mInstrumentation;
35    private DatePicker mDatePicker;
36
37    public DatePickerFocusTest() {
38        super(DatePickerActivity.class);
39    }
40
41    @Override
42    protected void setUp() throws Exception {
43        super.setUp();
44
45        mActivity = getActivity();
46        mInstrumentation = getInstrumentation();
47
48        mDatePicker = (DatePicker) mActivity.findViewById(R.id.datePicker);
49    }
50
51    /**
52     * Tabs (forward and backward) through the DatePicker to ensure the correct
53     * Views gain focus.
54     */
55    public void testFocusTravel() throws Throwable {
56        runTestOnUiThread(new Runnable() {
57            @Override
58            public void run() {
59                assertTrue(mDatePicker.requestFocus());
60            }
61        });
62        assertViewHasFocus(com.android.internal.R.id.date_picker_header_year);
63        sendKey(KeyEvent.KEYCODE_TAB);
64        assertViewHasFocus(com.android.internal.R.id.prev);
65        sendKey(KeyEvent.KEYCODE_TAB);
66        assertViewHasFocus(com.android.internal.R.id.next);
67        sendKey(KeyEvent.KEYCODE_TAB);
68        assertViewHasFocus(com.android.internal.R.id.day_picker_view_pager);
69        sendKey(KeyEvent.KEYCODE_TAB);
70        assertViewHasFocus(R.id.belowPicker);
71        sendShiftKey(KeyEvent.KEYCODE_TAB);
72        assertViewHasFocus(com.android.internal.R.id.day_picker_view_pager);
73        sendShiftKey(KeyEvent.KEYCODE_TAB);
74        assertViewHasFocus(com.android.internal.R.id.next);
75        sendShiftKey(KeyEvent.KEYCODE_TAB);
76        assertViewHasFocus(com.android.internal.R.id.prev);
77        sendShiftKey(KeyEvent.KEYCODE_TAB);
78        assertViewHasFocus(com.android.internal.R.id.date_picker_header_year);
79    }
80
81    private void sendKey(int keycode) {
82        mInstrumentation.sendKeyDownUpSync(keycode);
83        mInstrumentation.waitForIdleSync();
84    }
85
86    private void assertViewHasFocus(final int id) throws Throwable {
87        runTestOnUiThread(new Runnable() {
88            @Override
89            public void run() {
90                View view = mActivity.findViewById(id);
91                assertTrue(view.hasFocus());
92            }
93        });
94    }
95
96    private void sendShiftKey(int keycode) {
97        final KeyEvent shiftDown = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_SHIFT_LEFT);
98        mInstrumentation.sendKeySync(shiftDown);
99
100        final KeyEvent keyDown = new KeyEvent(SystemClock.uptimeMillis(),
101                SystemClock.uptimeMillis(), KeyEvent.ACTION_DOWN, keycode, 0,
102                KeyEvent.META_SHIFT_ON);
103        mInstrumentation.sendKeySync(keyDown);
104
105        final KeyEvent keyUp = new KeyEvent(SystemClock.uptimeMillis(),
106                SystemClock.uptimeMillis(), KeyEvent.ACTION_UP, keycode, 0,
107                KeyEvent.META_SHIFT_ON);
108        mInstrumentation.sendKeySync(keyUp);
109
110        final KeyEvent shiftUp = new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_SHIFT_LEFT);
111        mInstrumentation.sendKeySync(shiftUp);
112
113        mInstrumentation.waitForIdleSync();
114    }
115
116    /**
117     * Tests to ensure the keyboard can select the current year.
118     */
119    public void testYearChoice() throws Throwable {
120        setKnownDate();
121        runTestOnUiThread(new Runnable() {
122            @Override
123            public void run() {
124                View year = mDatePicker.
125                        findViewById(com.android.internal.R.id.date_picker_header_year);
126                assertTrue(year.requestFocus());
127            }
128        });
129        sendKey(KeyEvent.KEYCODE_ENTER);
130        runTestOnUiThread(new Runnable() {
131            @Override
132            public void run() {
133                View yearSelect = mDatePicker.
134                        findViewById(com.android.internal.R.id.date_picker_year_picker);
135                assertEquals(yearSelect, mDatePicker.findFocus());
136            }
137        });
138        sendKey(KeyEvent.KEYCODE_DPAD_UP);
139        sendKey(KeyEvent.KEYCODE_ENTER);
140        runTestOnUiThread(new Runnable() {
141            @Override
142            public void run() {
143                View yearSelect = mDatePicker.
144                        findViewById(com.android.internal.R.id.date_picker_year_picker);
145                assertNotSame(View.VISIBLE, yearSelect.getVisibility());
146                View year = mDatePicker.
147                        findViewById(com.android.internal.R.id.date_picker_header_year);
148                assertTrue(year.hasFocus());
149                assertEquals(2014, mDatePicker.getYear());
150            }
151        });
152    }
153
154    public void testArrowThroughDays() throws Throwable {
155        setKnownDate();
156        runTestOnUiThread(new Runnable() {
157            @Override
158            public void run() {
159                View prev = mDatePicker.findViewById(com.android.internal.R.id.next);
160                prev.requestFocus();
161            }
162        });
163        sendKey(KeyEvent.KEYCODE_TAB);
164        // Should select the current date and the date shouldn't change
165        sendKey(KeyEvent.KEYCODE_ENTER);
166        assertDateIs(12, 31, 2015);
167        // Move right to January 24, 2016
168        sendKey(KeyEvent.KEYCODE_DPAD_RIGHT);
169        sendKey(KeyEvent.KEYCODE_ENTER);
170        assertDateIs(1, 24, 2016);
171        // Move down to January 31, 2016
172        sendKey(KeyEvent.KEYCODE_DPAD_DOWN);
173        sendKey(KeyEvent.KEYCODE_ENTER);
174        assertDateIs(1, 31, 2016);
175        // Move up to January 5, 2016
176        sendKey(KeyEvent.KEYCODE_DPAD_UP);
177        sendKey(KeyEvent.KEYCODE_DPAD_UP);
178        sendKey(KeyEvent.KEYCODE_DPAD_UP);
179        sendKey(KeyEvent.KEYCODE_DPAD_UP);
180        sendKey(KeyEvent.KEYCODE_DPAD_RIGHT);
181        sendKey(KeyEvent.KEYCODE_DPAD_RIGHT);
182        sendKey(KeyEvent.KEYCODE_ENTER);
183        assertDateIs(1, 5, 2016);
184        // Move up to prev arrow key
185        sendKey(KeyEvent.KEYCODE_DPAD_UP);
186        assertViewHasFocus(com.android.internal.R.id.prev);
187        // tab back into the day-selection pager
188        sendKey(KeyEvent.KEYCODE_TAB);
189        sendKey(KeyEvent.KEYCODE_TAB);
190        sendKey(KeyEvent.KEYCODE_ENTER);
191        assertViewHasFocus(com.android.internal.R.id.day_picker_view_pager);
192        assertDateIs(1, 5, 2016);
193
194        // Move up out again, then down back into the day-selection pager.
195        // It should land right below the prev button (1/3/2016)
196        sendKey(KeyEvent.KEYCODE_DPAD_UP);
197        sendKey(KeyEvent.KEYCODE_DPAD_DOWN);
198        sendKey(KeyEvent.KEYCODE_ENTER);
199        assertViewHasFocus(com.android.internal.R.id.day_picker_view_pager);
200        assertDateIs(1, 3, 2016);
201
202        // Move left to previous month (12/12/2015)
203        sendKey(KeyEvent.KEYCODE_DPAD_LEFT);
204        sendKey(KeyEvent.KEYCODE_ENTER);
205        assertDateIs(12, 12, 2015);
206        // Now make sure the start of the month works
207        // Move up to 12/5/2015 and right to 1/1/2016
208        sendKey(KeyEvent.KEYCODE_DPAD_UP);
209        sendKey(KeyEvent.KEYCODE_DPAD_RIGHT);
210        sendKey(KeyEvent.KEYCODE_ENTER);
211        assertDateIs(1, 1, 2016);
212        // Now make sure the left key goes back to previous month (12/5/2015)
213        sendKey(KeyEvent.KEYCODE_DPAD_LEFT);
214        sendKey(KeyEvent.KEYCODE_ENTER);
215        assertDateIs(12, 5, 2015);
216        // Now go to a mismatched row (no such row on previous month)
217        // This moves over to 1/31/2016 and then left to 12/31/2015
218        sendKey(KeyEvent.KEYCODE_DPAD_DOWN);
219        sendKey(KeyEvent.KEYCODE_DPAD_DOWN);
220        sendKey(KeyEvent.KEYCODE_DPAD_DOWN);
221        sendKey(KeyEvent.KEYCODE_DPAD_RIGHT);
222        sendKey(KeyEvent.KEYCODE_DPAD_DOWN);
223        sendKey(KeyEvent.KEYCODE_DPAD_DOWN);
224        sendKey(KeyEvent.KEYCODE_DPAD_LEFT);
225        sendKey(KeyEvent.KEYCODE_ENTER);
226        assertDateIs(12, 31, 2015);
227    }
228
229    private void assertDateIs(int month, final int day, final int year) throws Throwable {
230        final int monthInt = month - 1; // months are 0-based
231        runTestOnUiThread(new Runnable() {
232            @Override
233            public void run() {
234                assertEquals(day, mDatePicker.getDayOfMonth());
235                assertEquals(year, mDatePicker.getYear());
236                assertEquals(monthInt, mDatePicker.getMonth());
237            }
238        });
239    }
240
241    private void setKnownDate() throws Throwable {
242        runTestOnUiThread(new Runnable() {
243            @Override
244            public void run() {
245                mDatePicker.updateDate(2015, 11, 31); // December 31, 2015
246            }
247        });
248        mInstrumentation.waitForIdleSync();
249    }
250}
251