OLD | NEW |
| (Empty) |
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 positio
nInYear); | |
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 newVa
l) { | |
65 int year = getYear(); | |
66 int positionInYear = getPositionInYear(); | |
67 // take care of wrapping of days and months to update greater fi
elds | |
68 if (picker == mPositionInYearSpinner) { | |
69 positionInYear = newVal; | |
70 if (oldVal == picker.getMaxValue() && newVal == picker.getMi
nValue()) { | |
71 year += 1; | |
72 positionInYear = getMinPositionInYear(year); | |
73 } else if (oldVal == picker.getMinValue() && newVal == picke
r.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_ye
ar); | |
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</stron
g> 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 @mCu
rrentDate 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_YEA
R; | |
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 mCurrent
Date. | |
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(), ge
tPositionInYear()); | |
248 } | |
249 } | |
250 } | |
OLD | NEW |