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.
5 package org.chromium.content.browser.input;
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;
19 import org.chromium.base.ApiCompatibilityUtils;
20 import org.chromium.content.R;
21 import org.chromium.content.browser.input.DateTimePickerDialog.OnDateTimeSetListener;
22 import org.chromium.content.browser.input.MultiFieldTimePickerDialog.OnMultiFieldTimeSetListener;
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;
32 * Opens the approprate date/time picker dialog for the given dialog type.
34 public class InputDialogContainer {
36 interface InputActionDelegate {
37 void cancelDateTimeDialog();
38 void replaceDateTime(double value);
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;
48 private final Context mContext;
50 // Prevents sending two notifications (from onClick and from onDismiss)
51 private boolean mDialogAlreadyDismissed;
53 private AlertDialog mDialog;
54 private final InputActionDelegate mInputActionDelegate;
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;
68 static boolean isDialogInputType(int type) {
69 return type == sTextInputTypeDate || type == sTextInputTypeTime
70 || type == sTextInputTypeDateTime || type == sTextInputTypeDateTimeLocal
71 || type == sTextInputTypeMonth || type == sTextInputTypeWeek;
74 InputDialogContainer(Context context, InputActionDelegate inputActionDelegate) {
76 mInputActionDelegate = inputActionDelegate;
79 void showPickerDialog(final int dialogType, double dialogValue,
80 double min, double max, double step) {
82 // |dialogValue|, |min|, |max| mean different things depending on the |dialogType|.
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 current time.
87 if (Double.isNaN(dialogValue)) {
88 cal = Calendar.getInstance();
89 cal.set(Calendar.MILLISECOND, 0);
91 if (dialogType == sTextInputTypeMonth) {
92 cal = MonthPicker.createDateFromValue(dialogValue);
93 } else if (dialogType == sTextInputTypeWeek) {
94 cal = WeekPicker.createDateFromValue(dialogValue);
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;
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),
127 } else if (dialogType == sTextInputTypeMonth) {
128 showPickerDialog(dialogType, cal.get(Calendar.YEAR), cal.get(Calendar.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);
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(suggestions));
144 suggestionListView.setAdapter(adapter);
145 suggestionListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
147 public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
148 if (position == adapter.getCount() - 1) {
150 showPickerDialog(dialogType, dialogValue, min, max, step);
152 double suggestionValue = adapter.getItem(position).value();
153 mInputActionDelegate.replaceDateTime(suggestionValue);
155 mDialogAlreadyDismissed = true;
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;
172 mDialog = new AlertDialog.Builder(mContext)
173 .setTitle(dialogTitleId)
174 .setView(suggestionListView)
175 .setNegativeButton(mContext.getText(android.R.string.cancel),
176 new DialogInterface.OnClickListener() {
178 public void onClick(DialogInterface dialog, int which) {
184 mDialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
186 public void onDismiss(DialogInterface dialog) {
187 if (mDialog == dialog && !mDialogAlreadyDismissed) {
188 mDialogAlreadyDismissed = true;
189 mInputActionDelegate.cancelDateTimeDialog();
193 mDialogAlreadyDismissed = false;
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 open,
201 // dismiss the old one.
203 if (suggestions == null) {
204 showPickerDialog(type, value, min, max, step);
206 showSuggestionDialog(type, value, min, max, step, suggestions);
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();
216 int stepTime = (int) step;
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,
225 (long) min, (long) max);
227 dialog.setTitle(mContext.getText(R.string.date_picker_dialog_title));
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,
242 DateFormat.is24HourFormat(mContext), min, max);
243 } else if (dialogType == sTextInputTypeMonth) {
244 mDialog = new MonthPickerDialog(mContext, new MonthOrWeekListener(dialogType),
245 year, month, min, max);
246 } else if (dialogType == sTextInputTypeWeek) {
247 mDialog = new WeekPickerDialog(mContext, new MonthOrWeekListener(dialogType),
248 year, week, min, max);
250 if (ApiCompatibilityUtils.datePickerRequiresAccept()) {
251 mDialog.setButton(DialogInterface.BUTTON_POSITIVE,
252 mContext.getText(R.string.date_picker_dialog_set),
253 (DialogInterface.OnClickListener) mDialog);
256 mDialog.setButton(DialogInterface.BUTTON_NEGATIVE,
257 mContext.getText(android.R.string.cancel),
258 (DialogInterface.OnClickListener) null);
260 mDialog.setButton(DialogInterface.BUTTON_NEUTRAL,
261 mContext.getText(R.string.date_picker_dialog_clear),
262 new DialogInterface.OnClickListener() {
264 public void onClick(DialogInterface dialog, int which) {
265 mDialogAlreadyDismissed = true;
266 mInputActionDelegate.replaceDateTime(Double.NaN);
270 mDialog.setOnDismissListener(
271 new OnDismissListener() {
273 public void onDismiss(final DialogInterface dialog) {
274 if (!mDialogAlreadyDismissed) {
275 mDialogAlreadyDismissed = true;
276 mInputActionDelegate.cancelDateTimeDialog();
281 mDialogAlreadyDismissed = false;
285 boolean isDialogShowing() {
286 return mDialog != null && mDialog.isShowing();
289 void dismissDialog() {
290 if (isDialogShowing()) mDialog.dismiss();
293 private class DateListener implements OnDateSetListener {
294 private final int mDialogType;
296 DateListener(int dialogType) {
297 mDialogType = dialogType;
301 public void onDateSet(DatePicker view, int year, int month, int monthDay) {
302 setFieldDateTimeValue(mDialogType, year, month, monthDay, 0, 0, 0, 0, 0);
306 private class FullTimeListener implements OnMultiFieldTimeSetListener {
307 private final int mDialogType;
308 FullTimeListener(int dialogType) {
309 mDialogType = dialogType;
313 public void onTimeSet(int hourOfDay, int minute, int second, int milli) {
314 setFieldDateTimeValue(mDialogType, 0, 0, 0, hourOfDay, minute, second, milli, 0);
318 private class DateTimeListener implements OnDateTimeSetListener {
319 private final boolean mLocal;
320 private final int mDialogType;
322 public DateTimeListener(int dialogType) {
323 mLocal = dialogType == sTextInputTypeDateTimeLocal;
324 mDialogType = dialogType;
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);
335 private class MonthOrWeekListener implements TwoFieldDatePickerDialog.OnValueSetListener {
336 private final int mDialogType;
338 MonthOrWeekListener(int dialogType) {
339 mDialogType = dialogType;
343 public void onValueSet(int year, int positionInYear) {
344 if (mDialogType == sTextInputTypeMonth) {
345 setFieldDateTimeValue(mDialogType, year, positionInYear, 0, 0, 0, 0, 0, 0);
347 setFieldDateTimeValue(mDialogType, year, 0, 0, 0, 0, 0, 0, positionInYear);
352 protected void setFieldDateTimeValue(int dialogType,
353 int year, int month, int monthDay,
354 int hourOfDay, int minute, int second, int millis,
356 // Prevents more than one callback being sent to the native
357 // side when the dialog triggers multiple events.
358 if (mDialogAlreadyDismissed)
360 mDialogAlreadyDismissed = true;
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(hourOfDay) +
369 TimeUnit.MINUTES.toMillis(minute) +
370 TimeUnit.SECONDS.toMillis(second) +
373 Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
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());