Upstream version 10.38.222.0
[platform/framework/web/crosswalk.git] / src / content / public / android / java / src / org / chromium / content / browser / input / TwoFieldDatePicker.java
1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 package org.chromium.content.browser.input;
6
7 import android.content.Context;
8 import android.text.format.DateUtils;
9 import android.view.LayoutInflater;
10 import android.view.accessibility.AccessibilityEvent;
11 import android.widget.FrameLayout;
12 import android.widget.NumberPicker;
13 import android.widget.NumberPicker.OnValueChangeListener;
14
15 import org.chromium.content.R;
16
17 import java.util.Calendar;
18 import java.util.TimeZone;
19
20 /**
21  * This class is heavily based on android.widget.DatePicker.
22  */
23 public abstract class TwoFieldDatePicker extends FrameLayout {
24
25     private final NumberPicker mPositionInYearSpinner;
26
27     private final NumberPicker mYearSpinner;
28
29     private OnMonthOrWeekChangedListener mMonthOrWeekChangedListener;
30
31     // It'd be nice to use android.text.Time like in other Dialogs but
32     // it suffers from the 2038 effect so it would prevent us from
33     // having dates over 2038.
34     private Calendar mMinDate;
35
36     private Calendar mMaxDate;
37
38     private Calendar mCurrentDate;
39
40     /**
41      * The callback used to indicate the user changes\d the date.
42      */
43     public interface OnMonthOrWeekChangedListener {
44
45         /**
46          * Called upon a date change.
47          *
48          * @param view The view associated with this listener.
49          * @param year The year that was set.
50          * @param positionInYear The month or week in year.
51          */
52         void onMonthOrWeekChanged(TwoFieldDatePicker view, int year, int positionInYear);
53     }
54
55     public TwoFieldDatePicker(Context context, double minValue, double maxValue) {
56         super(context, null, android.R.attr.datePickerStyle);
57
58         LayoutInflater inflater = (LayoutInflater) context
59                 .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
60         inflater.inflate(R.layout.two_field_date_picker, this, true);
61
62         OnValueChangeListener onChangeListener = new OnValueChangeListener() {
63             @Override
64             public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
65                 int year = getYear();
66                 int positionInYear = getPositionInYear();
67                 // take care of wrapping of days and months to update greater fields
68                 if (picker == mPositionInYearSpinner) {
69                     positionInYear = newVal;
70                     if (oldVal == picker.getMaxValue() && newVal == picker.getMinValue()) {
71                         year += 1;
72                         positionInYear = getMinPositionInYear(year);
73                     } else if (oldVal == picker.getMinValue() && newVal == picker.getMaxValue()) {
74                         year -= 1;
75                         positionInYear = getMaxPositionInYear(year);
76                     }
77                 } else if (picker == mYearSpinner) {
78                     year = newVal;
79                 } else {
80                     throw new IllegalArgumentException();
81                 }
82
83                 // now set the date to the adjusted one
84                 setCurrentDate(year, positionInYear);
85                 updateSpinners();
86                 notifyDateChanged();
87             }
88         };
89
90         mCurrentDate = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
91         if (minValue >= maxValue) {
92             mMinDate = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
93             mMinDate.set(0, 0, 1);
94             mMaxDate = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
95             mMaxDate.set(9999, 0, 1);
96         } else {
97             mMinDate = getDateForValue(minValue);
98             mMaxDate = getDateForValue(maxValue);
99         }
100
101         // month
102         mPositionInYearSpinner = (NumberPicker) findViewById(R.id.position_in_year);
103         mPositionInYearSpinner.setOnLongPressUpdateInterval(200);
104         mPositionInYearSpinner.setOnValueChangedListener(onChangeListener);
105
106         // year
107         mYearSpinner = (NumberPicker) findViewById(R.id.year);
108         mYearSpinner.setOnLongPressUpdateInterval(100);
109         mYearSpinner.setOnValueChangedListener(onChangeListener);
110     }
111
112     /**
113      * Initialize the state. If the provided values designate an inconsistent
114      * date the values are normalized before updating the spinners.
115      *
116      * @param year The initial year.
117      * @param positionInYear The initial month <strong>starting from zero</strong> or week in year.
118      * @param onMonthOrWeekChangedListener How user is notified date is changed by
119      *            user, can be null.
120      */
121     public void init(int year, int positionInYear,
122             OnMonthOrWeekChangedListener onMonthOrWeekChangedListener) {
123         setCurrentDate(year, positionInYear);
124         updateSpinners();
125         mMonthOrWeekChangedListener = onMonthOrWeekChangedListener;
126     }
127
128     public boolean isNewDate(int year, int positionInYear) {
129         return (getYear() != year || getPositionInYear() != positionInYear);
130     }
131
132     /**
133      * Subclasses know the semantics of @value, and need to return
134      * a Calendar corresponding to it.
135      */
136     protected abstract Calendar getDateForValue(double value);
137
138     /**
139      * Updates the current date.
140      *
141      * @param year The year.
142      * @param positionInYear The month or week in year.
143      */
144     public void updateDate(int year, int positionInYear) {
145         if (!isNewDate(year, positionInYear)) {
146             return;
147         }
148         setCurrentDate(year, positionInYear);
149         updateSpinners();
150         notifyDateChanged();
151     }
152
153     /**
154      * Subclasses know the semantics of @positionInYear, and need to update @mCurrentDate to the
155      * appropriate date.
156      */
157     protected abstract void setCurrentDate(int year, int positionInYear);
158
159     protected void setCurrentDate(Calendar date) {
160         mCurrentDate = date;
161     }
162
163     @Override
164     public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
165         onPopulateAccessibilityEvent(event);
166         return true;
167     }
168
169     @Override
170     public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
171         super.onPopulateAccessibilityEvent(event);
172
173         final int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR;
174         String selectedDateUtterance = DateUtils.formatDateTime(getContext(),
175                 mCurrentDate.getTimeInMillis(), flags);
176         event.getText().add(selectedDateUtterance);
177     }
178
179     /**
180      * @return The selected year.
181      */
182     public int getYear() {
183         return mCurrentDate.get(Calendar.YEAR);
184     }
185
186     /**
187      * @return The selected month or week.
188      */
189     public abstract int getPositionInYear();
190
191     protected abstract int getMaxYear();
192
193     protected abstract int getMinYear();
194
195     protected abstract int getMaxPositionInYear(int year);
196
197     protected abstract int getMinPositionInYear(int year);
198
199     protected Calendar getMaxDate() {
200         return mMaxDate;
201     }
202
203     protected Calendar getMinDate() {
204         return mMinDate;
205     }
206
207     protected Calendar getCurrentDate() {
208         return mCurrentDate;
209     }
210
211     protected NumberPicker getPositionInYearSpinner() {
212         return mPositionInYearSpinner;
213     }
214
215     protected NumberPicker getYearSpinner() {
216         return mYearSpinner;
217     }
218
219     /**
220      * This method should be subclassed to update the spinners based on mCurrentDate.
221      */
222     protected void updateSpinners() {
223         mPositionInYearSpinner.setDisplayedValues(null);
224
225         // set the spinner ranges respecting the min and max dates
226         mPositionInYearSpinner.setMinValue(getMinPositionInYear(getYear()));
227         mPositionInYearSpinner.setMaxValue(getMaxPositionInYear(getYear()));
228         mPositionInYearSpinner.setWrapSelectorWheel(
229                 !mCurrentDate.equals(mMinDate) && !mCurrentDate.equals(mMaxDate));
230
231         // year spinner range does not change based on the current date
232         mYearSpinner.setMinValue(getMinYear());
233         mYearSpinner.setMaxValue(getMaxYear());
234         mYearSpinner.setWrapSelectorWheel(false);
235
236         // set the spinner values
237         mYearSpinner.setValue(getYear());
238         mPositionInYearSpinner.setValue(getPositionInYear());
239     }
240
241     /**
242      * Notifies the listener, if such, for a change in the selected date.
243      */
244     protected void notifyDateChanged() {
245         sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED);
246         if (mMonthOrWeekChangedListener != null) {
247             mMonthOrWeekChangedListener.onMonthOrWeekChanged(this, getYear(), getPositionInYear());
248         }
249     }
250 }