1/*
2 * Copyright (C) 2014 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 com.android.tv.settings.widget.picker;
18
19import android.os.Bundle;
20import android.view.View;
21
22import java.util.ArrayList;
23import java.util.Calendar;
24import java.util.Date;
25import java.util.GregorianCalendar;
26import android.widget.TextView;
27
28public class DatePicker extends Picker {
29
30    private static final String EXTRA_START_YEAR = "start_year";
31    private static final String EXTRA_YEAR_RANGE = "year_range";
32    private static final String EXTRA_DEFAULT_TO_CURRENT = "default_to_current";
33    private static final String EXTRA_FORMAT = "date_format";
34    private static final int DEFAULT_YEAR_RANGE = 24;
35    private static final int DEFAULT_START_YEAR = Calendar.getInstance().get(Calendar.YEAR);
36    private String[] mYears;
37    private int mStartYear;
38    private int mYearRange;
39    private String[] mDayString = null;
40    private int mColMonthIndex = 0;
41    private int mColDayIndex = 1;
42    private int mColYearIndex = 2;
43
44    private boolean mPendingDate = false;
45    private int mInitYear;
46    private int mInitMonth;
47    private int mInitDay;
48
49
50    private int mSelectedYear = DEFAULT_START_YEAR;
51    private String mSelectedMonth;
52
53    public static DatePicker newInstance() {
54        return newInstance("");
55    }
56
57    /**
58     * Creates a new instance of DatePicker
59     *
60     * @param format         String containing a permutation of Y, M and D, indicating the order
61     *                       of the fields Year, Month and Day to be displayed in the DatePicker.
62     */
63    public static DatePicker newInstance(String format) {
64        return newInstance(format, DEFAULT_START_YEAR);
65    }
66
67    /**
68     * Creates a new instance of DatePicker
69     *
70     * @param format         String containing a permutation of Y, M and D, indicating the order
71     *                       of the fields Year, Month and Day to be displayed in the DatePicker.
72     * @param startYear      The lowest number to be displayed in the Year selector.
73     */
74    public static DatePicker newInstance(String format, int startYear) {
75        return newInstance(format, startYear, DEFAULT_YEAR_RANGE, true);
76    }
77
78    /**
79     * Creates a new instance of DatePicker
80     *
81     * @param format         String containing a permutation of Y, M and D, indicating the order
82     *                       of the fields Year, Month and Day to be displayed in the DatePicker.
83     * @param startYear      The lowest number to be displayed in the Year selector.
84     * @param yearRange      Number of entries to be displayed in the Year selector.
85     * @param startOnToday   Indicates if the date should be set to the current date by default.
86     */
87    public static DatePicker newInstance(String format, int startYear, int yearRange,
88            boolean startOnToday) {
89        DatePicker datePicker = new DatePicker();
90        if (startYear <= 0) {
91            throw new IllegalArgumentException("The start year must be > 0. Got " + startYear);
92        }
93        if (yearRange <= 0) {
94            throw new IllegalArgumentException("The year range must be > 0. Got " + yearRange);
95        }
96        Bundle args = new Bundle();
97        args.putString(EXTRA_FORMAT, format);
98        args.putInt(EXTRA_START_YEAR, startYear);
99        args.putInt(EXTRA_YEAR_RANGE, yearRange);
100        args.putBoolean(EXTRA_DEFAULT_TO_CURRENT, startOnToday);
101        datePicker.setArguments(args);
102        return datePicker;
103    }
104
105    private void initYearsArray(int startYear, int yearRange) {
106        mYears = new String[yearRange];
107        for (int i = 0; i < yearRange; i++) {
108            mYears[i] = String.valueOf(startYear + i);
109        }
110    }
111
112    @Override
113    public void onCreate(Bundle savedInstanceState) {
114        super.onCreate(savedInstanceState);
115        mStartYear = getArguments().getInt(EXTRA_START_YEAR, DEFAULT_START_YEAR);
116        mYearRange = getArguments().getInt(EXTRA_YEAR_RANGE, DEFAULT_YEAR_RANGE);
117        boolean startOnToday = getArguments().getBoolean(EXTRA_DEFAULT_TO_CURRENT, false);
118        mSelectedMonth = mConstant.months[0];
119        initYearsArray(mStartYear, mYearRange);
120
121        mDayString = mConstant.days30;
122
123        String format = getArguments().getString(EXTRA_FORMAT);
124        if (format != null && !format.isEmpty()) {
125            format = format.toUpperCase();
126
127            int yIndex = format.indexOf('Y');
128            int mIndex = format.indexOf('M');
129            int dIndex = format.indexOf('D');
130            if (yIndex < 0 || mIndex < 0 || dIndex < 0 || yIndex > 2 || mIndex > 2 || dIndex > 2) {
131                // Badly formatted input. Use default order.
132                mColMonthIndex = 0;
133                mColDayIndex = 1;
134                mColYearIndex = 2;
135            } else {
136                mColMonthIndex = mIndex;
137                mColDayIndex = dIndex;
138                mColYearIndex = yIndex;
139            }
140        }
141
142        if (startOnToday) {
143            mPendingDate = true;
144            Calendar cal = Calendar.getInstance();
145            mInitYear = cal.get(Calendar.YEAR);
146            mInitMonth = cal.get(Calendar.MONTH);
147            mInitDay = cal.get(Calendar.DATE);
148        }
149    }
150
151    @Override
152    public void onResume() {
153        if (mPendingDate) {
154            mPendingDate = false;
155            setDate(mInitYear, mInitMonth, mInitDay);
156        }
157        super.onResume();
158    }
159
160    @Override
161    protected ArrayList<PickerColumn> getColumns() {
162        ArrayList<PickerColumn> ret = new ArrayList<PickerColumn>();
163        // TODO orders of these columns might need to be localized
164        PickerColumn months = new PickerColumn(mConstant.months);
165        PickerColumn days = new PickerColumn(mDayString);
166        PickerColumn years = new PickerColumn(mYears);
167
168        for (int i = 0; i < 3; i++) {
169            if (i == mColYearIndex) {
170                ret.add(years);
171            } else if (i == mColMonthIndex) {
172                ret.add(months);
173            } else if (i == mColDayIndex) {
174                ret.add(days);
175            }
176        }
177
178        return ret;
179    }
180
181    @Override
182    protected String getSeparator() {
183        return mConstant.dateSeparator;
184    }
185
186    protected boolean setDate(int year, int month, int day) {
187        boolean isLeapYear = false;
188
189        if (year < mStartYear || year > (mStartYear + mYearRange)) {
190            return false;
191        }
192
193        // Test to see if this is a valid date
194        try {
195            GregorianCalendar cal = new GregorianCalendar(year, month, day);
196            cal.setLenient(false);
197            Date test = cal.getTime();
198        } catch (IllegalArgumentException e) {
199            return false;
200        }
201
202        mSelectedYear = year;
203        mSelectedMonth = mConstant.months[month];
204
205        updateSelection(mColYearIndex, year - mStartYear);
206        updateSelection(mColMonthIndex, month);
207
208        String[] dayString = null;
209        // This is according to http://en.wikipedia.org/wiki/Leap_year#Algorithm
210        if (year % 400 == 0) {
211            isLeapYear = true;
212        } else if (year % 100 == 0) {
213            isLeapYear = false;
214        } else if (year % 4 == 0) {
215            isLeapYear = true;
216        }
217
218        if (month == 1) {
219            if (isLeapYear) {
220                dayString = mConstant.days29;
221            } else {
222                dayString = mConstant.days28;
223            }
224        } else if ((month == 3) || (month == 5) || (month == 8) || (month == 10)) {
225            dayString = mConstant.days30;
226        } else {
227            dayString = mConstant.days31;
228        }
229
230        if (mDayString != dayString) {
231            mDayString = dayString;
232            updateAdapter(mColDayIndex, new PickerColumn(mDayString));
233        }
234
235        updateSelection(mColDayIndex, day - 1);
236        return true;
237    }
238
239    @Override
240    public void onScroll(View v) {
241        int column = (Integer) v.getTag();
242        String text = ((TextView) v).getText().toString();
243        if (column == mColMonthIndex) {
244            mSelectedMonth = text;
245        } else if (column == mColYearIndex) {
246            mSelectedYear = Integer.parseInt(text);
247        } else {
248            return;
249        }
250
251        String[] dayString = null;
252
253        boolean isLeapYear = false;
254        // This is according to http://en.wikipedia.org/wiki/Leap_year#Algorithm
255        if (mSelectedYear % 400 == 0) {
256            isLeapYear = true;
257        } else if (mSelectedYear % 100 == 0) {
258            isLeapYear = false;
259        } else if (mSelectedYear % 4 == 0) {
260            isLeapYear = true;
261        }
262        if (mSelectedMonth.equals(mConstant.months[1])) {
263            if (isLeapYear) {
264                dayString = mConstant.days29;
265            } else {
266                dayString = mConstant.days28;
267            }
268        } else if (mSelectedMonth.equals(mConstant.months[3])
269                || mSelectedMonth.equals(mConstant.months[5])
270                || mSelectedMonth.equals(mConstant.months[8])
271                || mSelectedMonth.equals(mConstant.months[10])) {
272            dayString = mConstant.days30;
273        } else {
274            dayString = mConstant.days31;
275        }
276        if (!mDayString.equals(dayString)) {
277            mDayString = dayString;
278            updateAdapter(mColDayIndex, new PickerColumn(mDayString));
279        }
280    }
281}
282