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.content.R;
20 import org.chromium.content.browser.input.DateTimePickerDialog.OnDateTimeSetListener;
21 import org.chromium.content.browser.input.MultiFieldTimePickerDialog.OnMultiFieldTimeSetListener;
23 import java.util.Arrays;
24 import java.util.Calendar;
25 import java.util.Date;
26 import java.util.GregorianCalendar;
27 import java.util.TimeZone;
28 import java.util.concurrent.TimeUnit;
31 * Opens the approprate date/time picker dialog for the given dialog type.
33 public class InputDialogContainer {
35 interface InputActionDelegate {
36 void cancelDateTimeDialog();
37 void replaceDateTime(double value);
40 private static int sTextInputTypeDate;
41 private static int sTextInputTypeDateTime;
42 private static int sTextInputTypeDateTimeLocal;
43 private static int sTextInputTypeMonth;
44 private static int sTextInputTypeTime;
45 private static int sTextInputTypeWeek;
47 private final Context mContext;
49 // Prevents sending two notifications (from onClick and from onDismiss)
50 private boolean mDialogAlreadyDismissed;
52 private AlertDialog mDialog;
53 private final InputActionDelegate mInputActionDelegate;
55 static void initializeInputTypes(int textInputTypeDate,
56 int textInputTypeDateTime, int textInputTypeDateTimeLocal,
57 int textInputTypeMonth, int textInputTypeTime,
58 int textInputTypeWeek) {
59 sTextInputTypeDate = textInputTypeDate;
60 sTextInputTypeDateTime = textInputTypeDateTime;
61 sTextInputTypeDateTimeLocal = textInputTypeDateTimeLocal;
62 sTextInputTypeMonth = textInputTypeMonth;
63 sTextInputTypeTime = textInputTypeTime;
64 sTextInputTypeWeek = textInputTypeWeek;
67 static boolean isDialogInputType(int type) {
68 return type == sTextInputTypeDate || type == sTextInputTypeTime
69 || type == sTextInputTypeDateTime || type == sTextInputTypeDateTimeLocal
70 || type == sTextInputTypeMonth || type == sTextInputTypeWeek;
73 InputDialogContainer(Context context, InputActionDelegate inputActionDelegate) {
75 mInputActionDelegate = inputActionDelegate;
78 void showPickerDialog(final int dialogType, double dialogValue,
79 double min, double max, double step) {
81 // |dialogValue|, |min|, |max| mean different things depending on the |dialogType|.
82 // For input type=month is the number of months since 1970.
83 // For input type=time it is milliseconds since midnight.
84 // For other types they are just milliseconds since 1970.
85 // If |dialogValue| is NaN it means an empty value. We will show the current time.
86 if (Double.isNaN(dialogValue)) {
87 cal = Calendar.getInstance();
88 cal.set(Calendar.MILLISECOND, 0);
90 if (dialogType == sTextInputTypeMonth) {
91 cal = MonthPicker.createDateFromValue(dialogValue);
92 } else if (dialogType == sTextInputTypeWeek) {
93 cal = WeekPicker.createDateFromValue(dialogValue);
95 GregorianCalendar gregorianCalendar =
96 new GregorianCalendar(TimeZone.getTimeZone("UTC"));
97 // According to the HTML spec we only use the Gregorian calendar
98 // so we ignore the Julian/Gregorian transition.
99 gregorianCalendar.setGregorianChange(new Date(Long.MIN_VALUE));
100 gregorianCalendar.setTimeInMillis((long) dialogValue);
101 cal = gregorianCalendar;
104 if (dialogType == sTextInputTypeDate) {
105 showPickerDialog(dialogType,
106 cal.get(Calendar.YEAR),
107 cal.get(Calendar.MONTH),
108 cal.get(Calendar.DAY_OF_MONTH),
109 0, 0, 0, 0, 0, min, max, step);
110 } else if (dialogType == sTextInputTypeTime) {
111 showPickerDialog(dialogType, 0, 0, 0,
112 cal.get(Calendar.HOUR_OF_DAY),
113 cal.get(Calendar.MINUTE),
114 0, 0, 0, min, max, step);
115 } else if (dialogType == sTextInputTypeDateTime ||
116 dialogType == sTextInputTypeDateTimeLocal) {
117 showPickerDialog(dialogType,
118 cal.get(Calendar.YEAR),
119 cal.get(Calendar.MONTH),
120 cal.get(Calendar.DAY_OF_MONTH),
121 cal.get(Calendar.HOUR_OF_DAY),
122 cal.get(Calendar.MINUTE),
123 cal.get(Calendar.SECOND),
124 cal.get(Calendar.MILLISECOND),
126 } else if (dialogType == sTextInputTypeMonth) {
127 showPickerDialog(dialogType, cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), 0,
128 0, 0, 0, 0, 0, min, max, step);
129 } else if (dialogType == sTextInputTypeWeek) {
130 int year = WeekPicker.getISOWeekYearForDate(cal);
131 int week = WeekPicker.getWeekForDate(cal);
132 showPickerDialog(dialogType, year, 0, 0, 0, 0, 0, 0, week, min, max, step);
136 void showSuggestionDialog(final int dialogType,
137 final double dialogValue,
138 final double min, final double max, final double step,
139 DateTimeSuggestion[] suggestions) {
140 ListView suggestionListView = new ListView(mContext);
141 final DateTimeSuggestionListAdapter adapter =
142 new DateTimeSuggestionListAdapter(mContext, Arrays.asList(suggestions));
143 suggestionListView.setAdapter(adapter);
144 suggestionListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
146 public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
147 if (position == adapter.getCount() - 1) {
149 showPickerDialog(dialogType, dialogValue, min, max, step);
151 double suggestionValue = adapter.getItem(position).value();
152 mInputActionDelegate.replaceDateTime(suggestionValue);
154 mDialogAlreadyDismissed = true;
159 int dialogTitleId = R.string.date_picker_dialog_title;
160 if (dialogType == sTextInputTypeTime) {
161 dialogTitleId = R.string.time_picker_dialog_title;
162 } else if (dialogType == sTextInputTypeDateTime ||
163 dialogType == sTextInputTypeDateTimeLocal) {
164 dialogTitleId = R.string.date_time_picker_dialog_title;
165 } else if (dialogType == sTextInputTypeMonth) {
166 dialogTitleId = R.string.month_picker_dialog_title;
167 } else if (dialogType == sTextInputTypeWeek) {
168 dialogTitleId = R.string.week_picker_dialog_title;
171 mDialog = new AlertDialog.Builder(mContext)
172 .setTitle(dialogTitleId)
173 .setView(suggestionListView)
174 .setNegativeButton(mContext.getText(android.R.string.cancel),
175 new DialogInterface.OnClickListener() {
177 public void onClick(DialogInterface dialog, int which) {
183 mDialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
185 public void onDismiss(DialogInterface dialog) {
186 if (mDialog == dialog && !mDialogAlreadyDismissed) {
187 mDialogAlreadyDismissed = true;
188 mInputActionDelegate.cancelDateTimeDialog();
192 mDialogAlreadyDismissed = false;
196 void showDialog(final int type, final double value,
197 double min, double max, double step,
198 DateTimeSuggestion[] suggestions) {
199 // When the web page asks to show a dialog while there is one already open,
200 // dismiss the old one.
202 if (suggestions == null) {
203 showPickerDialog(type, value, min, max, step);
205 showSuggestionDialog(type, value, min, max, step, suggestions);
209 void showPickerDialog(final int dialogType,
210 int year, int month, int monthDay,
211 int hourOfDay, int minute, int second, int millis, int week,
212 double min, double max, double step) {
213 if (isDialogShowing()) mDialog.dismiss();
215 int stepTime = (int) step;
217 if (dialogType == sTextInputTypeDate) {
218 ChromeDatePickerDialog dialog = new ChromeDatePickerDialog(mContext,
219 new DateListener(dialogType),
220 year, month, monthDay);
221 DateDialogNormalizer.normalize(dialog.getDatePicker(), dialog,
222 year, month, monthDay,
224 (long) min, (long) max);
226 dialog.setTitle(mContext.getText(R.string.date_picker_dialog_title));
228 } else if (dialogType == sTextInputTypeTime) {
229 mDialog = new MultiFieldTimePickerDialog(
230 mContext, 0 /* theme */ ,
231 hourOfDay, minute, second, millis,
232 (int) min, (int) max, stepTime,
233 DateFormat.is24HourFormat(mContext),
234 new FullTimeListener(dialogType));
235 } else if (dialogType == sTextInputTypeDateTime ||
236 dialogType == sTextInputTypeDateTimeLocal) {
237 mDialog = new DateTimePickerDialog(mContext,
238 new DateTimeListener(dialogType),
239 year, month, monthDay,
241 DateFormat.is24HourFormat(mContext), min, max);
242 } else if (dialogType == sTextInputTypeMonth) {
243 mDialog = new MonthPickerDialog(mContext, new MonthOrWeekListener(dialogType),
244 year, month, min, max);
245 } else if (dialogType == sTextInputTypeWeek) {
246 mDialog = new WeekPickerDialog(mContext, new MonthOrWeekListener(dialogType),
247 year, week, min, max);
250 mDialog.setButton(DialogInterface.BUTTON_POSITIVE,
251 mContext.getText(R.string.date_picker_dialog_set),
252 (DialogInterface.OnClickListener) mDialog);
255 mDialog.setButton(DialogInterface.BUTTON_NEGATIVE,
256 mContext.getText(android.R.string.cancel),
257 (DialogInterface.OnClickListener) null);
259 mDialog.setButton(DialogInterface.BUTTON_NEUTRAL,
260 mContext.getText(R.string.date_picker_dialog_clear),
261 new DialogInterface.OnClickListener() {
263 public void onClick(DialogInterface dialog, int which) {
264 mDialogAlreadyDismissed = true;
265 mInputActionDelegate.replaceDateTime(Double.NaN);
269 mDialog.setOnDismissListener(
270 new OnDismissListener() {
272 public void onDismiss(final DialogInterface dialog) {
273 if (!mDialogAlreadyDismissed) {
274 mDialogAlreadyDismissed = true;
275 mInputActionDelegate.cancelDateTimeDialog();
280 mDialogAlreadyDismissed = false;
284 boolean isDialogShowing() {
285 return mDialog != null && mDialog.isShowing();
288 void dismissDialog() {
289 if (isDialogShowing()) mDialog.dismiss();
292 private class DateListener implements OnDateSetListener {
293 private final int mDialogType;
295 DateListener(int dialogType) {
296 mDialogType = dialogType;
300 public void onDateSet(DatePicker view, int year, int month, int monthDay) {
301 setFieldDateTimeValue(mDialogType, year, month, monthDay, 0, 0, 0, 0, 0);
305 private class FullTimeListener implements OnMultiFieldTimeSetListener {
306 private final int mDialogType;
307 FullTimeListener(int dialogType) {
308 mDialogType = dialogType;
312 public void onTimeSet(int hourOfDay, int minute, int second, int milli) {
313 setFieldDateTimeValue(mDialogType, 0, 0, 0, hourOfDay, minute, second, milli, 0);
317 private class DateTimeListener implements OnDateTimeSetListener {
318 private final boolean mLocal;
319 private final int mDialogType;
321 public DateTimeListener(int dialogType) {
322 mLocal = dialogType == sTextInputTypeDateTimeLocal;
323 mDialogType = dialogType;
327 public void onDateTimeSet(DatePicker dateView, TimePicker timeView,
328 int year, int month, int monthDay,
329 int hourOfDay, int minute) {
330 setFieldDateTimeValue(mDialogType, year, month, monthDay, hourOfDay, minute, 0, 0, 0);
334 private class MonthOrWeekListener implements TwoFieldDatePickerDialog.OnValueSetListener {
335 private final int mDialogType;
337 MonthOrWeekListener(int dialogType) {
338 mDialogType = dialogType;
342 public void onValueSet(int year, int positionInYear) {
343 if (mDialogType == sTextInputTypeMonth) {
344 setFieldDateTimeValue(mDialogType, year, positionInYear, 0, 0, 0, 0, 0, 0);
346 setFieldDateTimeValue(mDialogType, year, 0, 0, 0, 0, 0, 0, positionInYear);
351 protected void setFieldDateTimeValue(int dialogType,
352 int year, int month, int monthDay,
353 int hourOfDay, int minute, int second, int millis,
355 // Prevents more than one callback being sent to the native
356 // side when the dialog triggers multiple events.
357 if (mDialogAlreadyDismissed)
359 mDialogAlreadyDismissed = true;
361 if (dialogType == sTextInputTypeMonth) {
362 mInputActionDelegate.replaceDateTime((year - 1970) * 12 + month);
363 } else if (dialogType == sTextInputTypeWeek) {
364 mInputActionDelegate.replaceDateTime(
365 WeekPicker.createDateFromWeek(year, week).getTimeInMillis());
366 } else if (dialogType == sTextInputTypeTime) {
367 mInputActionDelegate.replaceDateTime(TimeUnit.HOURS.toMillis(hourOfDay) +
368 TimeUnit.MINUTES.toMillis(minute) +
369 TimeUnit.SECONDS.toMillis(second) +
372 Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
374 cal.set(Calendar.YEAR, year);
375 cal.set(Calendar.MONTH, month);
376 cal.set(Calendar.DAY_OF_MONTH, monthDay);
377 cal.set(Calendar.HOUR_OF_DAY, hourOfDay);
378 cal.set(Calendar.MINUTE, minute);
379 cal.set(Calendar.SECOND, second);
380 cal.set(Calendar.MILLISECOND, millis);
381 mInputActionDelegate.replaceDateTime(cal.getTimeInMillis());