GuidedDatePickerTest.java revision fad2335f169d36b7b6f2c0ec8ddfe6c0094c2072
1/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 * in compliance with the License. You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software distributed under the License
10 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 * or implied. See the License for the specific language governing permissions and limitations under
12 * the License.
13 */
14
15package android.support.v17.leanback.app.wizard;
16
17import android.app.Instrumentation;
18import android.content.Intent;
19import android.content.res.Resources;
20import android.support.v17.leanback.app.GuidedStepFragment;
21import android.support.v17.leanback.test.R;
22import android.support.v17.leanback.widget.GuidanceStylist;
23import android.support.v17.leanback.widget.GuidedAction;
24import android.support.v17.leanback.widget.GuidedDatePickerAction;
25import android.support.v17.leanback.widget.VerticalGridView;
26import android.support.v17.leanback.widget.picker.DatePicker;
27import android.test.ActivityInstrumentationTestCase2;
28import android.test.suitebuilder.annotation.MediumTest;
29import android.util.Log;
30import android.view.KeyEvent;
31import android.view.View;
32import android.view.ViewGroup;
33import android.widget.LinearLayout;
34
35import java.text.SimpleDateFormat;
36import java.util.ArrayList;
37import java.util.Calendar;
38import java.util.Date;
39import java.util.List;
40
41@MediumTest
42public class GuidedDatePickerTest extends
43        ActivityInstrumentationTestCase2<GuidedStepAttributesTestActivity> {
44
45    static final long TRANSITION_LENGTH = 1000;
46    static long VERTICAL_SCROLL_WAIT = 500;
47    static long HORIZONTAL_SCROLL_WAIT = 500;
48    static final long FINAL_WAIT = 3000;
49
50    static final String TAG = "GuidedDatePickerTest";
51
52    private static final int DAY_INDEX = 0;
53    private static final int MONTH_INDEX = 1;
54    private static final int YEAR_INDEX = 2;
55    Instrumentation mInstrumentation;
56    GuidedStepAttributesTestActivity mActivity;
57
58    public GuidedDatePickerTest() {
59        super(GuidedStepAttributesTestActivity.class);
60    }
61
62    private void initActivity(Intent intent) {
63
64        setActivityIntent(intent);
65        mActivity = getActivity();
66        try {
67            Thread.sleep(2000);
68        } catch(InterruptedException e) {
69            e.printStackTrace();
70        }
71    }
72
73    private void scrollOnField(int field, int[] columnIndices, DatePicker mPickerView,
74                               int SCROLL_DIR) throws Throwable {
75
76        final GuidedStepFragment mFragment = (GuidedStepFragment)
77                mActivity.getGuidedStepTestFragment();
78
79        int mColDayIndex = columnIndices[0];
80        int mColMonthIndex = columnIndices[1];
81        int mColYearIndex = columnIndices[2];
82        int columnIndex = -1;
83        switch (field) {
84            case Calendar.DAY_OF_MONTH:
85                columnIndex = mColDayIndex;
86                break;
87            case Calendar.MONTH:
88                columnIndex = mColMonthIndex;
89                break;
90            case Calendar.YEAR:
91                columnIndex = mColYearIndex;
92        }
93
94
95        LinearLayout columnsLayout = (LinearLayout) mPickerView.getChildAt(0);
96
97        int focusedFieldPos = columnsLayout.indexOfChild(columnsLayout.getFocusedChild());
98        if (focusedFieldPos == -1) {
99            sendKeys(KeyEvent.KEYCODE_DPAD_CENTER);
100            Thread.sleep(TRANSITION_LENGTH);
101        }
102        focusedFieldPos = columnsLayout.indexOfChild(columnsLayout.getFocusedChild());
103        assertTrue("Date field could not be focused!", (focusedFieldPos != -1));
104
105        // following is to skip the separator fields "/" which are unfocusable but counted as
106        // children of columnsLayout
107        switch (focusedFieldPos) {
108            case 0:
109                focusedFieldPos = 0;
110                break;
111            case 2:
112                focusedFieldPos = 1;
113                break;
114            case 4:
115                focusedFieldPos = 2;
116        }
117
118        // now scroll right or left to the corresponding date field as indicated by the input field
119        int horizontalScrollOffset = columnIndex - focusedFieldPos;
120
121        int horizontalScrollDir = KeyEvent.KEYCODE_DPAD_RIGHT;
122        if (horizontalScrollOffset < 0) {
123            horizontalScrollOffset = -horizontalScrollOffset;
124            horizontalScrollDir = KeyEvent.KEYCODE_DPAD_LEFT;
125        }
126        for(int i = 0; i < horizontalScrollOffset; i++) {
127            sendKeys(horizontalScrollDir);
128            Thread.sleep(HORIZONTAL_SCROLL_WAIT);
129        }
130
131
132        Calendar currentActionCal = Calendar.getInstance();
133        currentActionCal.setTimeInMillis(mPickerView.getDate());
134
135        Calendar minCal = Calendar.getInstance();
136        minCal.setTimeInMillis(mPickerView.getMinDate());
137
138        Calendar maxCal = Calendar.getInstance();
139        maxCal.setTimeInMillis(mPickerView.getMaxDate());
140
141
142        int prevColumnVal = -1;
143        int currentColumnVal = mPickerView.getColumnAt(columnIndex).getCurrentValue();
144        while( currentColumnVal != prevColumnVal ){
145            assertTrue(getActivity().getString(R.string.datepicker_test_wrong_day_value),
146                    mPickerView.getColumnAt(mColDayIndex).getCurrentValue() ==
147                            currentActionCal.get(Calendar.DAY_OF_MONTH)
148            );
149            assertTrue(getActivity().getString(R.string.datepicker_test_wrong_month_value),
150                    mPickerView.getColumnAt(mColMonthIndex).getCurrentValue() ==
151                            currentActionCal.get(Calendar.MONTH)
152            );
153            assertTrue(getActivity().getString(R.string.datepicker_test_wrong_year_value),
154                    mPickerView.getColumnAt(mColYearIndex).getCurrentValue() ==
155                            currentActionCal.get(Calendar.YEAR)
156            );
157
158            int offset = SCROLL_DIR == KeyEvent.KEYCODE_DPAD_DOWN ? 1 : -1;
159            addDate(currentActionCal, field, offset, minCal, maxCal);
160
161            sendKeys(SCROLL_DIR);
162            Thread.sleep(VERTICAL_SCROLL_WAIT);
163
164            prevColumnVal = currentColumnVal;
165            currentColumnVal = mPickerView.getColumnAt(columnIndex).getCurrentValue();
166        }
167    }
168
169    private void addDate(Calendar mCurrentDate, int field, int offset,
170                         Calendar mMinDate, Calendar mMaxDate) {
171        int maxOffset = -1;
172        int actualMinFieldValue, actualMaxFieldValue;
173
174        if ( field == Calendar.YEAR ) {
175            actualMinFieldValue = mMinDate.get(Calendar.YEAR);
176            actualMaxFieldValue = mMaxDate.get(Calendar.YEAR);
177        } else {
178            actualMinFieldValue = mCurrentDate.getActualMinimum(field);
179            actualMaxFieldValue = mCurrentDate.getActualMaximum(field);
180        }
181
182        if ( offset > 0 ) {
183            maxOffset = Math.min(
184                    actualMaxFieldValue - mCurrentDate.get(field), offset);
185            mCurrentDate.add(field, maxOffset);
186            if (mCurrentDate.after(mMaxDate)) {
187                mCurrentDate.setTimeInMillis(mMaxDate.getTimeInMillis());
188            }
189        } else {
190            maxOffset = Math.max(
191                    actualMinFieldValue - mCurrentDate.get(field), offset);
192            mCurrentDate.add(field, Math.max(offset, maxOffset));
193            if (mCurrentDate.before(mMinDate)) {
194                mCurrentDate.setTimeInMillis(mMinDate.getTimeInMillis());
195            }
196        }
197    }
198
199    public void testDifferentMonthLengths() throws Throwable {
200
201        mInstrumentation = getInstrumentation();
202        Intent intent = new Intent(mInstrumentation.getContext(),
203                GuidedStepAttributesTestActivity.class);
204        Resources res = mInstrumentation.getContext().getResources();
205
206        final int NUM_DATE_ACTIONS = 1;
207
208        String title = "Date Picker Transition Test";
209        String breadcrumb = "Month Transition Test Demo";
210        String description = "Testing the transition between longer to shorter months";
211        GuidanceStylist.Guidance guidance = new GuidanceStylist.Guidance(title, description,
212                breadcrumb, null);
213
214        List<GuidedAction> actionList = new ArrayList<>();
215
216        Calendar cal = Calendar.getInstance();
217
218        cal.set(Calendar.YEAR, 2016);
219        cal.set(Calendar.MONTH, Calendar.JANUARY);
220        cal.set(Calendar.DAY_OF_MONTH, 30);
221        Date initialDate = cal.getTime();
222
223        GuidedDatePickerAction action = new GuidedDatePickerAction.Builder(
224                mInstrumentation.getContext())
225                .id(0)
226                .title("Date")
227                .date(initialDate.getTime())
228                .datePickerFormat("DMY")
229                .build();
230
231        actionList.add(action);
232
233        GuidedStepAttributesTestFragment.clear();
234        GuidedStepAttributesTestFragment.GUIDANCE = guidance;
235        GuidedStepAttributesTestFragment.ACTION_LIST = actionList;
236
237        initActivity(intent);
238
239        DatePicker mPickerView = (DatePicker) mActivity.findViewById(
240                R.id.guidedactions_activator_item);
241
242        final GuidedStepFragment mFragment = (GuidedStepFragment) mActivity.
243                getGuidedStepTestFragment();
244        traverseMonths(mPickerView, (GuidedDatePickerAction) actionList.get(0));
245        Thread.sleep(FINAL_WAIT);
246    }
247
248    private void traverseMonths(DatePicker mPickerView, GuidedDatePickerAction dateAction)
249            throws Throwable{
250
251        final GuidedStepFragment mFragment = (GuidedStepFragment)
252                mActivity.getGuidedStepTestFragment();
253
254        Calendar currentActionCal = Calendar.getInstance();
255        currentActionCal.setTimeInMillis(dateAction.getDate());
256
257        sendKeys(KeyEvent.KEYCODE_DPAD_CENTER);
258        Thread.sleep(TRANSITION_LENGTH);
259
260        int prevMonth = -1;
261        int currentMonth = mPickerView.getColumnAt(MONTH_INDEX).getCurrentValue();
262        while (currentMonth != prevMonth) {
263            int prevDayOfMonth = -1;
264            int currentDayOfMonth = mPickerView.getColumnAt(DAY_INDEX).getCurrentValue();
265            // scroll down the days till reaching the last day of month
266            while (currentDayOfMonth != prevDayOfMonth) {
267                sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
268                Thread.sleep(VERTICAL_SCROLL_WAIT);
269                prevDayOfMonth = currentDayOfMonth;
270                currentDayOfMonth = mPickerView.getColumnAt(DAY_INDEX).getCurrentValue();
271            }
272            int oldDayValue = mPickerView.getColumnAt(DAY_INDEX).getCurrentValue();
273            int oldMonthValue = mPickerView.getColumnAt(MONTH_INDEX).getCurrentValue();
274            // increment the month
275            sendKeys(KeyEvent.KEYCODE_DPAD_RIGHT);
276            Thread.sleep(VERTICAL_SCROLL_WAIT);
277
278            sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
279            Thread.sleep(TRANSITION_LENGTH);
280
281            int newDayValue = mPickerView.getColumnAt(DAY_INDEX).getCurrentValue();
282            int newMonthValue = mPickerView.getColumnAt(MONTH_INDEX).getCurrentValue();
283            verifyMonthTransition(currentActionCal,
284                    oldDayValue, oldMonthValue, newDayValue, newMonthValue);
285
286            sendKeys(KeyEvent.KEYCODE_DPAD_LEFT);
287            Thread.sleep(TRANSITION_LENGTH);
288            prevMonth = currentMonth;
289            currentMonth = newMonthValue;
290        }
291
292    }
293
294    private void verifyMonthTransition(Calendar currentCal, int oldDayValue, int oldMonthValue,
295                                       int newDayValue, int newMonthValue) {
296
297        if (oldMonthValue == newMonthValue)
298            return;
299
300        currentCal.set(Calendar.DAY_OF_MONTH, 1);
301        currentCal.set(Calendar.MONTH, oldMonthValue);
302        int expectedOldDayValue = currentCal.getActualMaximum(Calendar.DAY_OF_MONTH);
303        currentCal.set(Calendar.MONTH, newMonthValue);
304        int numDaysInNewMonth = currentCal.getActualMaximum(Calendar.DAY_OF_MONTH);
305        int expectedNewDayValue = (expectedOldDayValue <= numDaysInNewMonth) ?
306                expectedOldDayValue : numDaysInNewMonth;
307
308        assertTrue(getActivity().getString(
309                R.string.datepicker_test_transition_error1, oldMonthValue),
310                oldDayValue == expectedOldDayValue
311        );
312        assertTrue(getActivity().getString(
313                R.string.datepicker_test_transition_error2, newDayValue, newMonthValue),
314                newDayValue == expectedNewDayValue
315        );
316    }
317
318    public void testDateRanges() throws Throwable {
319
320        mInstrumentation = getInstrumentation();
321        Intent intent = new Intent(mInstrumentation.getContext(),
322                GuidedStepAttributesTestActivity.class);
323        Resources res = mInstrumentation.getContext().getResources();
324
325        SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy");
326
327        Calendar currCal = Calendar.getInstance();
328        currCal.set(Calendar.YEAR, 2016);
329        currCal.set(Calendar.MONTH, Calendar.JULY);
330        currCal.set(Calendar.DAY_OF_MONTH, 15);
331
332        Calendar minCal = Calendar.getInstance();
333        minCal.set(Calendar.YEAR, 2014);
334        minCal.set(Calendar.MONTH, Calendar.OCTOBER);
335        minCal.set(Calendar.DAY_OF_MONTH, 20);
336
337        Calendar maxCal = Calendar.getInstance();
338        maxCal.set(Calendar.YEAR, 2018);
339        maxCal.set(Calendar.MONTH, Calendar.FEBRUARY);
340        maxCal.set(Calendar.DAY_OF_MONTH, 10);
341
342        String title = "Date Picker Range Test";
343        String breadcrumb = "Date Picker Range Test Demo";
344        String description = "";
345        GuidanceStylist.Guidance guidance = new GuidanceStylist.Guidance(title, description,
346                breadcrumb, null);
347
348        List<GuidedAction> actionList = new ArrayList<>();
349
350        // testing different date formats and the correctness of range changes as we scroll
351        GuidedDatePickerAction dateAction1 = new GuidedDatePickerAction.Builder(
352                mInstrumentation.getContext())
353                .id(0)
354                .title(res.getString(R.string.datepicker_with_range_title,
355                        dateFormat.format(minCal.getTime()),
356                        dateFormat.format(maxCal.getTime())))
357                .multilineDescription(true)
358                .date(currCal.getTimeInMillis())
359                .datePickerFormat("MDY")
360                .minDate(minCal.getTimeInMillis())
361                .maxDate(maxCal.getTimeInMillis())
362                .build();
363
364        GuidedDatePickerAction dateAction2 = new GuidedDatePickerAction.Builder(
365                mInstrumentation.getContext())
366                .id(1)
367                .title(res.getString(R.string.datepicker_with_range_title,
368                        dateFormat.format(minCal.getTimeInMillis()),
369                        dateFormat.format(maxCal.getTimeInMillis())))
370                .multilineDescription(true)
371                .date(currCal.getTimeInMillis())
372                .datePickerFormat("DMY")
373                .minDate(minCal.getTimeInMillis())
374                .maxDate(maxCal.getTimeInMillis())
375                .build();
376
377        // testing date ranges when Year is equal
378        minCal.set(Calendar.YEAR, maxCal.get(Calendar.YEAR));
379        int minMonth = Math.min(minCal.get(Calendar.MONTH), maxCal.get(Calendar.MONTH));
380        int maxMonth = Math.max(minCal.get(Calendar.MONTH), maxCal.get(Calendar.MONTH));
381        minCal.set(Calendar.MONTH, minMonth);
382        maxCal.set(Calendar.MONTH, maxMonth);
383
384        GuidedDatePickerAction dateAction3 = new GuidedDatePickerAction.Builder(
385                mInstrumentation.getContext())
386                .id(2)
387                .title(res.getString(R.string.datepicker_with_range_title,
388                        dateFormat.format(minCal.getTimeInMillis()),
389                        dateFormat.format(maxCal.getTimeInMillis())))
390                .multilineDescription(true)
391                .date(currCal.getTimeInMillis())
392                .datePickerFormat("DMY")
393                .minDate(minCal.getTimeInMillis())
394                .maxDate(maxCal.getTimeInMillis())
395                .build();
396
397
398        // testing date ranges when both Month and Year are equal
399        minCal.set(Calendar.MONTH, maxCal.get(Calendar.MONTH));
400        int minDay = Math.min(minCal.get(Calendar.DAY_OF_MONTH), maxCal.get(Calendar.DAY_OF_MONTH));
401        int maxDay = Math.max(minCal.get(Calendar.DAY_OF_MONTH), maxCal.get(Calendar.DAY_OF_MONTH));
402        minCal.set(Calendar.DAY_OF_MONTH, minDay);
403        maxCal.set(Calendar.DAY_OF_MONTH, maxDay);
404
405        GuidedDatePickerAction dateAction4 = new GuidedDatePickerAction.Builder(
406                mInstrumentation.getContext())
407                .id(3)
408                .title(res.getString(R.string.datepicker_with_range_title,
409                        dateFormat.format(minCal.getTimeInMillis()),
410                        dateFormat.format(maxCal.getTimeInMillis())))
411                .multilineDescription(true)
412                .date(currCal.getTimeInMillis())
413                .datePickerFormat("DMY")
414                .minDate(minCal.getTimeInMillis())
415                .maxDate(maxCal.getTimeInMillis())
416                .build();
417
418
419        // testing date ranges when all fields are equal
420        minCal.set(Calendar.DAY_OF_MONTH, maxCal.get(Calendar.DAY_OF_MONTH));
421
422        GuidedDatePickerAction dateAction5 = new GuidedDatePickerAction.Builder(
423                mInstrumentation.getContext())
424                .id(4)
425                .title(res.getString(R.string.datepicker_with_range_title,
426                        dateFormat.format(minCal.getTimeInMillis()),
427                        dateFormat.format(maxCal.getTimeInMillis())))
428                .multilineDescription(true)
429                .date(currCal.getTimeInMillis())
430                .datePickerFormat("DMY")
431                .minDate(minCal.getTimeInMillis())
432                .maxDate(maxCal.getTimeInMillis())
433                .build();
434
435        actionList.add(dateAction1);
436        actionList.add(dateAction2);
437        actionList.add(dateAction3);
438        actionList.add(dateAction4);
439        actionList.add(dateAction5);
440
441        GuidedStepAttributesTestFragment.clear();
442        GuidedStepAttributesTestFragment.GUIDANCE = guidance;
443        GuidedStepAttributesTestFragment.ACTION_LIST = actionList;
444
445        initActivity(intent);
446
447        final GuidedStepFragment mFragment = (GuidedStepFragment) mActivity.
448                getGuidedStepTestFragment();
449
450        scrollToMinAndMaxDates(new int[] {1, 0, 2}, dateAction1);
451        scrollToMinAndMaxDates(new int[] {0, 1, 2}, dateAction2);
452        scrollToMinAndMaxDates(new int[] {0, 1, 2}, dateAction3);
453        scrollToMinAndMaxDates(new int[] {0, 1, 2}, dateAction4);
454        scrollToMinAndMaxDates(new int[] {0, 1, 2}, dateAction5);
455
456        Thread.sleep(FINAL_WAIT);
457    }
458
459    private void scrollToMinAndMaxDates(int[] columnIndices, GuidedDatePickerAction dateAction)
460            throws Throwable{
461
462        final GuidedStepFragment mFragment = (GuidedStepFragment)
463                mActivity.getGuidedStepTestFragment();
464
465        VerticalGridView guidedActionsList = (VerticalGridView)
466                mActivity.findViewById(R.id.guidedactions_list);
467
468        int currSelectedAction = mFragment.getSelectedActionPosition();
469        // scroll up/down to the requested action
470        long verticalScrollOffset = dateAction.getId() - currSelectedAction;
471
472        int verticalScrollDir = KeyEvent.KEYCODE_DPAD_DOWN;
473        if (verticalScrollOffset < 0) {
474            verticalScrollOffset= -verticalScrollOffset;
475            verticalScrollDir = KeyEvent.KEYCODE_DPAD_UP;
476        }
477        for(int i = 0; i < verticalScrollOffset; i++) {
478            sendKeys(verticalScrollDir);
479            Thread.sleep(TRANSITION_LENGTH);
480        }
481
482        assertTrue("The wrong action was selected!", mFragment.getSelectedActionPosition() ==
483                dateAction.getId());
484        DatePicker mPickerView = (DatePicker) mFragment.getActionItemView((int) dateAction.getId())
485                .findViewById(R.id.guidedactions_activator_item);
486
487        Calendar currentActionCal = Calendar.getInstance();
488        currentActionCal.setTimeInMillis(dateAction.getDate());
489
490
491        // scrolling to the minimum date
492
493        scrollOnField(Calendar.YEAR, columnIndices, mPickerView, KeyEvent.KEYCODE_DPAD_UP);
494        dateAction.setDate(mPickerView.getDate());
495
496        scrollOnField(Calendar.MONTH, columnIndices, mPickerView, KeyEvent.KEYCODE_DPAD_UP);
497        dateAction.setDate(mPickerView.getDate());
498
499        scrollOnField(Calendar.DAY_OF_MONTH, columnIndices, mPickerView, KeyEvent.KEYCODE_DPAD_UP);
500        dateAction.setDate(mPickerView.getDate());
501
502        Thread.sleep(VERTICAL_SCROLL_WAIT);
503
504        // now scrolling to the maximum date
505
506        scrollOnField(Calendar.YEAR, columnIndices, mPickerView, KeyEvent.KEYCODE_DPAD_DOWN);
507        dateAction.setDate(mPickerView.getDate());
508
509        scrollOnField(Calendar.MONTH, columnIndices, mPickerView, KeyEvent.KEYCODE_DPAD_DOWN);
510        dateAction.setDate(mPickerView.getDate());
511
512        scrollOnField(Calendar.DAY_OF_MONTH, columnIndices, mPickerView, KeyEvent.KEYCODE_DPAD_DOWN);
513        dateAction.setDate(mPickerView.getDate());
514
515        sendKeys(KeyEvent.KEYCODE_DPAD_CENTER);
516        Thread.sleep(TRANSITION_LENGTH);
517    }
518
519}
520