44bf207b3b6d7f2de6523420c20ebb2ef1b67fa4
[platform/framework/web/crosswalk.git] / src / content / public / android / java / src / org / chromium / content / browser / input / InputDialogContainer.java
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.content.R;
20 import org.chromium.content.browser.input.DateTimePickerDialog.OnDateTimeSetListener;
21 import org.chromium.content.browser.input.MultiFieldTimePickerDialog.OnMultiFieldTimeSetListener;
22
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;
29
30 /**
31  * Opens the approprate date/time picker dialog for the given dialog type.
32  */
33 public class InputDialogContainer {
34
35     interface InputActionDelegate {
36         void cancelDateTimeDialog();
37         void replaceDateTime(double value);
38     }
39
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;
46
47     private final Context mContext;
48
49     // Prevents sending two notifications (from onClick and from onDismiss)
50     private boolean mDialogAlreadyDismissed;
51
52     private AlertDialog mDialog;
53     private final InputActionDelegate mInputActionDelegate;
54
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;
65     }
66
67     static boolean isDialogInputType(int type) {
68         return type == sTextInputTypeDate || type == sTextInputTypeTime
69                 || type == sTextInputTypeDateTime || type == sTextInputTypeDateTimeLocal
70                 || type == sTextInputTypeMonth || type == sTextInputTypeWeek;
71     }
72
73     InputDialogContainer(Context context, InputActionDelegate inputActionDelegate) {
74         mContext = context;
75         mInputActionDelegate = inputActionDelegate;
76     }
77
78     void showPickerDialog(final int dialogType, double dialogValue,
79         double min, double max, double step) {
80         Calendar cal;
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);
89         } else {
90             if (dialogType == sTextInputTypeMonth) {
91                 cal = MonthPicker.createDateFromValue(dialogValue);
92             } else if (dialogType == sTextInputTypeWeek) {
93                 cal = WeekPicker.createDateFromValue(dialogValue);
94             } else {
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;
102             }
103         }
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),
125                     0, min, max, step);
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);
133         }
134     }
135
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() {
145             @Override
146             public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
147                 if (position == adapter.getCount() - 1) {
148                     dismissDialog();
149                     showPickerDialog(dialogType, dialogValue, min, max, step);
150                 } else {
151                     double suggestionValue = adapter.getItem(position).value();
152                     mInputActionDelegate.replaceDateTime(suggestionValue);
153                     dismissDialog();
154                     mDialogAlreadyDismissed = true;
155                 }
156             }
157         });
158
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;
169         }
170
171         mDialog = new AlertDialog.Builder(mContext)
172             .setTitle(dialogTitleId)
173             .setView(suggestionListView)
174             .setNegativeButton(mContext.getText(android.R.string.cancel),
175                 new DialogInterface.OnClickListener() {
176                     @Override
177                     public void onClick(DialogInterface dialog, int which) {
178                         dismissDialog();
179                     }
180                 })
181             .create();
182
183         mDialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
184             @Override
185             public void onDismiss(DialogInterface dialog) {
186                 if (mDialog == dialog && !mDialogAlreadyDismissed) {
187                     mDialogAlreadyDismissed = true;
188                     mInputActionDelegate.cancelDateTimeDialog();
189                 }
190             }
191         });
192         mDialogAlreadyDismissed = false;
193         mDialog.show();
194     }
195
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.
201         dismissDialog();
202         if (suggestions == null) {
203             showPickerDialog(type, value, min, max, step);
204         } else {
205             showSuggestionDialog(type, value, min, max, step, suggestions);
206         }
207     }
208
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();
214
215         int stepTime = (int) step;
216
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,
223                     0, 0,
224                     (long) min, (long) max);
225
226             dialog.setTitle(mContext.getText(R.string.date_picker_dialog_title));
227             mDialog = dialog;
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,
240                     hourOfDay, minute,
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);
248         }
249
250         mDialog.setButton(DialogInterface.BUTTON_POSITIVE,
251                 mContext.getText(R.string.date_picker_dialog_set),
252                 (DialogInterface.OnClickListener) mDialog);
253
254         mDialog.setButton(DialogInterface.BUTTON_NEGATIVE,
255                 mContext.getText(android.R.string.cancel),
256                 (DialogInterface.OnClickListener) null);
257
258         mDialog.setButton(DialogInterface.BUTTON_NEUTRAL,
259                 mContext.getText(R.string.date_picker_dialog_clear),
260                 new DialogInterface.OnClickListener() {
261                     @Override
262                     public void onClick(DialogInterface dialog, int which) {
263                         mDialogAlreadyDismissed = true;
264                         mInputActionDelegate.replaceDateTime(Double.NaN);
265                     }
266                 });
267
268         mDialog.setOnDismissListener(
269                 new OnDismissListener() {
270                     @Override
271                     public void onDismiss(final DialogInterface dialog) {
272                         if (!mDialogAlreadyDismissed) {
273                             mDialogAlreadyDismissed = true;
274                             mInputActionDelegate.cancelDateTimeDialog();
275                         }
276                     }
277                 });
278
279         mDialogAlreadyDismissed = false;
280         mDialog.show();
281     }
282
283     boolean isDialogShowing() {
284         return mDialog != null && mDialog.isShowing();
285     }
286
287     void dismissDialog() {
288         if (isDialogShowing()) mDialog.dismiss();
289     }
290
291     private class DateListener implements OnDateSetListener {
292         private final int mDialogType;
293
294         DateListener(int dialogType) {
295             mDialogType = dialogType;
296         }
297
298         @Override
299         public void onDateSet(DatePicker view, int year, int month, int monthDay) {
300             setFieldDateTimeValue(mDialogType, year, month, monthDay, 0, 0, 0, 0, 0);
301         }
302     }
303
304     private class FullTimeListener implements OnMultiFieldTimeSetListener {
305         private final int mDialogType;
306         FullTimeListener(int dialogType) {
307             mDialogType = dialogType;
308         }
309
310         @Override
311         public void onTimeSet(int hourOfDay, int minute, int second, int milli) {
312             setFieldDateTimeValue(mDialogType, 0, 0, 0, hourOfDay, minute, second, milli, 0);
313         }
314     }
315
316     private class DateTimeListener implements OnDateTimeSetListener {
317         private final boolean mLocal;
318         private final int mDialogType;
319
320         public DateTimeListener(int dialogType) {
321             mLocal = dialogType == sTextInputTypeDateTimeLocal;
322             mDialogType = dialogType;
323         }
324
325         @Override
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);
330         }
331     }
332
333     private class MonthOrWeekListener implements TwoFieldDatePickerDialog.OnValueSetListener {
334         private final int mDialogType;
335
336         MonthOrWeekListener(int dialogType) {
337             mDialogType = dialogType;
338         }
339
340         @Override
341         public void onValueSet(int year, int positionInYear) {
342             if (mDialogType == sTextInputTypeMonth) {
343                 setFieldDateTimeValue(mDialogType, year, positionInYear, 0, 0, 0, 0, 0, 0);
344             } else {
345                 setFieldDateTimeValue(mDialogType, year, 0, 0, 0, 0, 0, 0, positionInYear);
346             }
347         }
348     }
349
350     protected void setFieldDateTimeValue(int dialogType,
351                                        int year, int month, int monthDay,
352                                        int hourOfDay, int minute, int second, int millis,
353                                        int week) {
354         // Prevents more than one callback being sent to the native
355         // side when the dialog triggers multiple events.
356         if (mDialogAlreadyDismissed)
357             return;
358         mDialogAlreadyDismissed = true;
359
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) +
369                                                  millis);
370         } else {
371             Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
372             cal.clear();
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());
381         }
382     }
383 }