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.ui.picker;
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.ui.R;
20 import org.chromium.ui.picker.DateTimePickerDialog.OnDateTimeSetListener;
21 import org.chromium.ui.picker.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 appropriate date/time picker dialog for the given dialog type.
33 public class InputDialogContainer {
35 public 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 public 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 public static boolean isDialogInputType(int type) {
68 return type == sTextInputTypeDate || type == sTextInputTypeTime
69 || type == sTextInputTypeDateTime || type == sTextInputTypeDateTimeLocal
70 || type == sTextInputTypeMonth || type == sTextInputTypeWeek;
73 public InputDialogContainer(Context context, InputActionDelegate inputActionDelegate) {
75 mInputActionDelegate = inputActionDelegate;
78 public 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 public 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 protected 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);
254 mDialog.setButton(DialogInterface.BUTTON_NEGATIVE,
255 mContext.getText(android.R.string.cancel),
256 (DialogInterface.OnClickListener) null);
258 mDialog.setButton(DialogInterface.BUTTON_NEUTRAL,
259 mContext.getText(R.string.date_picker_dialog_clear),
260 new DialogInterface.OnClickListener() {
262 public void onClick(DialogInterface dialog, int which) {
263 mDialogAlreadyDismissed = true;
264 mInputActionDelegate.replaceDateTime(Double.NaN);
268 mDialog.setOnDismissListener(
269 new OnDismissListener() {
271 public void onDismiss(final DialogInterface dialog) {
272 if (!mDialogAlreadyDismissed) {
273 mDialogAlreadyDismissed = true;
274 mInputActionDelegate.cancelDateTimeDialog();
279 mDialogAlreadyDismissed = false;
283 boolean isDialogShowing() {
284 return mDialog != null && mDialog.isShowing();
287 void dismissDialog() {
288 if (isDialogShowing()) mDialog.dismiss();
291 private class DateListener implements OnDateSetListener {
292 private final int mDialogType;
294 DateListener(int dialogType) {
295 mDialogType = dialogType;
299 public void onDateSet(DatePicker view, int year, int month, int monthDay) {
300 setFieldDateTimeValue(mDialogType, year, month, monthDay, 0, 0, 0, 0, 0);
304 private class FullTimeListener implements OnMultiFieldTimeSetListener {
305 private final int mDialogType;
306 FullTimeListener(int dialogType) {
307 mDialogType = dialogType;
311 public void onTimeSet(int hourOfDay, int minute, int second, int milli) {
312 setFieldDateTimeValue(mDialogType, 0, 0, 0, hourOfDay, minute, second, milli, 0);
316 private class DateTimeListener implements OnDateTimeSetListener {
317 private final boolean mLocal;
318 private final int mDialogType;
320 public DateTimeListener(int dialogType) {
321 mLocal = dialogType == sTextInputTypeDateTimeLocal;
322 mDialogType = dialogType;
326 public void onDateTimeSet(DatePicker dateView, TimePicker timeView,
327 int year, int month, int monthDay,
328 int hourOfDay, int minute) {
329 setFieldDateTimeValue(mDialogType, year, month, monthDay, hourOfDay, minute, 0, 0, 0);
333 private class MonthOrWeekListener implements TwoFieldDatePickerDialog.OnValueSetListener {
334 private final int mDialogType;
336 MonthOrWeekListener(int dialogType) {
337 mDialogType = dialogType;
341 public void onValueSet(int year, int positionInYear) {
342 if (mDialogType == sTextInputTypeMonth) {
343 setFieldDateTimeValue(mDialogType, year, positionInYear, 0, 0, 0, 0, 0, 0);
345 setFieldDateTimeValue(mDialogType, year, 0, 0, 0, 0, 0, 0, positionInYear);
350 protected void setFieldDateTimeValue(int dialogType,
351 int year, int month, int monthDay,
352 int hourOfDay, int minute, int second, int millis,
354 // Prevents more than one callback being sent to the native
355 // side when the dialog triggers multiple events.
356 if (mDialogAlreadyDismissed)
358 mDialogAlreadyDismissed = true;
360 if (dialogType == sTextInputTypeMonth) {
361 mInputActionDelegate.replaceDateTime((year - 1970) * 12 + month);
362 } else if (dialogType == sTextInputTypeWeek) {
363 mInputActionDelegate.replaceDateTime(
364 WeekPicker.createDateFromWeek(year, week).getTimeInMillis());
365 } else if (dialogType == sTextInputTypeTime) {
366 mInputActionDelegate.replaceDateTime(TimeUnit.HOURS.toMillis(hourOfDay) +
367 TimeUnit.MINUTES.toMillis(minute) +
368 TimeUnit.SECONDS.toMillis(second) +
371 Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
373 cal.set(Calendar.YEAR, year);
374 cal.set(Calendar.MONTH, month);
375 cal.set(Calendar.DAY_OF_MONTH, monthDay);
376 cal.set(Calendar.HOUR_OF_DAY, hourOfDay);
377 cal.set(Calendar.MINUTE, minute);
378 cal.set(Calendar.SECOND, second);
379 cal.set(Calendar.MILLISECOND, millis);
380 mInputActionDelegate.replaceDateTime(cal.getTimeInMillis());