Upstream version 5.34.92.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;
9 import android.app.DatePickerDialog.OnDateSetListener;
10 import android.content.Context;
11 import android.content.DialogInterface;
12 import android.content.DialogInterface.OnDismissListener;
13 import android.text.format.DateFormat;
14 import android.view.View;
15 import android.widget.AdapterView;
16 import android.widget.DatePicker;
17 import android.widget.ListView;
18 import android.widget.TimePicker;
19
20 import org.chromium.content.R;
21 import org.chromium.content.browser.input.DateTimePickerDialog.OnDateTimeSetListener;
22 import org.chromium.content.browser.input.MultiFieldTimePickerDialog.OnMultiFieldTimeSetListener;
23
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;
30
31 /**
32  * Opens the approprate date/time picker dialog for the given dialog type.
33  */
34 public class InputDialogContainer {
35
36     interface InputActionDelegate {
37         void cancelDateTimeDialog();
38         void replaceDateTime(double value);
39     }
40
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;
47
48     private final Context mContext;
49
50     // Prevents sending two notifications (from onClick and from onDismiss)
51     private boolean mDialogAlreadyDismissed;
52
53     private AlertDialog mDialog;
54     private final InputActionDelegate mInputActionDelegate;
55
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;
66     }
67
68     static boolean isDialogInputType(int type) {
69         return type == sTextInputTypeDate || type == sTextInputTypeTime
70                 || type == sTextInputTypeDateTime || type == sTextInputTypeDateTimeLocal
71                 || type == sTextInputTypeMonth || type == sTextInputTypeWeek;
72     }
73
74     InputDialogContainer(Context context, InputActionDelegate inputActionDelegate) {
75         mContext = context;
76         mInputActionDelegate = inputActionDelegate;
77     }
78
79     void showPickerDialog(final int dialogType, double dialogValue,
80         double min, double max, double step) {
81         Calendar cal;
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);
90         } else {
91             if (dialogType == sTextInputTypeMonth) {
92                 cal = MonthPicker.createDateFromValue(dialogValue);
93             } else if (dialogType == sTextInputTypeWeek) {
94                 cal = WeekPicker.createDateFromValue(dialogValue);
95             } else {
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;
103             }
104         }
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),
126                     0, min, max, step);
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);
134         }
135     }
136
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() {
146             @Override
147             public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
148                 if (position == adapter.getCount() - 1) {
149                     dismissDialog();
150                     showPickerDialog(dialogType, dialogValue, min, max, step);
151                 } else {
152                     double suggestionValue = adapter.getItem(position).value();
153                     mInputActionDelegate.replaceDateTime(suggestionValue);
154                     dismissDialog();
155                     mDialogAlreadyDismissed = true;
156                 }
157             }
158         });
159
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;
170         }
171
172         mDialog = new AlertDialog.Builder(mContext)
173             .setTitle(dialogTitleId)
174             .setView(suggestionListView)
175             .setNegativeButton(mContext.getText(android.R.string.cancel),
176                 new DialogInterface.OnClickListener() {
177                     @Override
178                     public void onClick(DialogInterface dialog, int which) {
179                         dismissDialog();
180                     }
181                 })
182             .create();
183
184         mDialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
185             @Override
186             public void onDismiss(DialogInterface dialog) {
187                 if (mDialog == dialog && !mDialogAlreadyDismissed) {
188                     mDialogAlreadyDismissed = true;
189                     mInputActionDelegate.cancelDateTimeDialog();
190                 }
191             }
192         });
193         mDialogAlreadyDismissed = false;
194         mDialog.show();
195     }
196
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.
202         dismissDialog();
203         if (suggestions == null) {
204             showPickerDialog(type, value, min, max, step);
205         } else {
206             showSuggestionDialog(type, value, min, max, step, suggestions);
207         }
208     }
209
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();
215
216         int stepTime = (int) step;
217
218         if (dialogType == sTextInputTypeDate) {
219             DatePickerDialog dialog = new DatePickerDialog(mContext,
220                     new DateListener(dialogType),
221                     year, month, monthDay);
222             DateDialogNormalizer.normalize(dialog.getDatePicker(), dialog,
223                     year, month, monthDay,
224                     0, 0,
225                     (long) min, (long) max);
226
227             dialog.setTitle(mContext.getText(R.string.date_picker_dialog_title));
228             mDialog = dialog;
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,
241                     hourOfDay, minute,
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);
249         }
250
251         mDialog.setButton(DialogInterface.BUTTON_POSITIVE,
252                 mContext.getText(R.string.date_picker_dialog_set),
253                 (DialogInterface.OnClickListener) mDialog);
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 }