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 static org.junit.Assert.assertTrue;
18
19import android.content.Context;
20import android.content.Intent;
21import android.content.res.Resources;
22import android.support.test.InstrumentationRegistry;
23import android.support.test.filters.LargeTest;
24import android.support.test.rule.ActivityTestRule;
25import android.support.test.runner.AndroidJUnit4;
26import android.support.v17.leanback.app.GuidedStepFragment;
27import android.support.v17.leanback.test.R;
28import android.support.v17.leanback.widget.GuidanceStylist;
29import android.support.v17.leanback.widget.GuidedAction;
30import android.support.v17.leanback.widget.GuidedDatePickerAction;
31import android.support.v17.leanback.widget.VerticalGridView;
32import android.support.v17.leanback.widget.picker.DatePicker;
33import android.util.Log;
34import android.view.KeyEvent;
35import android.widget.LinearLayout;
36
37import org.junit.Before;
38import org.junit.Rule;
39import org.junit.Test;
40import org.junit.runner.RunWith;
41
42import java.text.SimpleDateFormat;
43import java.util.ArrayList;
44import java.util.Calendar;
45import java.util.Date;
46import java.util.List;
47
48@LargeTest
49@RunWith(AndroidJUnit4.class)
50public class GuidedDatePickerTest {
51
52    static final long TRANSITION_LENGTH = 1000;
53    static long VERTICAL_SCROLL_WAIT = 500;
54    static long HORIZONTAL_SCROLL_WAIT = 500;
55    static final long FINAL_WAIT = 1000;
56
57    static final String TAG = "GuidedDatePickerTest";
58
59    private static final int DAY_INDEX = 0;
60    private static final int MONTH_INDEX = 1;
61    private static final int YEAR_INDEX = 2;
62
63    @Rule
64    public ActivityTestRule<GuidedStepAttributesTestActivity> activityTestRule =
65            new ActivityTestRule<>(GuidedStepAttributesTestActivity.class, false, false);
66
67    GuidedStepAttributesTestActivity mActivity;
68
69
70    private void initActivity(Intent intent) {
71        mActivity = activityTestRule.launchActivity(intent);
72        try {
73            Thread.sleep(2000);
74        } catch(InterruptedException e) {
75            e.printStackTrace();
76        }
77
78    }
79
80    Context mContext;
81    @Before
82    public void setUp() {
83        mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();;
84    }
85
86    public static void sendKey(int keyCode) {
87        InstrumentationRegistry.getInstrumentation().sendKeyDownUpSync(keyCode);
88    }
89
90    private int getColumnIndexForDateField(int field, int[] columnIndices) {
91        int mColDayIndex = columnIndices[0];
92        int mColMonthIndex = columnIndices[1];
93        int mColYearIndex = columnIndices[2];
94        int columnIndex = -1;
95        switch (field) {
96            case Calendar.DAY_OF_MONTH:
97                columnIndex = mColDayIndex;
98                break;
99            case Calendar.MONTH:
100                columnIndex = mColMonthIndex;
101                break;
102            case Calendar.YEAR:
103                columnIndex = mColYearIndex;
104        }
105        return columnIndex;
106    }
107
108    private void horizontalScrollToDateField(int field, int[] columnIndices,
109                                             DatePicker pickerView) throws Throwable{
110        int columnIndex = getColumnIndexForDateField(field, columnIndices);
111
112        LinearLayout columnsLayout = (LinearLayout) pickerView.getChildAt(0);
113
114        int focusedFieldPos = columnsLayout.indexOfChild(columnsLayout.getFocusedChild());
115        if (focusedFieldPos == -1) {
116            sendKey(KeyEvent.KEYCODE_DPAD_CENTER);
117            Thread.sleep(TRANSITION_LENGTH);
118        }
119        focusedFieldPos = columnsLayout.indexOfChild(columnsLayout.getFocusedChild());
120        assertTrue("Date field could not be focused!", (focusedFieldPos != -1));
121
122        // following is to skip the separator fields "/" which are unfocusable but counted as
123        // children of columnsLayout
124        switch (focusedFieldPos) {
125            case 0:
126                focusedFieldPos = 0;
127                break;
128            case 2:
129                focusedFieldPos = 1;
130                break;
131            case 4:
132                focusedFieldPos = 2;
133        }
134
135        // now scroll right or left to the corresponding date field as indicated by the input field
136        int horizontalScrollOffset = columnIndex - focusedFieldPos;
137
138        int horizontalScrollDir = KeyEvent.KEYCODE_DPAD_RIGHT;
139        if (horizontalScrollOffset < 0) {
140            horizontalScrollOffset = -horizontalScrollOffset;
141            horizontalScrollDir = KeyEvent.KEYCODE_DPAD_LEFT;
142        }
143        for(int i = 0; i < horizontalScrollOffset; i++) {
144            sendKey(horizontalScrollDir);
145            Thread.sleep(HORIZONTAL_SCROLL_WAIT);
146        }
147
148    }
149
150    /**
151     * Scrolls vertically all the way up or down (depending on the provided scrollDir parameter)
152     * to fieldValue if it's not equal to -1; otherwise, the scrolling goes all the way to the end.
153     * @param field The date field over which the scrolling is performed
154     * @param fieldValue The field value to scroll to or -1 if the scrolling should go all the way.
155     * @param columnIndices The date field indices corresponding to day, month, and the year
156     * @param pickerView The DatePicker view.
157     * @param scrollDir The direction of scrolling to reach the desired field value.
158     * @throws Throwable
159     */
160    private void verticalScrollToFieldValue(int field, int fieldValue, int[] columnIndices,
161                                                 DatePicker pickerView, int scrollDir)
162            throws Throwable {
163
164        int columnIndex = getColumnIndexForDateField(field, columnIndices);
165        int colDayIndex = columnIndices[0];
166        int colMonthIndex = columnIndices[1];
167        int colYearIndex = columnIndices[2];
168
169        horizontalScrollToDateField(field, columnIndices, pickerView);
170
171        Calendar currentActionCal = Calendar.getInstance();
172        currentActionCal.setTimeInMillis(pickerView.getDate());
173
174        Calendar minCal = Calendar.getInstance();
175        minCal.setTimeInMillis(pickerView.getMinDate());
176
177        Calendar maxCal = Calendar.getInstance();
178        maxCal.setTimeInMillis(pickerView.getMaxDate());
179
180
181        int prevColumnVal = -1;
182        int currentColumnVal = pickerView.getColumnAt(columnIndex).getCurrentValue();
183        while( currentColumnVal != prevColumnVal && currentColumnVal != fieldValue){
184            assertTrue(mContext.getString(R.string.datepicker_test_wrong_day_value),
185                    pickerView.getColumnAt(colDayIndex).getCurrentValue()
186                            == currentActionCal.get(Calendar.DAY_OF_MONTH)
187            );
188            assertTrue(mContext.getString(R.string.datepicker_test_wrong_month_value),
189                    pickerView.getColumnAt(colMonthIndex).getCurrentValue()
190                            == currentActionCal.get(Calendar.MONTH)
191            );
192            assertTrue(mContext.getString(R.string.datepicker_test_wrong_year_value),
193                    pickerView.getColumnAt(colYearIndex).getCurrentValue()
194                            == currentActionCal.get(Calendar.YEAR)
195            );
196
197            int offset = scrollDir == KeyEvent.KEYCODE_DPAD_DOWN ? 1 : -1;
198            addDate(currentActionCal, field, offset, minCal, maxCal);
199
200            sendKey(scrollDir);
201            Thread.sleep(VERTICAL_SCROLL_WAIT);
202
203            prevColumnVal = currentColumnVal;
204            currentColumnVal = pickerView.getColumnAt(columnIndex).getCurrentValue();
205        }
206    }
207
208    private void addDate(Calendar mCurrentDate, int field, int offset,
209                         Calendar mMinDate, Calendar mMaxDate) {
210        int maxOffset = -1;
211        int actualMinFieldValue, actualMaxFieldValue;
212
213        if ( field == Calendar.YEAR ) {
214            actualMinFieldValue = mMinDate.get(Calendar.YEAR);
215            actualMaxFieldValue = mMaxDate.get(Calendar.YEAR);
216        } else {
217            actualMinFieldValue = mCurrentDate.getActualMinimum(field);
218            actualMaxFieldValue = mCurrentDate.getActualMaximum(field);
219        }
220
221        if ( offset > 0 ) {
222            maxOffset = Math.min(
223                    actualMaxFieldValue - mCurrentDate.get(field), offset);
224            mCurrentDate.add(field, maxOffset);
225            if (mCurrentDate.after(mMaxDate)) {
226                mCurrentDate.setTimeInMillis(mMaxDate.getTimeInMillis());
227            }
228        } else {
229            maxOffset = Math.max(
230                    actualMinFieldValue - mCurrentDate.get(field), offset);
231            mCurrentDate.add(field, Math.max(offset, maxOffset));
232            if (mCurrentDate.before(mMinDate)) {
233                mCurrentDate.setTimeInMillis(mMinDate.getTimeInMillis());
234            }
235        }
236    }
237
238    @Test
239    public void testJanuaryToFebruaryTransitionForLeapYear() throws Throwable {
240        long startTime = System.currentTimeMillis();
241        Intent intent = new Intent();
242
243        String title = "Date Picker Transition Test";
244        String breadcrumb = "Month Transition Test Demo";
245        String description = "Testing the transition from Jan to Feb (leap year)";
246        GuidanceStylist.Guidance guidance = new GuidanceStylist.Guidance(title, description,
247                breadcrumb, null);
248
249        List<GuidedAction> actionList = new ArrayList<>();
250
251        Calendar cal = Calendar.getInstance();
252
253        cal.set(Calendar.YEAR, 2016);   // 2016 is a leap year
254        cal.set(Calendar.MONTH, Calendar.JANUARY);
255        cal.set(Calendar.DAY_OF_MONTH, cal.getActualMaximum(Calendar.DAY_OF_MONTH));
256        Date initialDate = cal.getTime();
257
258        GuidedDatePickerAction action = new GuidedDatePickerAction.Builder(
259                mContext)
260                .id(0)
261                .title("Date")
262                .date(initialDate.getTime())
263                .datePickerFormat("DMY")
264                .build();
265
266        actionList.add(action);
267
268        GuidedStepAttributesTestFragment.clear();
269        GuidedStepAttributesTestFragment.GUIDANCE = guidance;
270        GuidedStepAttributesTestFragment.ACTION_LIST = actionList;
271
272        initActivity(intent);
273
274        DatePicker mPickerView = (DatePicker) mActivity.findViewById(
275                R.id.guidedactions_activator_item);
276
277        verticalScrollToFieldValue(Calendar.MONTH, Calendar.FEBRUARY, new int[] {0, 1, 2},
278                mPickerView, KeyEvent.KEYCODE_DPAD_DOWN);
279        long executionTime = System.currentTimeMillis() - startTime;
280        Log.d(TAG, "testJanuaryToFebruaryTransitionForLeapYear() Execution time: " + executionTime);
281        Thread.sleep(FINAL_WAIT);
282    }
283
284    @Test
285    public void testFebruaryToMarchTransitionForLeapYear() throws Throwable {
286        long startTime = System.currentTimeMillis();
287        Intent intent = new Intent();
288
289        String title = "Date Picker Transition Test";
290        String breadcrumb = "Month Transition Test Demo";
291        String description = "Testing the transition from Feb to Mar (leap year)";
292        GuidanceStylist.Guidance guidance = new GuidanceStylist.Guidance(title, description,
293                breadcrumb, null);
294
295        List<GuidedAction> actionList = new ArrayList<>();
296
297        Calendar cal = Calendar.getInstance();
298
299        cal.set(Calendar.YEAR, 2016);
300        cal.set(Calendar.MONTH, Calendar.FEBRUARY);
301        cal.set(Calendar.DAY_OF_MONTH, cal.getActualMaximum(Calendar.DAY_OF_MONTH));
302        Date initialDate = cal.getTime();
303
304        GuidedDatePickerAction action = new GuidedDatePickerAction.Builder(
305                mContext)
306                .id(0)
307                .title("Date")
308                .date(initialDate.getTime())
309                .datePickerFormat("DMY")
310                .build();
311
312        actionList.add(action);
313
314        GuidedStepAttributesTestFragment.clear();
315        GuidedStepAttributesTestFragment.GUIDANCE = guidance;
316        GuidedStepAttributesTestFragment.ACTION_LIST = actionList;
317
318        initActivity(intent);
319
320        DatePicker mPickerView = (DatePicker) mActivity.findViewById(
321                R.id.guidedactions_activator_item);
322
323        verticalScrollToFieldValue(Calendar.MONTH, Calendar.MARCH, new int[] {0, 1, 2},
324                mPickerView, KeyEvent.KEYCODE_DPAD_DOWN);
325        long executionTime = System.currentTimeMillis() - startTime;
326        Log.d(TAG, "testFebruaryToMarchTransition() Execution time: " + executionTime);
327        Thread.sleep(FINAL_WAIT);
328    }
329
330    @Test
331    public void testJanuaryToFebruaryTransitionForNonLeapYear() throws Throwable {
332        long startTime = System.currentTimeMillis();
333        Intent intent = new Intent();
334
335        String title = "Date Picker Transition Test";
336        String breadcrumb = "Month Transition Test Demo";
337        String description = "Testing the transition from Jan to Feb (nonleap year)";
338        GuidanceStylist.Guidance guidance = new GuidanceStylist.Guidance(title, description,
339                breadcrumb, null);
340
341        List<GuidedAction> actionList = new ArrayList<>();
342
343        Calendar cal = Calendar.getInstance();
344
345        cal.set(Calendar.YEAR, 2017);   // 2017 is a leap year
346        cal.set(Calendar.MONTH, Calendar.JANUARY);
347        cal.set(Calendar.DAY_OF_MONTH, cal.getActualMaximum(Calendar.DAY_OF_MONTH));
348        Date initialDate = cal.getTime();
349
350        GuidedDatePickerAction action = new GuidedDatePickerAction.Builder(
351                mContext)
352                .id(0)
353                .title("Date")
354                .date(initialDate.getTime())
355                .datePickerFormat("DMY")
356                .build();
357
358        actionList.add(action);
359
360        GuidedStepAttributesTestFragment.clear();
361        GuidedStepAttributesTestFragment.GUIDANCE = guidance;
362        GuidedStepAttributesTestFragment.ACTION_LIST = actionList;
363
364        initActivity(intent);
365
366        DatePicker mPickerView = (DatePicker) mActivity.findViewById(
367                R.id.guidedactions_activator_item);
368
369        verticalScrollToFieldValue(Calendar.MONTH, Calendar.FEBRUARY, new int[] {0, 1, 2},
370                mPickerView, KeyEvent.KEYCODE_DPAD_DOWN);
371        long executionTime = System.currentTimeMillis() - startTime;
372        Log.d(TAG, "testJanuaryToFebruaryTransition() Execution time: " + executionTime);
373        Thread.sleep(FINAL_WAIT);
374    }
375
376    @Test
377    public void testFebruaryToMarchTransitionForNonLeapYear() throws Throwable {
378        long startTime = System.currentTimeMillis();
379        Intent intent = new Intent();
380
381        String title = "Date Picker Transition Test";
382        String breadcrumb = "Month Transition Test Demo";
383        String description = "Testing the transition from Feb to Mar (nonleap year)";
384        GuidanceStylist.Guidance guidance = new GuidanceStylist.Guidance(title, description,
385                breadcrumb, null);
386
387        List<GuidedAction> actionList = new ArrayList<>();
388
389        Calendar cal = Calendar.getInstance();
390
391        cal.set(Calendar.YEAR, 2017);
392        cal.set(Calendar.MONTH, Calendar.FEBRUARY);
393        cal.set(Calendar.DAY_OF_MONTH, cal.getActualMaximum(Calendar.DAY_OF_MONTH));
394        Date initialDate = cal.getTime();
395
396        GuidedDatePickerAction action = new GuidedDatePickerAction.Builder(
397                mContext)
398                .id(0)
399                .title("Date")
400                .date(initialDate.getTime())
401                .datePickerFormat("DMY")
402                .build();
403
404        actionList.add(action);
405
406        GuidedStepAttributesTestFragment.clear();
407        GuidedStepAttributesTestFragment.GUIDANCE = guidance;
408        GuidedStepAttributesTestFragment.ACTION_LIST = actionList;
409
410        initActivity(intent);
411
412        DatePicker mPickerView = (DatePicker) mActivity.findViewById(
413                R.id.guidedactions_activator_item);
414
415        verticalScrollToFieldValue(Calendar.MONTH, Calendar.MARCH, new int[] {0, 1, 2},
416                mPickerView, KeyEvent.KEYCODE_DPAD_DOWN);
417        long executionTime = System.currentTimeMillis() - startTime;
418        Log.d(TAG, "testFebruaryToMarchTransition() Execution time: " + executionTime);
419        Thread.sleep(FINAL_WAIT);
420    }
421
422    @Test
423    public void testDecemberToNovemberTransition() throws Throwable {
424        long startTime = System.currentTimeMillis();
425        Intent intent = new Intent();
426
427        String title = "Date Picker Transition Test";
428        String breadcrumb = "Month Transition Test Demo";
429        String description = "Testing the transition from Dec to Nov";
430        GuidanceStylist.Guidance guidance = new GuidanceStylist.Guidance(title, description,
431                breadcrumb, null);
432
433        List<GuidedAction> actionList = new ArrayList<>();
434
435        Calendar cal = Calendar.getInstance();
436
437        cal.set(Calendar.YEAR, 2016);
438        cal.set(Calendar.MONTH, Calendar.DECEMBER);
439        cal.set(Calendar.DAY_OF_MONTH, cal.getActualMaximum(Calendar.DAY_OF_MONTH));
440        Date initialDate = cal.getTime();
441
442        GuidedDatePickerAction action = new GuidedDatePickerAction.Builder(
443                mContext)
444                .id(0)
445                .title("Date")
446                .date(initialDate.getTime())
447                .datePickerFormat("DMY")
448                .build();
449
450        actionList.add(action);
451
452        GuidedStepAttributesTestFragment.clear();
453        GuidedStepAttributesTestFragment.GUIDANCE = guidance;
454        GuidedStepAttributesTestFragment.ACTION_LIST = actionList;
455
456        initActivity(intent);
457
458        DatePicker mPickerView = (DatePicker) mActivity.findViewById(
459                R.id.guidedactions_activator_item);
460
461        verticalScrollToFieldValue(Calendar.MONTH, Calendar.NOVEMBER, new int[] {0, 1, 2},
462                mPickerView, KeyEvent.KEYCODE_DPAD_UP);
463        long executionTime = System.currentTimeMillis() - startTime;
464        Log.d(TAG, "testDecemberToNovember() Execution time: " + executionTime);
465        Thread.sleep(FINAL_WAIT);
466    }
467
468    @Test
469    public void testNovemberToOctoberTransition() throws Throwable {
470        long startTime = System.currentTimeMillis();
471        Intent intent = new Intent();
472
473        String title = "Date Picker Transition Test";
474        String breadcrumb = "Month Transition Test Demo";
475        String description = "Testing the transition from Nov to Oct";
476        GuidanceStylist.Guidance guidance = new GuidanceStylist.Guidance(title, description,
477                breadcrumb, null);
478
479        List<GuidedAction> actionList = new ArrayList<>();
480
481        Calendar cal = Calendar.getInstance();
482
483        cal.set(Calendar.YEAR, 2016);
484        cal.set(Calendar.MONTH, Calendar.NOVEMBER);
485        cal.set(Calendar.DAY_OF_MONTH, cal.getActualMaximum(Calendar.DAY_OF_MONTH));
486        Date initialDate = cal.getTime();
487
488        GuidedDatePickerAction action = new GuidedDatePickerAction.Builder(
489                mContext)
490                .id(0)
491                .title("Date")
492                .date(initialDate.getTime())
493                .datePickerFormat("DMY")
494                .build();
495
496        actionList.add(action);
497
498        GuidedStepAttributesTestFragment.clear();
499        GuidedStepAttributesTestFragment.GUIDANCE = guidance;
500        GuidedStepAttributesTestFragment.ACTION_LIST = actionList;
501
502        initActivity(intent);
503
504        DatePicker mPickerView = (DatePicker) mActivity.findViewById(
505                R.id.guidedactions_activator_item);
506
507        verticalScrollToFieldValue(Calendar.MONTH, Calendar.OCTOBER, new int[] {0, 1, 2},
508                mPickerView, KeyEvent.KEYCODE_DPAD_UP);
509        long executionTime = System.currentTimeMillis() - startTime;
510        Log.d(TAG, "testNovemberToOctober() Execution time: " + executionTime);
511        Thread.sleep(FINAL_WAIT);
512    }
513
514    @Test
515    public void testLeapToNonLeapYearTransition() throws Throwable {
516        long startTime = System.currentTimeMillis();
517        Intent intent = new Intent();
518
519        String title = "Date Picker Transition Test";
520        String breadcrumb = "Leap Year Transition Test Demo";
521        String description = "Testing Feb transition from leap to nonlneap year";
522        GuidanceStylist.Guidance guidance = new GuidanceStylist.Guidance(title, description,
523                breadcrumb, null);
524
525        List<GuidedAction> actionList = new ArrayList<>();
526
527        Calendar cal = Calendar.getInstance();
528
529        cal.set(Calendar.YEAR, 2016);   // 2016 is a leap year
530        cal.set(Calendar.MONTH, Calendar.FEBRUARY);
531        cal.set(Calendar.DAY_OF_MONTH, cal.getActualMaximum(Calendar.DAY_OF_MONTH));
532        Date initialDate = cal.getTime();
533
534        GuidedDatePickerAction action = new GuidedDatePickerAction.Builder(
535                mContext)
536                .id(0)
537                .title("Date")
538                .date(initialDate.getTime())
539                .datePickerFormat("DMY")
540                .build();
541
542        actionList.add(action);
543
544        GuidedStepAttributesTestFragment.clear();
545        GuidedStepAttributesTestFragment.GUIDANCE = guidance;
546        GuidedStepAttributesTestFragment.ACTION_LIST = actionList;
547
548        initActivity(intent);
549
550        DatePicker mPickerView = (DatePicker) mActivity.findViewById(
551                R.id.guidedactions_activator_item);
552
553        verticalScrollToFieldValue(Calendar.YEAR, 2017, new int[] {0, 1, 2},
554                mPickerView, KeyEvent.KEYCODE_DPAD_DOWN);
555        long executionTime = System.currentTimeMillis() - startTime;
556        Log.d(TAG, "testLeapToNonLeapYearTransition() Execution time: " + executionTime);
557        Thread.sleep(FINAL_WAIT);
558    }
559
560    @Test
561    public void testNonLeapToLeapYearTransition() throws Throwable {
562        long startTime = System.currentTimeMillis();
563        Intent intent = new Intent();
564
565        String title = "Date Picker Transition Test";
566        String breadcrumb = "Leap Year Transition Test Demo";
567        String description = "Testing Feb transition from nonleap to leap year";
568        GuidanceStylist.Guidance guidance = new GuidanceStylist.Guidance(title, description,
569                breadcrumb, null);
570
571        List<GuidedAction> actionList = new ArrayList<>();
572
573        Calendar cal = Calendar.getInstance();
574
575        cal.set(Calendar.YEAR, 2017);   // 2017 is a non-leap year
576        cal.set(Calendar.MONTH, Calendar.FEBRUARY);
577        cal.set(Calendar.DAY_OF_MONTH, cal.getActualMaximum(Calendar.DAY_OF_MONTH));
578        Date initialDate = cal.getTime();
579
580        GuidedDatePickerAction action = new GuidedDatePickerAction.Builder(
581                mContext)
582                .id(0)
583                .title("Date")
584                .date(initialDate.getTime())
585                .datePickerFormat("DMY")
586                .build();
587
588        actionList.add(action);
589
590        GuidedStepAttributesTestFragment.clear();
591        GuidedStepAttributesTestFragment.GUIDANCE = guidance;
592        GuidedStepAttributesTestFragment.ACTION_LIST = actionList;
593
594        initActivity(intent);
595
596        DatePicker mPickerView = (DatePicker) mActivity.findViewById(
597                R.id.guidedactions_activator_item);
598
599        verticalScrollToFieldValue(Calendar.YEAR, 2016, new int[] {0, 1, 2},
600                mPickerView, KeyEvent.KEYCODE_DPAD_UP);
601        long executionTime = System.currentTimeMillis() - startTime;
602        Log.d(TAG, "testNonLeapToLeapYearTransition() Execution time: " + executionTime);
603        Thread.sleep(FINAL_WAIT);
604    }
605
606    private void traverseMonths(DatePicker mPickerView, GuidedDatePickerAction dateAction)
607            throws Throwable {
608
609        final GuidedStepFragment mFragment = (GuidedStepFragment)
610                mActivity.getGuidedStepTestFragment();
611
612        Calendar currentActionCal = Calendar.getInstance();
613        currentActionCal.setTimeInMillis(dateAction.getDate());
614
615        sendKey(KeyEvent.KEYCODE_DPAD_CENTER);
616        Thread.sleep(TRANSITION_LENGTH);
617
618        int prevMonth = -1;
619        int currentMonth = mPickerView.getColumnAt(MONTH_INDEX).getCurrentValue();
620        while (currentMonth != prevMonth) {
621            int prevDayOfMonth = -1;
622            int currentDayOfMonth = mPickerView.getColumnAt(DAY_INDEX).getCurrentValue();
623            // scroll down the days till reaching the last day of month
624            while (currentDayOfMonth != prevDayOfMonth) {
625                sendKey(KeyEvent.KEYCODE_DPAD_DOWN);
626                Thread.sleep(VERTICAL_SCROLL_WAIT);
627                prevDayOfMonth = currentDayOfMonth;
628                currentDayOfMonth = mPickerView.getColumnAt(DAY_INDEX).getCurrentValue();
629            }
630            int oldDayValue = mPickerView.getColumnAt(DAY_INDEX).getCurrentValue();
631            int oldMonthValue = mPickerView.getColumnAt(MONTH_INDEX).getCurrentValue();
632            // increment the month
633            sendKey(KeyEvent.KEYCODE_DPAD_RIGHT);
634            Thread.sleep(VERTICAL_SCROLL_WAIT);
635
636            sendKey(KeyEvent.KEYCODE_DPAD_DOWN);
637            Thread.sleep(TRANSITION_LENGTH);
638
639            int newDayValue = mPickerView.getColumnAt(DAY_INDEX).getCurrentValue();
640            int newMonthValue = mPickerView.getColumnAt(MONTH_INDEX).getCurrentValue();
641            verifyMonthTransition(currentActionCal,
642                    oldDayValue, oldMonthValue, newDayValue, newMonthValue);
643
644            sendKey(KeyEvent.KEYCODE_DPAD_LEFT);
645            Thread.sleep(TRANSITION_LENGTH);
646            prevMonth = currentMonth;
647            currentMonth = newMonthValue;
648        }
649
650    }
651
652
653    private void verifyMonthTransition(Calendar currentCal, int oldDayValue, int oldMonthValue,
654                                       int newDayValue, int newMonthValue) {
655
656        if (oldMonthValue == newMonthValue)
657            return;
658
659        currentCal.set(Calendar.DAY_OF_MONTH, 1);
660        currentCal.set(Calendar.MONTH, oldMonthValue);
661        int expectedOldDayValue = currentCal.getActualMaximum(Calendar.DAY_OF_MONTH);
662        currentCal.set(Calendar.MONTH, newMonthValue);
663        int numDaysInNewMonth = currentCal.getActualMaximum(Calendar.DAY_OF_MONTH);
664        int expectedNewDayValue = (expectedOldDayValue <= numDaysInNewMonth)
665                ? expectedOldDayValue : numDaysInNewMonth;
666
667        assertTrue(mContext.getString(
668                R.string.datepicker_test_transition_error1, oldMonthValue),
669                oldDayValue == expectedOldDayValue
670        );
671        assertTrue(mContext.getString(
672                R.string.datepicker_test_transition_error2, newDayValue, newMonthValue),
673                newDayValue == expectedNewDayValue
674        );
675    }
676
677    @Test
678    public void testDateRangesMDYFormat() throws Throwable {
679
680        long startTime = System.currentTimeMillis();
681
682        GuidedDatePickerAction[] datePickerActions = setupDateActionsForMinAndMaxRangeTests();
683
684        scrollToMinAndMaxDates(new int[] {1, 0, 2}, datePickerActions[0]);
685        long executionTime = System.currentTimeMillis() - startTime;
686        Log.d(TAG, "testDateRangesMDYFormat() Execution time: " + executionTime);
687        Thread.sleep(FINAL_WAIT);
688    }
689
690    public void testDateRangesDMYFormat() throws Throwable {
691
692        long startTime = System.currentTimeMillis();
693
694        GuidedDatePickerAction[] datePickerActions = setupDateActionsForMinAndMaxRangeTests();
695        Log.d(TAG, "setup dateactions complete!");
696        scrollToMinAndMaxDates(new int[] {0, 1, 2}, datePickerActions[1]);
697        long executionTime = System.currentTimeMillis() - startTime;
698        Log.d(TAG, "testDateRangesDMYFormat() Execution time: " + executionTime);
699        Thread.sleep(FINAL_WAIT);
700    }
701
702    @Test
703    public void testDateRangesWithYearEqual() throws Throwable {
704
705        long startTime = System.currentTimeMillis();
706
707        GuidedDatePickerAction[] datePickerActions = setupDateActionsForMinAndMaxRangeTests();
708
709        scrollToMinAndMaxDates(new int[] {0, 1, 2}, datePickerActions[2]);
710        long executionTime = System.currentTimeMillis() - startTime;
711        Log.d(TAG, "testDateRangesWithYearEqual() Execution time: " + executionTime);
712        Thread.sleep(FINAL_WAIT);
713    }
714
715    @Test
716    public void testDateRangesWithMonthAndYearEqual() throws Throwable {
717
718        long startTime = System.currentTimeMillis();
719
720        GuidedDatePickerAction[] datePickerActions = setupDateActionsForMinAndMaxRangeTests();
721
722        scrollToMinAndMaxDates(new int[] {0, 1, 2}, datePickerActions[3]);
723        long executionTime = System.currentTimeMillis() - startTime;
724        Log.d(TAG, "testDateRangesWithMonthAndYearEqual() Execution time: " + executionTime);
725        Thread.sleep(FINAL_WAIT);
726    }
727
728    @Test
729    public void testDateRangesWithAllFieldsEqual() throws Throwable {
730
731        long startTime = System.currentTimeMillis();
732
733        GuidedDatePickerAction[] datePickerActions = setupDateActionsForMinAndMaxRangeTests();
734
735        scrollToMinAndMaxDates(new int[] {0, 1, 2}, datePickerActions[4]);
736        long executionTime = System.currentTimeMillis() - startTime;
737        Log.d(TAG, "testDateRangesWithAllFieldsEqual() Execution time: " + executionTime);
738        Thread.sleep(FINAL_WAIT);
739    }
740
741    private GuidedDatePickerAction[] setupDateActionsForMinAndMaxRangeTests() {
742        Intent intent = new Intent();
743        Resources res = mContext.getResources();
744
745        SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy");
746
747        Calendar currCal = Calendar.getInstance();
748        currCal.set(Calendar.YEAR, 2016);
749        currCal.set(Calendar.MONTH, Calendar.JULY);
750        currCal.set(Calendar.DAY_OF_MONTH, 15);
751
752        Calendar minCal = Calendar.getInstance();
753        minCal.set(Calendar.YEAR, 2014);
754        minCal.set(Calendar.MONTH, Calendar.OCTOBER);
755        minCal.set(Calendar.DAY_OF_MONTH, 20);
756
757        Calendar maxCal = Calendar.getInstance();
758        maxCal.set(Calendar.YEAR, 2018);
759        maxCal.set(Calendar.MONTH, Calendar.FEBRUARY);
760        maxCal.set(Calendar.DAY_OF_MONTH, 10);
761
762        String title = "Date Picker Range Test";
763        String breadcrumb = "Date Picker Range Test Demo";
764        String description = "";
765        GuidanceStylist.Guidance guidance = new GuidanceStylist.Guidance(title, description,
766                breadcrumb, null);
767
768        List<GuidedAction> actionList = new ArrayList<>();
769
770        // testing different date formats and the correctness of range changes as we scroll
771        GuidedDatePickerAction dateAction1 = new GuidedDatePickerAction.Builder(
772                mContext)
773                .id(0)
774                .title(res.getString(R.string.datepicker_with_range_title,
775                        dateFormat.format(minCal.getTime()),
776                        dateFormat.format(maxCal.getTime())))
777                .multilineDescription(true)
778                .date(currCal.getTimeInMillis())
779                .datePickerFormat("MDY")
780                .minDate(minCal.getTimeInMillis())
781                .maxDate(maxCal.getTimeInMillis())
782                .build();
783
784        GuidedDatePickerAction dateAction2 = new GuidedDatePickerAction.Builder(
785                mContext)
786                .id(1)
787                .title(res.getString(R.string.datepicker_with_range_title,
788                        dateFormat.format(minCal.getTimeInMillis()),
789                        dateFormat.format(maxCal.getTimeInMillis())))
790                .multilineDescription(true)
791                .date(currCal.getTimeInMillis())
792                .datePickerFormat("DMY")
793                .minDate(minCal.getTimeInMillis())
794                .maxDate(maxCal.getTimeInMillis())
795                .build();
796
797        // testing date ranges when Year is equal
798        minCal.set(Calendar.YEAR, maxCal.get(Calendar.YEAR));
799        int minMonth = Math.min(minCal.get(Calendar.MONTH), maxCal.get(Calendar.MONTH));
800        int maxMonth = Math.max(minCal.get(Calendar.MONTH), maxCal.get(Calendar.MONTH));
801        minCal.set(Calendar.MONTH, minMonth);
802        maxCal.set(Calendar.MONTH, maxMonth);
803
804        GuidedDatePickerAction dateAction3 = new GuidedDatePickerAction.Builder(
805                mContext)
806                .id(2)
807                .title(res.getString(R.string.datepicker_with_range_title,
808                        dateFormat.format(minCal.getTimeInMillis()),
809                        dateFormat.format(maxCal.getTimeInMillis())))
810                .multilineDescription(true)
811                .date(currCal.getTimeInMillis())
812                .datePickerFormat("DMY")
813                .minDate(minCal.getTimeInMillis())
814                .maxDate(maxCal.getTimeInMillis())
815                .build();
816
817
818        // testing date ranges when both Month and Year are equal
819        minCal.set(Calendar.MONTH, maxCal.get(Calendar.MONTH));
820        int minDay = Math.min(minCal.get(Calendar.DAY_OF_MONTH), maxCal.get(Calendar.DAY_OF_MONTH));
821        int maxDay = Math.max(minCal.get(Calendar.DAY_OF_MONTH), maxCal.get(Calendar.DAY_OF_MONTH));
822        minCal.set(Calendar.DAY_OF_MONTH, minDay);
823        maxCal.set(Calendar.DAY_OF_MONTH, maxDay);
824
825        GuidedDatePickerAction dateAction4 = new GuidedDatePickerAction.Builder(
826                mContext)
827                .id(3)
828                .title(res.getString(R.string.datepicker_with_range_title,
829                        dateFormat.format(minCal.getTimeInMillis()),
830                        dateFormat.format(maxCal.getTimeInMillis())))
831                .multilineDescription(true)
832                .date(currCal.getTimeInMillis())
833                .datePickerFormat("DMY")
834                .minDate(minCal.getTimeInMillis())
835                .maxDate(maxCal.getTimeInMillis())
836                .build();
837
838
839        // testing date ranges when all fields are equal
840        minCal.set(Calendar.DAY_OF_MONTH, maxCal.get(Calendar.DAY_OF_MONTH));
841
842        GuidedDatePickerAction dateAction5 = new GuidedDatePickerAction.Builder(
843                mContext)
844                .id(4)
845                .title(res.getString(R.string.datepicker_with_range_title,
846                        dateFormat.format(minCal.getTimeInMillis()),
847                        dateFormat.format(maxCal.getTimeInMillis())))
848                .multilineDescription(true)
849                .date(currCal.getTimeInMillis())
850                .datePickerFormat("DMY")
851                .minDate(minCal.getTimeInMillis())
852                .maxDate(maxCal.getTimeInMillis())
853                .build();
854
855        actionList.add(dateAction1);
856        actionList.add(dateAction2);
857        actionList.add(dateAction3);
858        actionList.add(dateAction4);
859        actionList.add(dateAction5);
860
861        GuidedStepAttributesTestFragment.clear();
862        GuidedStepAttributesTestFragment.GUIDANCE = guidance;
863        GuidedStepAttributesTestFragment.ACTION_LIST = actionList;
864
865        initActivity(intent);
866        return new GuidedDatePickerAction[] {dateAction1, dateAction2, dateAction3, dateAction4,
867                dateAction5};
868    }
869
870    private void scrollToMinAndMaxDates(int[] columnIndices, GuidedDatePickerAction dateAction)
871            throws Throwable{
872
873        final GuidedStepFragment mFragment = (GuidedStepFragment)
874                mActivity.getGuidedStepTestFragment();
875
876        VerticalGridView guidedActionsList = (VerticalGridView)
877                mActivity.findViewById(R.id.guidedactions_list);
878
879        int currSelectedAction = mFragment.getSelectedActionPosition();
880        // scroll up/down to the requested action
881        long verticalScrollOffset = dateAction.getId() - currSelectedAction;
882
883        int verticalScrollDir = KeyEvent.KEYCODE_DPAD_DOWN;
884        if (verticalScrollOffset < 0) {
885            verticalScrollOffset= -verticalScrollOffset;
886            verticalScrollDir = KeyEvent.KEYCODE_DPAD_UP;
887        }
888        for(int i = 0; i < verticalScrollOffset; i++) {
889            sendKey(verticalScrollDir);
890            Thread.sleep(TRANSITION_LENGTH);
891        }
892
893        assertTrue("The wrong action was selected!", mFragment.getSelectedActionPosition()
894                == dateAction.getId());
895        DatePicker mPickerView = (DatePicker) mFragment.getActionItemView((int) dateAction.getId())
896                .findViewById(R.id.guidedactions_activator_item);
897
898        Calendar currentActionCal = Calendar.getInstance();
899        currentActionCal.setTimeInMillis(dateAction.getDate());
900
901
902        // scrolling to the minimum date
903
904        verticalScrollToFieldValue(Calendar.YEAR, -1, columnIndices, mPickerView,
905                KeyEvent.KEYCODE_DPAD_UP);
906        dateAction.setDate(mPickerView.getDate());
907
908        verticalScrollToFieldValue(Calendar.MONTH, -1, columnIndices, mPickerView,
909                KeyEvent.KEYCODE_DPAD_UP);
910        dateAction.setDate(mPickerView.getDate());
911
912        verticalScrollToFieldValue(Calendar.DAY_OF_MONTH, -1, columnIndices, mPickerView,
913                KeyEvent.KEYCODE_DPAD_UP);
914        dateAction.setDate(mPickerView.getDate());
915
916        Thread.sleep(VERTICAL_SCROLL_WAIT);
917
918        // now scrolling to the maximum date
919
920        verticalScrollToFieldValue(Calendar.YEAR, -1, columnIndices, mPickerView,
921                KeyEvent.KEYCODE_DPAD_DOWN);
922        dateAction.setDate(mPickerView.getDate());
923
924        verticalScrollToFieldValue(Calendar.MONTH, -1, columnIndices, mPickerView,
925                KeyEvent.KEYCODE_DPAD_DOWN);
926        dateAction.setDate(mPickerView.getDate());
927
928        verticalScrollToFieldValue(Calendar.DAY_OF_MONTH, -1, columnIndices, mPickerView,
929                KeyEvent.KEYCODE_DPAD_DOWN);
930        dateAction.setDate(mPickerView.getDate());
931
932        sendKey(KeyEvent.KEYCODE_DPAD_CENTER);
933        Thread.sleep(TRANSITION_LENGTH);
934    }
935
936}
937