// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.content.browser.input;
import android.app.AlertDialog;
import android.app.DatePickerDialog;
import android.app.TimePickerDialog;
import android.app.DatePickerDialog.OnDateSetListener;
import android.app.TimePickerDialog.OnTimeSetListener;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnDismissListener;
import android.text.TextUtils;
import android.text.format.DateFormat;
import android.text.format.Time;
import android.widget.DatePicker;
import android.widget.TimePicker;
import org.chromium.content.browser.input.DateTimePickerDialog.OnDateTimeSetListener;
import org.chromium.content.browser.input.MultiFieldTimePickerDialog.OnMultiFieldTimeSetListener;
import org.chromium.content.browser.input.TwoFieldDatePickerDialog;
import org.chromium.content.R;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
public class InputDialogContainer {
interface InputActionDelegate {
void cancelDateTimeDialog();
void replaceDateTime(int dialogType,
int year, int month, int day, int hour, int minute, int second, int milli, int week);
}
// Default values used in Time representations of selected date/time before formatting.
// They are never displayed to the user.
private static final int YEAR_DEFAULT = 1970;
private static final int MONTH_DEFAULT = 0;
private static final int MONTHDAY_DEFAULT = 1;
private static final int HOUR_DEFAULT = 0;
private static final int MINUTE_DEFAULT = 0;
private static final int WEEK_DEFAULT = 0;
// Date formats as accepted by Time.format.
private static final String HTML_DATE_FORMAT = "%Y-%m-%d";
private static final String HTML_TIME_FORMAT = "%H:%M";
// For datetime we always send selected time as UTC, as we have no timezone selector.
// This is consistent with other browsers.
private static final String HTML_DATE_TIME_FORMAT = "%Y-%m-%dT%H:%MZ";
private static final String HTML_DATE_TIME_LOCAL_FORMAT = "%Y-%m-%dT%H:%M";
private static final String HTML_MONTH_FORMAT = "%Y-%m";
private static final String HTML_WEEK_FORMAT = "%Y-%w";
private static int sTextInputTypeDate;
private static int sTextInputTypeDateTime;
private static int sTextInputTypeDateTimeLocal;
private static int sTextInputTypeMonth;
private static int sTextInputTypeTime;
private static int sTextInputTypeWeek;
private Context mContext;
// Prevents sending two notifications (from onClick and from onDismiss)
private boolean mDialogAlreadyDismissed;
private AlertDialog mDialog;
private InputActionDelegate mInputActionDelegate;
static void initializeInputTypes(int textInputTypeDate,
int textInputTypeDateTime, int textInputTypeDateTimeLocal,
int textInputTypeMonth, int textInputTypeTime,
int textInputTypeWeek) {
sTextInputTypeDate = textInputTypeDate;
sTextInputTypeDateTime = textInputTypeDateTime;
sTextInputTypeDateTimeLocal = textInputTypeDateTimeLocal;
sTextInputTypeMonth = textInputTypeMonth;
sTextInputTypeTime = textInputTypeTime;
sTextInputTypeWeek = textInputTypeWeek;
}
static boolean isDialogInputType(int type) {
return type == sTextInputTypeDate || type == sTextInputTypeTime
|| type == sTextInputTypeDateTime || type == sTextInputTypeDateTimeLocal
|| type == sTextInputTypeMonth || type == sTextInputTypeWeek;
}
InputDialogContainer(Context context, InputActionDelegate inputActionDelegate) {
mContext = context;
mInputActionDelegate = inputActionDelegate;
}
private Time normalizeTime(int year, int month, int monthDay,
int hour, int minute, int second) {
Time result = new Time();
if (year == 0 && month == 0 && monthDay == 0 && hour == 0 &&
minute == 0 && second == 0) {
Calendar cal = Calendar.getInstance();
result.set(cal.get(Calendar.SECOND), cal.get(Calendar.MINUTE),
cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.DATE),
cal.get(Calendar.MONTH), cal.get(Calendar.YEAR));
} else {
result.set(second, minute, hour, monthDay, month, year);
}
return result;
}
void showDialog(final int dialogType, int year, int month, int monthDay,
int hour, int minute, int second, int milli, int week,
double min, double max, double step) {
if (isDialogShowing()) mDialog.dismiss();
// Java Date dialogs like longs but Blink prefers doubles..
// Both parameters mean different things depending on the type
// For input type=month min and max come as number on months since 1970
// For other types (including type=time) they are just milliseconds since 1970
// In any case the cast here is safe given the above restrictions.
long minTime = (long) min;
long maxTime = (long) max;
int stepTime = (int) step;
if (milli > 1000) {
second += milli / 1000;
milli %= 1000;
}
Time time = normalizeTime(year, month, monthDay, hour, minute, second);
if (dialogType == sTextInputTypeDate) {
DatePickerDialog dialog = new DatePickerDialog(mContext,
new DateListener(dialogType), time.year, time.month, time.monthDay);
DateDialogNormalizer.normalize(dialog.getDatePicker(), dialog,
time.year, time.month, time.monthDay, 0, 0, minTime, maxTime);
dialog.setTitle(mContext.getText(R.string.date_picker_dialog_title));
mDialog = dialog;
} else if (dialogType == sTextInputTypeTime) {
mDialog = new MultiFieldTimePickerDialog(
mContext, 0 /* theme */ ,
time.hour, time.minute, time.second, milli,
(int) minTime, (int) maxTime, stepTime,
DateFormat.is24HourFormat(mContext),
new FullTimeListener(dialogType));
} else if (dialogType == sTextInputTypeDateTime ||
dialogType == sTextInputTypeDateTimeLocal) {
mDialog = new DateTimePickerDialog(mContext,
new DateTimeListener(dialogType),
time.year, time.month, time.monthDay,
time.hour, time.minute, DateFormat.is24HourFormat(mContext),
minTime, maxTime);
} else if (dialogType == sTextInputTypeMonth) {
mDialog = new MonthPickerDialog(mContext, new MonthOrWeekListener(dialogType),
time.year, time.month, minTime, maxTime);
} else if (dialogType == sTextInputTypeWeek) {
if (week == 0) {
Calendar cal = Calendar.getInstance();
year = WeekPicker.getISOWeekYearForDate(cal);
week = WeekPicker.getWeekForDate(cal);
}
mDialog = new WeekPickerDialog(mContext, new MonthOrWeekListener(dialogType),
year, week, minTime, maxTime);
}
mDialog.setButton(DialogInterface.BUTTON_POSITIVE,
mContext.getText(R.string.date_picker_dialog_set),
(DialogInterface.OnClickListener) mDialog);
mDialog.setButton(DialogInterface.BUTTON_NEGATIVE,
mContext.getText(android.R.string.cancel),
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
mDialogAlreadyDismissed = true;
mInputActionDelegate.cancelDateTimeDialog();
}
});
mDialog.setButton(DialogInterface.BUTTON_NEUTRAL,
mContext.getText(R.string.date_picker_dialog_clear),
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
mDialogAlreadyDismissed = true;
mInputActionDelegate.replaceDateTime(dialogType, 0, 0, 0, 0, 0, 0, 0, 0);
}
});
mDialogAlreadyDismissed = false;
mDialog.show();
}
boolean isDialogShowing() {
return mDialog != null && mDialog.isShowing();
}
void dismissDialog() {
if (isDialogShowing()) mDialog.dismiss();
}
private class DateListener implements OnDateSetListener {
private final int mDialogType;
DateListener(int dialogType) {
mDialogType = dialogType;
}
@Override
public void onDateSet(DatePicker view, int year, int month, int monthDay) {
if (!mDialogAlreadyDismissed) {
setFieldDateTimeValue(mDialogType,
year, month, monthDay,
HOUR_DEFAULT, MINUTE_DEFAULT, WEEK_DEFAULT,
HTML_DATE_FORMAT);
}
}
}
private class TimeListener implements OnTimeSetListener {
private final int mDialogType;
TimeListener(int dialogType) {
mDialogType = dialogType;
}
@Override
public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
if (!mDialogAlreadyDismissed) {
setFieldDateTimeValue(mDialogType,
YEAR_DEFAULT, MONTH_DEFAULT, MONTHDAY_DEFAULT,
hourOfDay, minute, WEEK_DEFAULT, HTML_TIME_FORMAT);
}
}
}
private class FullTimeListener implements OnMultiFieldTimeSetListener {
private final int mDialogType;
FullTimeListener(int dialogType) {
mDialogType = dialogType;
}
@Override
public void onTimeSet(int hourOfDay, int minute, int second, int milli) {
if (!mDialogAlreadyDismissed) {
setFieldDateTimeValue(mDialogType,
YEAR_DEFAULT, MONTH_DEFAULT, MONTHDAY_DEFAULT,
hourOfDay, minute, second, milli, WEEK_DEFAULT, HTML_TIME_FORMAT);
}
}
}
private class DateTimeListener implements OnDateTimeSetListener {
private final boolean mLocal;
private final int mDialogType;
public DateTimeListener(int dialogType) {
mLocal = dialogType == sTextInputTypeDateTimeLocal;
mDialogType = dialogType;
}
@Override
public void onDateTimeSet(DatePicker dateView, TimePicker timeView,
int year, int month, int monthDay,
int hourOfDay, int minute) {
if (!mDialogAlreadyDismissed) {
setFieldDateTimeValue(mDialogType, year, month, monthDay,
hourOfDay, minute, WEEK_DEFAULT,
mLocal ? HTML_DATE_TIME_LOCAL_FORMAT : HTML_DATE_TIME_FORMAT);
}
}
}
private class MonthOrWeekListener implements TwoFieldDatePickerDialog.OnValueSetListener {
private final int mDialogType;
MonthOrWeekListener(int dialogType) {
mDialogType = dialogType;
}
@Override
public void onValueSet(int year, int positionInYear) {
if (!mDialogAlreadyDismissed) {
if (mDialogType == sTextInputTypeMonth) {
setFieldDateTimeValue(mDialogType, year, positionInYear, MONTHDAY_DEFAULT,
HOUR_DEFAULT, MINUTE_DEFAULT, WEEK_DEFAULT,
HTML_MONTH_FORMAT);
} else {
setFieldDateTimeValue(mDialogType, year, MONTH_DEFAULT, MONTHDAY_DEFAULT,
HOUR_DEFAULT, MINUTE_DEFAULT, positionInYear, HTML_WEEK_FORMAT);
}
}
}
}
private void setFieldDateTimeValue(int dialogType,
int year, int month, int monthDay, int hourOfDay,
int minute, int week, String dateFormat) {
// Prevents more than one callback being sent to the native
// side when the dialog triggers multiple events.
mDialogAlreadyDismissed = true;
mInputActionDelegate.replaceDateTime(dialogType,
year, month, monthDay, hourOfDay, minute, 0 /* second */, 0 /* milli */, week);
}
private void setFieldDateTimeValue(int dialogType,
int year, int month, int monthDay, int hourOfDay,
int minute, int second, int milli, int week, String dateFormat) {
// Prevents more than one callback being sent to the native
// side when the dialog triggers multiple events.
mDialogAlreadyDismissed = true;
mInputActionDelegate.replaceDateTime(
dialogType, year, month, monthDay, hourOfDay, minute, second, milli, week);
}
}