Upstream version 10.38.222.0
[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
255         mDialog.setButton(DialogInterface.BUTTON_NEGATIVE,
256                 mContext.getText(android.R.string.cancel),
257                 (DialogInterface.OnClickListener) null);
258
259         mDialog.setButton(DialogInterface.BUTTON_NEUTRAL,
260                 mContext.getText(R.string.date_picker_dialog_clear),
261                 new DialogInterface.OnClickListener() {
262                     @Override
263                     public void onClick(DialogInterface dialog, int which) {
264                         mDialogAlreadyDismissed = true;
265                         mInputActionDelegate.replaceDateTime(Double.NaN);
266                     }
267                 });
268
269         mDialog.setOnDismissListener(
270                 new OnDismissListener() {
271                     @Override
272                     public void onDismiss(final DialogInterface dialog) {
273                         if (!mDialogAlreadyDismissed) {
274                             mDialogAlreadyDismissed = true;
275                             mInputActionDelegate.cancelDateTimeDialog();
276                         }
277                     }
278                 });
279
280         mDialogAlreadyDismissed = false;
281         mDialog.show();
282     }
283
284     boolean isDialogShowing() {
285         return mDialog != null && mDialog.isShowing();
286     }
287
288     void dismissDialog() {
289         if (isDialogShowing()) mDialog.dismiss();
290     }
291
292     private class DateListener implements OnDateSetListener {
293         private final int mDialogType;
294
295         DateListener(int dialogType) {
296             mDialogType = dialogType;
297         }
298
299         @Override
300         public void onDateSet(DatePicker view, int year, int month, int monthDay) {
301             setFieldDateTimeValue(mDialogType, year, month, monthDay, 0, 0, 0, 0, 0);
302         }
303     }
304
305     private class FullTimeListener implements OnMultiFieldTimeSetListener {
306         private final int mDialogType;
307         FullTimeListener(int dialogType) {
308             mDialogType = dialogType;
309         }
310
311         @Override
312         public void onTimeSet(int hourOfDay, int minute, int second, int milli) {
313             setFieldDateTimeValue(mDialogType, 0, 0, 0, hourOfDay, minute, second, milli, 0);
314         }
315     }
316
317     private class DateTimeListener implements OnDateTimeSetListener {
318         private final boolean mLocal;
319         private final int mDialogType;
320
321         public DateTimeListener(int dialogType) {
322             mLocal = dialogType == sTextInputTypeDateTimeLocal;
323             mDialogType = dialogType;
324         }
325
326         @Override
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);
331         }
332     }
333
334     private class MonthOrWeekListener implements TwoFieldDatePickerDialog.OnValueSetListener {
335         private final int mDialogType;
336
337         MonthOrWeekListener(int dialogType) {
338             mDialogType = dialogType;
339         }
340
341         @Override
342         public void onValueSet(int year, int positionInYear) {
343             if (mDialogType == sTextInputTypeMonth) {
344                 setFieldDateTimeValue(mDialogType, year, positionInYear, 0, 0, 0, 0, 0, 0);
345             } else {
346                 setFieldDateTimeValue(mDialogType, year, 0, 0, 0, 0, 0, 0, positionInYear);
347             }
348         }
349     }
350
351     protected void setFieldDateTimeValue(int dialogType,
352                                        int year, int month, int monthDay,
353                                        int hourOfDay, int minute, int second, int millis,
354                                        int week) {
355         // Prevents more than one callback being sent to the native
356         // side when the dialog triggers multiple events.
357         if (mDialogAlreadyDismissed)
358             return;
359         mDialogAlreadyDismissed = true;
360
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) +
370                                                  millis);
371         } else {
372             Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
373             cal.clear();
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());
382         }
383     }
384 }