Upstream version 9.37.195.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.base.ApiCompatibilityUtils;
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             ChromeDatePickerDialog dialog = new ChromeDatePickerDialog(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         if (ApiCompatibilityUtils.datePickerRequiresAccept()) {
251             mDialog.setButton(DialogInterface.BUTTON_POSITIVE,
252                     mContext.getText(R.string.date_picker_dialog_set),
253                     (DialogInterface.OnClickListener) mDialog);
254         }
255
256         mDialog.setButton(DialogInterface.BUTTON_NEGATIVE,
257                 mContext.getText(android.R.string.cancel),
258                 (DialogInterface.OnClickListener) null);
259
260         mDialog.setButton(DialogInterface.BUTTON_NEUTRAL,
261                 mContext.getText(R.string.date_picker_dialog_clear),
262                 new DialogInterface.OnClickListener() {
263                     @Override
264                     public void onClick(DialogInterface dialog, int which) {
265                         mDialogAlreadyDismissed = true;
266                         mInputActionDelegate.replaceDateTime(Double.NaN);
267                     }
268                 });
269
270         mDialog.setOnDismissListener(
271                 new OnDismissListener() {
272                     @Override
273                     public void onDismiss(final DialogInterface dialog) {
274                         if (!mDialogAlreadyDismissed) {
275                             mDialogAlreadyDismissed = true;
276                             mInputActionDelegate.cancelDateTimeDialog();
277                         }
278                     }
279                 });
280
281         mDialogAlreadyDismissed = false;
282         mDialog.show();
283     }
284
285     boolean isDialogShowing() {
286         return mDialog != null && mDialog.isShowing();
287     }
288
289     void dismissDialog() {
290         if (isDialogShowing()) mDialog.dismiss();
291     }
292
293     private class DateListener implements OnDateSetListener {
294         private final int mDialogType;
295
296         DateListener(int dialogType) {
297             mDialogType = dialogType;
298         }
299
300         @Override
301         public void onDateSet(DatePicker view, int year, int month, int monthDay) {
302             setFieldDateTimeValue(mDialogType, year, month, monthDay, 0, 0, 0, 0, 0);
303         }
304     }
305
306     private class FullTimeListener implements OnMultiFieldTimeSetListener {
307         private final int mDialogType;
308         FullTimeListener(int dialogType) {
309             mDialogType = dialogType;
310         }
311
312         @Override
313         public void onTimeSet(int hourOfDay, int minute, int second, int milli) {
314             setFieldDateTimeValue(mDialogType, 0, 0, 0, hourOfDay, minute, second, milli, 0);
315         }
316     }
317
318     private class DateTimeListener implements OnDateTimeSetListener {
319         private final boolean mLocal;
320         private final int mDialogType;
321
322         public DateTimeListener(int dialogType) {
323             mLocal = dialogType == sTextInputTypeDateTimeLocal;
324             mDialogType = dialogType;
325         }
326
327         @Override
328         public void onDateTimeSet(DatePicker dateView, TimePicker timeView,
329                 int year, int month, int monthDay,
330                 int hourOfDay, int minute) {
331             setFieldDateTimeValue(mDialogType, year, month, monthDay, hourOfDay, minute, 0, 0, 0);
332         }
333     }
334
335     private class MonthOrWeekListener implements TwoFieldDatePickerDialog.OnValueSetListener {
336         private final int mDialogType;
337
338         MonthOrWeekListener(int dialogType) {
339             mDialogType = dialogType;
340         }
341
342         @Override
343         public void onValueSet(int year, int positionInYear) {
344             if (mDialogType == sTextInputTypeMonth) {
345                 setFieldDateTimeValue(mDialogType, year, positionInYear, 0, 0, 0, 0, 0, 0);
346             } else {
347                 setFieldDateTimeValue(mDialogType, year, 0, 0, 0, 0, 0, 0, positionInYear);
348             }
349         }
350     }
351
352     protected void setFieldDateTimeValue(int dialogType,
353                                        int year, int month, int monthDay,
354                                        int hourOfDay, int minute, int second, int millis,
355                                        int week) {
356         // Prevents more than one callback being sent to the native
357         // side when the dialog triggers multiple events.
358         if (mDialogAlreadyDismissed)
359             return;
360         mDialogAlreadyDismissed = true;
361
362         if (dialogType == sTextInputTypeMonth) {
363             mInputActionDelegate.replaceDateTime((year - 1970) * 12 + month);
364         } else if (dialogType == sTextInputTypeWeek) {
365             mInputActionDelegate.replaceDateTime(
366                   WeekPicker.createDateFromWeek(year, week).getTimeInMillis());
367         } else if (dialogType == sTextInputTypeTime) {
368             mInputActionDelegate.replaceDateTime(TimeUnit.HOURS.toMillis(hourOfDay) +
369                                                  TimeUnit.MINUTES.toMillis(minute) +
370                                                  TimeUnit.SECONDS.toMillis(second) +
371                                                  millis);
372         } else {
373             Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
374             cal.clear();
375             cal.set(Calendar.YEAR, year);
376             cal.set(Calendar.MONTH, month);
377             cal.set(Calendar.DAY_OF_MONTH, monthDay);
378             cal.set(Calendar.HOUR_OF_DAY, hourOfDay);
379             cal.set(Calendar.MINUTE, minute);
380             cal.set(Calendar.SECOND, second);
381             cal.set(Calendar.MILLISECOND, millis);
382             mInputActionDelegate.replaceDateTime(cal.getTimeInMillis());
383         }
384     }
385 }