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
17package android.support.v17.leanback.widget;
18
19import static org.hamcrest.CoreMatchers.is;
20import static org.hamcrest.MatcherAssert.assertThat;
21
22import android.content.Context;
23import android.content.Intent;
24import android.support.test.InstrumentationRegistry;
25import android.support.test.filters.MediumTest;
26import android.support.test.rule.ActivityTestRule;
27import android.support.test.runner.AndroidJUnit4;
28import android.support.v17.leanback.test.R;
29import android.support.v17.leanback.widget.picker.TimePicker;
30import android.view.KeyEvent;
31import android.view.View;
32import android.view.ViewGroup;
33
34import org.junit.Rule;
35import org.junit.Test;
36import org.junit.runner.RunWith;
37
38@MediumTest
39@RunWith(AndroidJUnit4.class)
40public class TimePickerTest {
41
42    private static final String TAG = "TimePickerTest";
43    private static final long TRANSITION_LENGTH = 1000;
44    private static final long UPDATE_LENGTH = 1000;
45
46
47    Context mContext;
48    View mViewAbove;
49    TimePicker mTimePicker12HourView;
50    TimePicker mTimePicker24HourView;
51    View mViewBelow;
52
53    @Rule
54    public ActivityTestRule<TimePickerActivity> mActivityTestRule =
55            new ActivityTestRule<>(TimePickerActivity.class, false, false);
56    private TimePickerActivity mActivity;
57
58    public void initActivity(Intent intent) throws Throwable {
59        mActivity = mActivityTestRule.launchActivity(intent);
60        mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
61        mTimePicker12HourView = (TimePicker) mActivity.findViewById(R.id.time_picker12);
62        mTimePicker12HourView.setActivatedVisibleItemCount(3);
63        mTimePicker12HourView.setOnClickListener(new View.OnClickListener() {
64            @Override
65            public void onClick(View v) {
66                mTimePicker12HourView.setActivated(!mTimePicker12HourView.isActivated());
67            }
68        });
69
70        if (intent.getIntExtra(DatePickerActivity.EXTRA_LAYOUT_RESOURCE_ID,
71                R.layout.timepicker_with_other_widgets) == R.layout.timepicker_with_other_widgets) {
72            mViewAbove = mActivity.findViewById(R.id.above_picker);
73            mViewBelow = mActivity.findViewById(R.id.below_picker);
74            mTimePicker24HourView = (TimePicker) mActivity.findViewById(R.id.time_picker24);
75            mTimePicker24HourView.setActivatedVisibleItemCount(3);
76            mTimePicker24HourView.setOnClickListener(new View.OnClickListener() {
77                @Override
78                public void onClick(View v) {
79                    mTimePicker24HourView.setActivated(!mTimePicker24HourView.isActivated());
80                }
81            });
82        } else if (intent.getIntExtra(DatePickerActivity.EXTRA_LAYOUT_RESOURCE_ID,
83                R.layout.timepicker_with_other_widgets) == R.layout.timepicker_alone) {
84            // A layout with only a TimePicker widget that is initially activated.
85            mActivityTestRule.runOnUiThread(new Runnable() {
86                @Override
87                public void run() {
88                    mTimePicker12HourView.setActivated(true);
89                }
90            });
91            Thread.sleep(500);
92        }
93    }
94
95    @Test
96    public void testSetHourIn24hFormat() throws Throwable {
97        Intent intent = new Intent();
98        intent.putExtra(DatePickerActivity.EXTRA_LAYOUT_RESOURCE_ID,
99                R.layout.timepicker_with_other_widgets);
100        initActivity(intent);
101        mActivityTestRule.runOnUiThread(new Runnable() {
102            @Override
103            public void run() {
104                mTimePicker24HourView.setHour(0);
105            }
106        });
107        Thread.sleep(UPDATE_LENGTH);
108        assertThat("TimePicker in 24-hour mode returns a different hour in getHour()",
109                mTimePicker24HourView.getHour(), is(0));
110
111        mActivityTestRule.runOnUiThread(new Runnable() {
112            @Override
113            public void run() {
114                mTimePicker24HourView.setHour(11);
115            }
116        });
117        Thread.sleep(UPDATE_LENGTH);
118        assertThat("TimePicker in 24-hour mode returns a different hour in getHour()",
119                mTimePicker24HourView.getHour(), is(11));
120
121        mActivityTestRule.runOnUiThread(new Runnable() {
122            @Override
123            public void run() {
124                mTimePicker24HourView.setHour(12);
125            }
126        });
127        Thread.sleep(UPDATE_LENGTH);
128        assertThat("TimePicker in 24-hour mode returns a different hour in getHour()",
129                mTimePicker24HourView.getHour(), is(12));
130
131        mActivityTestRule.runOnUiThread(new Runnable() {
132            @Override
133            public void run() {
134                mTimePicker24HourView.setHour(13);
135            }
136        });
137        Thread.sleep(UPDATE_LENGTH);
138        assertThat("TimePicker in 24-hour mode returns a different hour in getHour()",
139                mTimePicker24HourView.getHour(), is(13));
140
141        mActivityTestRule.runOnUiThread(new Runnable() {
142            @Override
143            public void run() {
144                mTimePicker24HourView.setHour(23);
145            }
146        });
147        Thread.sleep(UPDATE_LENGTH);
148        assertThat("TimePicker in 24-hour mode returns a different hour in getHour()",
149                mTimePicker24HourView.getHour(), is(23));
150    }
151
152    @Test
153    public void testSetHourIn12hFormat() throws Throwable {
154        Intent intent = new Intent();
155        intent.putExtra(DatePickerActivity.EXTRA_LAYOUT_RESOURCE_ID,
156                R.layout.timepicker_with_other_widgets);
157        initActivity(intent);
158        mActivityTestRule.runOnUiThread(new Runnable() {
159            @Override
160            public void run() {
161                mTimePicker12HourView.setHour(0);
162            }
163        });
164        Thread.sleep(UPDATE_LENGTH);
165        assertThat("TimePicker in 24-hour mode returns a different hour in getHour()",
166                mTimePicker12HourView.getHour(), is(0));
167
168        mActivityTestRule.runOnUiThread(new Runnable() {
169            @Override
170            public void run() {
171                mTimePicker12HourView.setHour(11);
172            }
173        });
174        Thread.sleep(UPDATE_LENGTH);
175        assertThat("TimePicker in 24-hour mode returns a different hour in getHour()",
176                mTimePicker12HourView.getHour(), is(11));
177
178        mActivityTestRule.runOnUiThread(new Runnable() {
179            @Override
180            public void run() {
181                mTimePicker12HourView.setHour(12);
182            }
183        });
184        Thread.sleep(UPDATE_LENGTH);
185        assertThat("TimePicker in 24-hour mode returns a different hour in getHour()",
186                mTimePicker12HourView.getHour(), is(12));
187
188        mActivityTestRule.runOnUiThread(new Runnable() {
189            @Override
190            public void run() {
191                mTimePicker12HourView.setHour(13);
192            }
193        });
194        Thread.sleep(UPDATE_LENGTH);
195        assertThat("TimePicker in 24-hour mode returns a different hour in getHour()",
196                mTimePicker12HourView.getHour(), is(13));
197
198        mActivityTestRule.runOnUiThread(new Runnable() {
199            @Override
200            public void run() {
201                mTimePicker12HourView.setHour(23);
202            }
203        });
204        Thread.sleep(UPDATE_LENGTH);
205        assertThat("TimePicker in 24-hour mode returns a different hour in getHour()",
206                mTimePicker12HourView.getHour(), is(23));
207    }
208
209    @Test
210    public void testSetMinuteIn24hFormat() throws Throwable {
211        Intent intent = new Intent();
212        intent.putExtra(DatePickerActivity.EXTRA_LAYOUT_RESOURCE_ID,
213                R.layout.timepicker_with_other_widgets);
214        initActivity(intent);
215        mActivityTestRule.runOnUiThread(new Runnable() {
216            @Override
217            public void run() {
218                mTimePicker24HourView.setMinute(0);
219            }
220        });
221        Thread.sleep(UPDATE_LENGTH);
222        assertThat("TimePicker in 24-hour mode returns a different hour in getHour()",
223                mTimePicker24HourView.getMinute(), is(0));
224
225        mActivityTestRule.runOnUiThread(new Runnable() {
226            @Override
227            public void run() {
228                mTimePicker24HourView.setMinute(11);
229            }
230        });
231        Thread.sleep(UPDATE_LENGTH);
232        assertThat("TimePicker in 24-hour mode returns a different hour in getHour()",
233                mTimePicker24HourView.getMinute(), is(11));
234
235        mActivityTestRule.runOnUiThread(new Runnable() {
236            @Override
237            public void run() {
238                mTimePicker24HourView.setMinute(59);
239            }
240        });
241        Thread.sleep(UPDATE_LENGTH);
242        assertThat("TimePicker in 24-hour mode returns a different hour in getHour()",
243                mTimePicker24HourView.getMinute(), is(59));
244    }
245
246    @Test
247    public void testSetMinuteIn12hFormat() throws Throwable {
248        Intent intent = new Intent();
249        intent.putExtra(DatePickerActivity.EXTRA_LAYOUT_RESOURCE_ID,
250                R.layout.timepicker_with_other_widgets);
251        initActivity(intent);
252        mActivityTestRule.runOnUiThread(new Runnable() {
253            @Override
254            public void run() {
255                mTimePicker12HourView.setMinute(0);
256            }
257        });
258        Thread.sleep(UPDATE_LENGTH);
259        assertThat("TimePicker in 24-hour mode returns a different hour in getHour()",
260                mTimePicker12HourView.getMinute(), is(0));
261
262        mActivityTestRule.runOnUiThread(new Runnable() {
263            @Override
264            public void run() {
265                mTimePicker12HourView.setMinute(11);
266            }
267        });
268        Thread.sleep(UPDATE_LENGTH);
269        assertThat("TimePicker in 24-hour mode returns a different hour in getHour()",
270                mTimePicker12HourView.getMinute(), is(11));
271
272        mActivityTestRule.runOnUiThread(new Runnable() {
273            @Override
274            public void run() {
275                mTimePicker12HourView.setMinute(59);
276            }
277        });
278        Thread.sleep(UPDATE_LENGTH);
279        assertThat("TimePicker in 24-hour mode returns a different hour in getHour()",
280                mTimePicker12HourView.getMinute(), is(59));
281
282    }
283
284    @Test
285    public void testAmToPmTransition() throws Throwable {
286        Intent intent = new Intent();
287        intent.putExtra(DatePickerActivity.EXTRA_LAYOUT_RESOURCE_ID,
288                R.layout.timepicker_with_other_widgets);
289        initActivity(intent);
290        mActivityTestRule.runOnUiThread(new Runnable() {
291            @Override
292            public void run() {
293                mTimePicker12HourView.setHour(0);
294                mTimePicker12HourView.setMinute(47);
295            }
296        });
297        Thread.sleep(UPDATE_LENGTH);
298        assertThat("TimePicker in 12-hour mode returns a different hour in getHour()",
299                mTimePicker12HourView.getHour(), is(0));
300        assertThat("TimePicker in 12-hour mode returns a different hour in getMinute()",
301                mTimePicker12HourView.getMinute(), is(47));
302
303        // traverse to the AM/PM column of 12 hour TimePicker widget
304        sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
305        Thread.sleep(TRANSITION_LENGTH);
306        // Click once to activate
307        sendKeys(KeyEvent.KEYCODE_DPAD_CENTER);
308        Thread.sleep(TRANSITION_LENGTH);
309        sendKeys(KeyEvent.KEYCODE_DPAD_RIGHT);
310        Thread.sleep(TRANSITION_LENGTH);
311        sendKeys(KeyEvent.KEYCODE_DPAD_RIGHT);
312        Thread.sleep(TRANSITION_LENGTH);
313        // scroll down to PM value
314        sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
315        Thread.sleep(TRANSITION_LENGTH);
316        // Click now to deactivate
317        sendKeys(KeyEvent.KEYCODE_DPAD_CENTER);
318        Thread.sleep(TRANSITION_LENGTH);
319
320        assertThat("TimePicker in 24-hour mode returns a different hour in getHour() returns",
321                mTimePicker12HourView.getHour(), is(12));
322        assertThat("TimePicker in 24-hour mode returns a different hour in getHour() returns",
323                mTimePicker12HourView.getMinute(), is(47));
324    }
325
326    @Test
327    public void testPmToAmTransition() throws Throwable {
328        Intent intent = new Intent();
329        intent.putExtra(DatePickerActivity.EXTRA_LAYOUT_RESOURCE_ID,
330                R.layout.timepicker_with_other_widgets);
331        initActivity(intent);
332        mActivityTestRule.runOnUiThread(new Runnable() {
333            @Override
334            public void run() {
335                mTimePicker12HourView.setHour(12);
336                mTimePicker12HourView.setMinute(47);
337            }
338        });
339        Thread.sleep(UPDATE_LENGTH);
340        assertThat("TimePicker in 12-hour mode returns a different hour in getHour()",
341                mTimePicker12HourView.getHour(), is(12));
342        assertThat("TimePicker in 12-hour mode returns a different hour in getMinute()",
343                mTimePicker12HourView.getMinute(), is(47));
344
345        // traverse to the AM/PM column of 12 hour TimePicker widget
346        sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
347        Thread.sleep(TRANSITION_LENGTH);
348        // Click once to activate
349        sendKeys(KeyEvent.KEYCODE_DPAD_CENTER);
350        Thread.sleep(TRANSITION_LENGTH);
351        sendKeys(KeyEvent.KEYCODE_DPAD_RIGHT);
352        Thread.sleep(TRANSITION_LENGTH);
353        sendKeys(KeyEvent.KEYCODE_DPAD_RIGHT);
354        Thread.sleep(TRANSITION_LENGTH);
355        // scroll down to PM value
356        sendKeys(KeyEvent.KEYCODE_DPAD_UP);
357        Thread.sleep(TRANSITION_LENGTH);
358        // Click now to deactivate
359        sendKeys(KeyEvent.KEYCODE_DPAD_CENTER);
360        Thread.sleep(TRANSITION_LENGTH);
361
362        assertThat("TimePicker in 24-hour mode returns a different hour in getHour() returns",
363                mTimePicker12HourView.getHour(), is(0));
364        assertThat("TimePicker in 24-hour mode returns a different hour in getHour() returns",
365                mTimePicker12HourView.getMinute(), is(47));
366    }
367
368    @Test
369    public void test12To24HourFormatTransition() throws Throwable {
370        Intent intent = new Intent();
371        intent.putExtra(DatePickerActivity.EXTRA_LAYOUT_RESOURCE_ID,
372                R.layout.timepicker_with_other_widgets);
373        initActivity(intent);
374        mActivityTestRule.runOnUiThread(new Runnable() {
375            @Override
376            public void run() {
377                mTimePicker12HourView.setHour(14);
378                mTimePicker12HourView.setMinute(47);
379            }
380        });
381        Thread.sleep(UPDATE_LENGTH);
382        assertThat("TimePicker should be in 12-hour format.", mTimePicker12HourView.is24Hour(),
383                is(false));
384        mActivityTestRule.runOnUiThread(new Runnable() {
385            @Override
386            public void run() {
387                mTimePicker12HourView.setIs24Hour(true);
388            }
389        });
390        Thread.sleep(UPDATE_LENGTH);
391        assertThat("TimePicker should now be in 24-hour format.", mTimePicker12HourView.is24Hour(),
392                is(true));
393        // The hour and minute should not be changed.
394        assertThat("TimePicker in 24-hour mode returns a different hour in getHour() returns",
395                mTimePicker12HourView.getHour(), is(14));
396        assertThat("TimePicker in 24-hour mode returns a different hour in getHour() returns",
397                mTimePicker12HourView.getMinute(), is(47));
398    }
399
400    @Test
401    public void test24To12HourFormatTransition() throws Throwable {
402        Intent intent = new Intent();
403        intent.putExtra(DatePickerActivity.EXTRA_LAYOUT_RESOURCE_ID,
404                R.layout.timepicker_with_other_widgets);
405        initActivity(intent);
406        mActivityTestRule.runOnUiThread(new Runnable() {
407            @Override
408            public void run() {
409                mTimePicker24HourView.setHour(14);
410                mTimePicker24HourView.setMinute(47);
411            }
412        });
413        Thread.sleep(UPDATE_LENGTH);
414        assertThat("TimePicker should be in 12-hour format.", mTimePicker24HourView.is24Hour(),
415                is(true));
416        mActivityTestRule.runOnUiThread(new Runnable() {
417            @Override
418            public void run() {
419                mTimePicker24HourView.setIs24Hour(false);
420            }
421        });
422        Thread.sleep(UPDATE_LENGTH);
423        assertThat("TimePicker should now be in 24-hour format.", mTimePicker24HourView.is24Hour(),
424                is(false));
425        // The hour and minute should not be changed.
426        assertThat("TimePicker in 24-hour mode returns a different hour in getHour() returns",
427                mTimePicker24HourView.getHour(), is(14));
428        assertThat("TimePicker in 24-hour mode returns a different hour in getHour() returns",
429                mTimePicker24HourView.getMinute(), is(47));
430    }
431
432    @Test
433    public void testInitiallyActiveTimePicker()
434            throws Throwable {
435        Intent intent = new Intent();
436        intent.putExtra(DatePickerActivity.EXTRA_LAYOUT_RESOURCE_ID,
437                R.layout.timepicker_alone);
438        initActivity(intent);
439
440        mActivityTestRule.runOnUiThread(new Runnable() {
441            @Override
442            public void run() {
443                mTimePicker12HourView.setHour(14);
444                mTimePicker12HourView.setMinute(47);
445            }
446        });
447        Thread.sleep(UPDATE_LENGTH);
448
449        ViewGroup mTimePickerInnerView = (ViewGroup) mTimePicker12HourView.findViewById(
450                R.id.picker);
451
452        assertThat("The first column of TimePicker should initially hold focus",
453                mTimePickerInnerView.getChildAt(0).hasFocus(), is(true));
454
455        // focus on first column
456        sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
457        Thread.sleep(TRANSITION_LENGTH);
458        assertThat("The first column of TimePicker should still hold focus after scrolling down",
459                mTimePickerInnerView.getChildAt(0).hasFocus(), is(true));
460
461        // focus on second column
462        sendKeys(KeyEvent.KEYCODE_DPAD_RIGHT);
463        Thread.sleep(TRANSITION_LENGTH);
464        assertThat("The second column of TimePicker should hold focus after scrolling right",
465                mTimePickerInnerView.getChildAt(2).hasFocus(), is(true));
466
467        sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
468        Thread.sleep(TRANSITION_LENGTH);
469        assertThat("The second column of TimePicker should still hold focus after scrolling down",
470                mTimePickerInnerView.getChildAt(2).hasFocus(), is(true));
471
472        // focus on third column
473        sendKeys(KeyEvent.KEYCODE_DPAD_RIGHT);
474        Thread.sleep(TRANSITION_LENGTH);
475        assertThat("The third column of TimePicker should hold focus after scrolling right",
476                mTimePickerInnerView.getChildAt(4).hasFocus(), is(true));
477
478        sendKeys(KeyEvent.KEYCODE_DPAD_UP);
479        Thread.sleep(TRANSITION_LENGTH);
480        assertThat("The third column of TimePicker should still hold focus after scrolling down",
481                mTimePickerInnerView.getChildAt(4).hasFocus(), is(true));
482    }
483
484    private void sendKeys(int ...keys) {
485        for (int i = 0; i < keys.length; i++) {
486            InstrumentationRegistry.getInstrumentation().sendKeyDownUpSync(keys[i]);
487        }
488    }
489}
490