OLD | NEW |
| (Empty) |
1 // Copyright 2012 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.app.AlertDialog; | |
8 import android.app.DatePickerDialog.OnDateSetListener; | |
9 import android.content.Context; | |
10 import android.content.DialogInterface; | |
11 import android.content.DialogInterface.OnDismissListener; | |
12 import android.text.format.DateFormat; | |
13 import android.view.View; | |
14 import android.widget.AdapterView; | |
15 import android.widget.DatePicker; | |
16 import android.widget.ListView; | |
17 import android.widget.TimePicker; | |
18 | |
19 import org.chromium.base.ApiCompatibilityUtils; | |
20 import org.chromium.content.R; | |
21 import org.chromium.content.browser.input.DateTimePickerDialog.OnDateTimeSetList
ener; | |
22 import org.chromium.content.browser.input.MultiFieldTimePickerDialog.OnMultiFiel
dTimeSetListener; | |
23 | |
24 import java.util.Arrays; | |
25 import java.util.Calendar; | |
26 import java.util.Date; | |
27 import java.util.GregorianCalendar; | |
28 import java.util.TimeZone; | |
29 import java.util.concurrent.TimeUnit; | |
30 | |
31 /** | |
32 * Opens the approprate date/time picker dialog for the given dialog type. | |
33 */ | |
34 public class InputDialogContainer { | |
35 | |
36 interface InputActionDelegate { | |
37 void cancelDateTimeDialog(); | |
38 void replaceDateTime(double value); | |
39 } | |
40 | |
41 private static int sTextInputTypeDate; | |
42 private static int sTextInputTypeDateTime; | |
43 private static int sTextInputTypeDateTimeLocal; | |
44 private static int sTextInputTypeMonth; | |
45 private static int sTextInputTypeTime; | |
46 private static int sTextInputTypeWeek; | |
47 | |
48 private final Context mContext; | |
49 | |
50 // Prevents sending two notifications (from onClick and from onDismiss) | |
51 private boolean mDialogAlreadyDismissed; | |
52 | |
53 private AlertDialog mDialog; | |
54 private final InputActionDelegate mInputActionDelegate; | |
55 | |
56 static void initializeInputTypes(int textInputTypeDate, | |
57 int textInputTypeDateTime, int textInputTypeDateTimeLocal, | |
58 int textInputTypeMonth, int textInputTypeTime, | |
59 int textInputTypeWeek) { | |
60 sTextInputTypeDate = textInputTypeDate; | |
61 sTextInputTypeDateTime = textInputTypeDateTime; | |
62 sTextInputTypeDateTimeLocal = textInputTypeDateTimeLocal; | |
63 sTextInputTypeMonth = textInputTypeMonth; | |
64 sTextInputTypeTime = textInputTypeTime; | |
65 sTextInputTypeWeek = textInputTypeWeek; | |
66 } | |
67 | |
68 static boolean isDialogInputType(int type) { | |
69 return type == sTextInputTypeDate || type == sTextInputTypeTime | |
70 || type == sTextInputTypeDateTime || type == sTextInputTypeDateT
imeLocal | |
71 || type == sTextInputTypeMonth || type == sTextInputTypeWeek; | |
72 } | |
73 | |
74 InputDialogContainer(Context context, InputActionDelegate inputActionDelegat
e) { | |
75 mContext = context; | |
76 mInputActionDelegate = inputActionDelegate; | |
77 } | |
78 | |
79 void showPickerDialog(final int dialogType, double dialogValue, | |
80 double min, double max, double step) { | |
81 Calendar cal; | |
82 // |dialogValue|, |min|, |max| mean different things depending on the |d
ialogType|. | |
83 // For input type=month is the number of months since 1970. | |
84 // For input type=time it is milliseconds since midnight. | |
85 // For other types they are just milliseconds since 1970. | |
86 // If |dialogValue| is NaN it means an empty value. We will show the cur
rent time. | |
87 if (Double.isNaN(dialogValue)) { | |
88 cal = Calendar.getInstance(); | |
89 cal.set(Calendar.MILLISECOND, 0); | |
90 } else { | |
91 if (dialogType == sTextInputTypeMonth) { | |
92 cal = MonthPicker.createDateFromValue(dialogValue); | |
93 } else if (dialogType == sTextInputTypeWeek) { | |
94 cal = WeekPicker.createDateFromValue(dialogValue); | |
95 } else { | |
96 GregorianCalendar gregorianCalendar = | |
97 new GregorianCalendar(TimeZone.getTimeZone("UTC")); | |
98 // According to the HTML spec we only use the Gregorian calendar | |
99 // so we ignore the Julian/Gregorian transition. | |
100 gregorianCalendar.setGregorianChange(new Date(Long.MIN_VALUE)); | |
101 gregorianCalendar.setTimeInMillis((long) dialogValue); | |
102 cal = gregorianCalendar; | |
103 } | |
104 } | |
105 if (dialogType == sTextInputTypeDate) { | |
106 showPickerDialog(dialogType, | |
107 cal.get(Calendar.YEAR), | |
108 cal.get(Calendar.MONTH), | |
109 cal.get(Calendar.DAY_OF_MONTH), | |
110 0, 0, 0, 0, 0, min, max, step); | |
111 } else if (dialogType == sTextInputTypeTime) { | |
112 showPickerDialog(dialogType, 0, 0, 0, | |
113 cal.get(Calendar.HOUR_OF_DAY), | |
114 cal.get(Calendar.MINUTE), | |
115 0, 0, 0, min, max, step); | |
116 } else if (dialogType == sTextInputTypeDateTime || | |
117 dialogType == sTextInputTypeDateTimeLocal) { | |
118 showPickerDialog(dialogType, | |
119 cal.get(Calendar.YEAR), | |
120 cal.get(Calendar.MONTH), | |
121 cal.get(Calendar.DAY_OF_MONTH), | |
122 cal.get(Calendar.HOUR_OF_DAY), | |
123 cal.get(Calendar.MINUTE), | |
124 cal.get(Calendar.SECOND), | |
125 cal.get(Calendar.MILLISECOND), | |
126 0, min, max, step); | |
127 } else if (dialogType == sTextInputTypeMonth) { | |
128 showPickerDialog(dialogType, cal.get(Calendar.YEAR), cal.get(Calenda
r.MONTH), 0, | |
129 0, 0, 0, 0, 0, min, max, step); | |
130 } else if (dialogType == sTextInputTypeWeek) { | |
131 int year = WeekPicker.getISOWeekYearForDate(cal); | |
132 int week = WeekPicker.getWeekForDate(cal); | |
133 showPickerDialog(dialogType, year, 0, 0, 0, 0, 0, 0, week, min, max,
step); | |
134 } | |
135 } | |
136 | |
137 void showSuggestionDialog(final int dialogType, | |
138 final double dialogValue, | |
139 final double min, final double max, final double step, | |
140 DateTimeSuggestion[] suggestions) { | |
141 ListView suggestionListView = new ListView(mContext); | |
142 final DateTimeSuggestionListAdapter adapter = | |
143 new DateTimeSuggestionListAdapter(mContext, Arrays.asList(suggestion
s)); | |
144 suggestionListView.setAdapter(adapter); | |
145 suggestionListView.setOnItemClickListener(new AdapterView.OnItemClickLis
tener() { | |
146 @Override | |
147 public void onItemClick(AdapterView<?> parent, View view, int positi
on, long id) { | |
148 if (position == adapter.getCount() - 1) { | |
149 dismissDialog(); | |
150 showPickerDialog(dialogType, dialogValue, min, max, step); | |
151 } else { | |
152 double suggestionValue = adapter.getItem(position).value(); | |
153 mInputActionDelegate.replaceDateTime(suggestionValue); | |
154 dismissDialog(); | |
155 mDialogAlreadyDismissed = true; | |
156 } | |
157 } | |
158 }); | |
159 | |
160 int dialogTitleId = R.string.date_picker_dialog_title; | |
161 if (dialogType == sTextInputTypeTime) { | |
162 dialogTitleId = R.string.time_picker_dialog_title; | |
163 } else if (dialogType == sTextInputTypeDateTime || | |
164 dialogType == sTextInputTypeDateTimeLocal) { | |
165 dialogTitleId = R.string.date_time_picker_dialog_title; | |
166 } else if (dialogType == sTextInputTypeMonth) { | |
167 dialogTitleId = R.string.month_picker_dialog_title; | |
168 } else if (dialogType == sTextInputTypeWeek) { | |
169 dialogTitleId = R.string.week_picker_dialog_title; | |
170 } | |
171 | |
172 mDialog = new AlertDialog.Builder(mContext) | |
173 .setTitle(dialogTitleId) | |
174 .setView(suggestionListView) | |
175 .setNegativeButton(mContext.getText(android.R.string.cancel), | |
176 new DialogInterface.OnClickListener() { | |
177 @Override | |
178 public void onClick(DialogInterface dialog, int which) { | |
179 dismissDialog(); | |
180 } | |
181 }) | |
182 .create(); | |
183 | |
184 mDialog.setOnDismissListener(new DialogInterface.OnDismissListener() { | |
185 @Override | |
186 public void onDismiss(DialogInterface dialog) { | |
187 if (mDialog == dialog && !mDialogAlreadyDismissed) { | |
188 mDialogAlreadyDismissed = true; | |
189 mInputActionDelegate.cancelDateTimeDialog(); | |
190 } | |
191 } | |
192 }); | |
193 mDialogAlreadyDismissed = false; | |
194 mDialog.show(); | |
195 } | |
196 | |
197 void showDialog(final int type, final double value, | |
198 double min, double max, double step, | |
199 DateTimeSuggestion[] suggestions) { | |
200 // When the web page asks to show a dialog while there is one already op
en, | |
201 // dismiss the old one. | |
202 dismissDialog(); | |
203 if (suggestions == null) { | |
204 showPickerDialog(type, value, min, max, step); | |
205 } else { | |
206 showSuggestionDialog(type, value, min, max, step, suggestions); | |
207 } | |
208 } | |
209 | |
210 void showPickerDialog(final int dialogType, | |
211 int year, int month, int monthDay, | |
212 int hourOfDay, int minute, int second, int millis, int week, | |
213 double min, double max, double step) { | |
214 if (isDialogShowing()) mDialog.dismiss(); | |
215 | |
216 int stepTime = (int) step; | |
217 | |
218 if (dialogType == sTextInputTypeDate) { | |
219 ChromeDatePickerDialog dialog = new ChromeDatePickerDialog(mContext, | |
220 new DateListener(dialogType), | |
221 year, month, monthDay); | |
222 DateDialogNormalizer.normalize(dialog.getDatePicker(), dialog, | |
223 year, month, monthDay, | |
224 0, 0, | |
225 (long) min, (long) max); | |
226 | |
227 dialog.setTitle(mContext.getText(R.string.date_picker_dialog_title))
; | |
228 mDialog = dialog; | |
229 } else if (dialogType == sTextInputTypeTime) { | |
230 mDialog = new MultiFieldTimePickerDialog( | |
231 mContext, 0 /* theme */ , | |
232 hourOfDay, minute, second, millis, | |
233 (int) min, (int) max, stepTime, | |
234 DateFormat.is24HourFormat(mContext), | |
235 new FullTimeListener(dialogType)); | |
236 } else if (dialogType == sTextInputTypeDateTime || | |
237 dialogType == sTextInputTypeDateTimeLocal) { | |
238 mDialog = new DateTimePickerDialog(mContext, | |
239 new DateTimeListener(dialogType), | |
240 year, month, monthDay, | |
241 hourOfDay, minute, | |
242 DateFormat.is24HourFormat(mContext), min, max); | |
243 } else if (dialogType == sTextInputTypeMonth) { | |
244 mDialog = new MonthPickerDialog(mContext, new MonthOrWeekListener(di
alogType), | |
245 year, month, min, max); | |
246 } else if (dialogType == sTextInputTypeWeek) { | |
247 mDialog = new WeekPickerDialog(mContext, new MonthOrWeekListener(dia
logType), | |
248 year, week, min, max); | |
249 } | |
250 if (ApiCompatibilityUtils.datePickerRequiresAccept()) { | |
251 mDialog.setButton(DialogInterface.BUTTON_POSITIVE, | |
252 mContext.getText(R.string.date_picker_dialog_set), | |
253 (DialogInterface.OnClickListener) mDialog); | |
254 } | |
255 | |
256 mDialog.setButton(DialogInterface.BUTTON_NEGATIVE, | |
257 mContext.getText(android.R.string.cancel), | |
258 (DialogInterface.OnClickListener) null); | |
259 | |
260 mDialog.setButton(DialogInterface.BUTTON_NEUTRAL, | |
261 mContext.getText(R.string.date_picker_dialog_clear), | |
262 new DialogInterface.OnClickListener() { | |
263 @Override | |
264 public void onClick(DialogInterface dialog, int which) { | |
265 mDialogAlreadyDismissed = true; | |
266 mInputActionDelegate.replaceDateTime(Double.NaN); | |
267 } | |
268 }); | |
269 | |
270 mDialog.setOnDismissListener( | |
271 new OnDismissListener() { | |
272 @Override | |
273 public void onDismiss(final DialogInterface dialog) { | |
274 if (!mDialogAlreadyDismissed) { | |
275 mDialogAlreadyDismissed = true; | |
276 mInputActionDelegate.cancelDateTimeDialog(); | |
277 } | |
278 } | |
279 }); | |
280 | |
281 mDialogAlreadyDismissed = false; | |
282 mDialog.show(); | |
283 } | |
284 | |
285 boolean isDialogShowing() { | |
286 return mDialog != null && mDialog.isShowing(); | |
287 } | |
288 | |
289 void dismissDialog() { | |
290 if (isDialogShowing()) mDialog.dismiss(); | |
291 } | |
292 | |
293 private class DateListener implements OnDateSetListener { | |
294 private final int mDialogType; | |
295 | |
296 DateListener(int dialogType) { | |
297 mDialogType = dialogType; | |
298 } | |
299 | |
300 @Override | |
301 public void onDateSet(DatePicker view, int year, int month, int monthDay
) { | |
302 setFieldDateTimeValue(mDialogType, year, month, monthDay, 0, 0, 0, 0
, 0); | |
303 } | |
304 } | |
305 | |
306 private class FullTimeListener implements OnMultiFieldTimeSetListener { | |
307 private final int mDialogType; | |
308 FullTimeListener(int dialogType) { | |
309 mDialogType = dialogType; | |
310 } | |
311 | |
312 @Override | |
313 public void onTimeSet(int hourOfDay, int minute, int second, int milli)
{ | |
314 setFieldDateTimeValue(mDialogType, 0, 0, 0, hourOfDay, minute, secon
d, milli, 0); | |
315 } | |
316 } | |
317 | |
318 private class DateTimeListener implements OnDateTimeSetListener { | |
319 private final boolean mLocal; | |
320 private final int mDialogType; | |
321 | |
322 public DateTimeListener(int dialogType) { | |
323 mLocal = dialogType == sTextInputTypeDateTimeLocal; | |
324 mDialogType = dialogType; | |
325 } | |
326 | |
327 @Override | |
328 public void onDateTimeSet(DatePicker dateView, TimePicker timeView, | |
329 int year, int month, int monthDay, | |
330 int hourOfDay, int minute) { | |
331 setFieldDateTimeValue(mDialogType, year, month, monthDay, hourOfDay,
minute, 0, 0, 0); | |
332 } | |
333 } | |
334 | |
335 private class MonthOrWeekListener implements TwoFieldDatePickerDialog.OnValu
eSetListener { | |
336 private final int mDialogType; | |
337 | |
338 MonthOrWeekListener(int dialogType) { | |
339 mDialogType = dialogType; | |
340 } | |
341 | |
342 @Override | |
343 public void onValueSet(int year, int positionInYear) { | |
344 if (mDialogType == sTextInputTypeMonth) { | |
345 setFieldDateTimeValue(mDialogType, year, positionInYear, 0, 0, 0
, 0, 0, 0); | |
346 } else { | |
347 setFieldDateTimeValue(mDialogType, year, 0, 0, 0, 0, 0, 0, posit
ionInYear); | |
348 } | |
349 } | |
350 } | |
351 | |
352 protected void setFieldDateTimeValue(int dialogType, | |
353 int year, int month, int monthDay, | |
354 int hourOfDay, int minute, int second, in
t millis, | |
355 int week) { | |
356 // Prevents more than one callback being sent to the native | |
357 // side when the dialog triggers multiple events. | |
358 if (mDialogAlreadyDismissed) | |
359 return; | |
360 mDialogAlreadyDismissed = true; | |
361 | |
362 if (dialogType == sTextInputTypeMonth) { | |
363 mInputActionDelegate.replaceDateTime((year - 1970) * 12 + month); | |
364 } else if (dialogType == sTextInputTypeWeek) { | |
365 mInputActionDelegate.replaceDateTime( | |
366 WeekPicker.createDateFromWeek(year, week).getTimeInMillis()); | |
367 } else if (dialogType == sTextInputTypeTime) { | |
368 mInputActionDelegate.replaceDateTime(TimeUnit.HOURS.toMillis(hourOfD
ay) + | |
369 TimeUnit.MINUTES.toMillis(minut
e) + | |
370 TimeUnit.SECONDS.toMillis(secon
d) + | |
371 millis); | |
372 } else { | |
373 Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC")); | |
374 cal.clear(); | |
375 cal.set(Calendar.YEAR, year); | |
376 cal.set(Calendar.MONTH, month); | |
377 cal.set(Calendar.DAY_OF_MONTH, monthDay); | |
378 cal.set(Calendar.HOUR_OF_DAY, hourOfDay); | |
379 cal.set(Calendar.MINUTE, minute); | |
380 cal.set(Calendar.SECOND, second); | |
381 cal.set(Calendar.MILLISECOND, millis); | |
382 mInputActionDelegate.replaceDateTime(cal.getTimeInMillis()); | |
383 } | |
384 } | |
385 } | |
OLD | NEW |