From a91f4578e0de51430919d2982b906e59dcb227d3 Mon Sep 17 00:00:00 2001 From: "ankit2.kumar" Date: Wed, 24 Sep 2014 21:20:47 -0700 Subject: Move *Picker.java and *PickerDialog.java to ui/android/java/... There is no reason for the following files to live in content/ layer and they should all be moved to ui/ layer. BUG=302114 Review URL: https://codereview.chromium.org/574673003 Cr-Commit-Position: refs/heads/master@{#296637} --- .../java/res/layout/date_time_picker_dialog.xml | 33 -- .../res/layout/multi_field_time_picker_dialog.xml | 122 ------- .../java/res/layout/two_field_date_picker.xml | 48 --- .../java/resource_map/org/chromium/content/R.java | 14 - .../browser/input/ChromeDatePickerDialog.java | 42 --- .../browser/input/DateDialogNormalizer.java | 77 ----- .../browser/input/DateTimeChooserAndroid.java | 2 + .../browser/input/DateTimePickerDialog.java | 147 -------- .../content/browser/input/DateTimeSuggestion.java | 59 ---- .../input/DateTimeSuggestionListAdapter.java | 54 --- .../chromium/content/browser/input/ImeAdapter.java | 1 + .../browser/input/InputDialogContainer.java | 384 --------------------- .../content/browser/input/MonthPicker.java | 117 ------- .../content/browser/input/MonthPickerDialog.java | 38 -- .../browser/input/MultiFieldTimePickerDialog.java | 275 --------------- .../content/browser/input/TwoFieldDatePicker.java | 250 -------------- .../browser/input/TwoFieldDatePickerDialog.java | 113 ------ .../chromium/content/browser/input/WeekPicker.java | 141 -------- .../content/browser/input/WeekPickerDialog.java | 56 --- .../java/strings/android_content_strings.grd | 69 ---- .../browser/input/InputDialogContainerTest.java | 3 +- .../java/res/layout/date_time_picker_dialog.xml | 33 ++ .../res/layout/multi_field_time_picker_dialog.xml | 122 +++++++ .../java/res/layout/two_field_date_picker.xml | 48 +++ .../java/resource_map/org/chromium/ui/R.java | 10 + .../chromium/ui/picker/ChromeDatePickerDialog.java | 42 +++ .../chromium/ui/picker/DateDialogNormalizer.java | 77 +++++ .../chromium/ui/picker/DateTimePickerDialog.java | 147 ++++++++ .../org/chromium/ui/picker/DateTimeSuggestion.java | 59 ++++ .../ui/picker/DateTimeSuggestionListAdapter.java | 54 +++ .../chromium/ui/picker/InputDialogContainer.java | 383 ++++++++++++++++++++ .../src/org/chromium/ui/picker/MonthPicker.java | 117 +++++++ .../org/chromium/ui/picker/MonthPickerDialog.java | 38 ++ .../ui/picker/MultiFieldTimePickerDialog.java | 275 +++++++++++++++ ui/android/java/src/org/chromium/ui/picker/OWNERS | 2 + .../org/chromium/ui/picker/TwoFieldDatePicker.java | 250 ++++++++++++++ .../ui/picker/TwoFieldDatePickerDialog.java | 113 ++++++ .../src/org/chromium/ui/picker/WeekPicker.java | 141 ++++++++ .../org/chromium/ui/picker/WeekPickerDialog.java | 56 +++ ui/android/java/strings/android_ui_strings.grd | 69 ++++ 40 files changed, 2041 insertions(+), 2040 deletions(-) delete mode 100644 content/public/android/java/res/layout/date_time_picker_dialog.xml delete mode 100644 content/public/android/java/res/layout/multi_field_time_picker_dialog.xml delete mode 100644 content/public/android/java/res/layout/two_field_date_picker.xml delete mode 100644 content/public/android/java/src/org/chromium/content/browser/input/ChromeDatePickerDialog.java delete mode 100644 content/public/android/java/src/org/chromium/content/browser/input/DateDialogNormalizer.java delete mode 100644 content/public/android/java/src/org/chromium/content/browser/input/DateTimePickerDialog.java delete mode 100644 content/public/android/java/src/org/chromium/content/browser/input/DateTimeSuggestion.java delete mode 100644 content/public/android/java/src/org/chromium/content/browser/input/DateTimeSuggestionListAdapter.java delete mode 100644 content/public/android/java/src/org/chromium/content/browser/input/InputDialogContainer.java delete mode 100644 content/public/android/java/src/org/chromium/content/browser/input/MonthPicker.java delete mode 100644 content/public/android/java/src/org/chromium/content/browser/input/MonthPickerDialog.java delete mode 100644 content/public/android/java/src/org/chromium/content/browser/input/MultiFieldTimePickerDialog.java delete mode 100644 content/public/android/java/src/org/chromium/content/browser/input/TwoFieldDatePicker.java delete mode 100644 content/public/android/java/src/org/chromium/content/browser/input/TwoFieldDatePickerDialog.java delete mode 100644 content/public/android/java/src/org/chromium/content/browser/input/WeekPicker.java delete mode 100644 content/public/android/java/src/org/chromium/content/browser/input/WeekPickerDialog.java create mode 100644 ui/android/java/res/layout/date_time_picker_dialog.xml create mode 100644 ui/android/java/res/layout/multi_field_time_picker_dialog.xml create mode 100644 ui/android/java/res/layout/two_field_date_picker.xml create mode 100644 ui/android/java/src/org/chromium/ui/picker/ChromeDatePickerDialog.java create mode 100644 ui/android/java/src/org/chromium/ui/picker/DateDialogNormalizer.java create mode 100644 ui/android/java/src/org/chromium/ui/picker/DateTimePickerDialog.java create mode 100644 ui/android/java/src/org/chromium/ui/picker/DateTimeSuggestion.java create mode 100644 ui/android/java/src/org/chromium/ui/picker/DateTimeSuggestionListAdapter.java create mode 100644 ui/android/java/src/org/chromium/ui/picker/InputDialogContainer.java create mode 100644 ui/android/java/src/org/chromium/ui/picker/MonthPicker.java create mode 100644 ui/android/java/src/org/chromium/ui/picker/MonthPickerDialog.java create mode 100644 ui/android/java/src/org/chromium/ui/picker/MultiFieldTimePickerDialog.java create mode 100644 ui/android/java/src/org/chromium/ui/picker/OWNERS create mode 100644 ui/android/java/src/org/chromium/ui/picker/TwoFieldDatePicker.java create mode 100644 ui/android/java/src/org/chromium/ui/picker/TwoFieldDatePickerDialog.java create mode 100644 ui/android/java/src/org/chromium/ui/picker/WeekPicker.java create mode 100644 ui/android/java/src/org/chromium/ui/picker/WeekPickerDialog.java diff --git a/content/public/android/java/res/layout/date_time_picker_dialog.xml b/content/public/android/java/res/layout/date_time_picker_dialog.xml deleted file mode 100644 index ddc0c5e..0000000 --- a/content/public/android/java/res/layout/date_time_picker_dialog.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - - diff --git a/content/public/android/java/res/layout/multi_field_time_picker_dialog.xml b/content/public/android/java/res/layout/multi_field_time_picker_dialog.xml deleted file mode 100644 index f149ef0..0000000 --- a/content/public/android/java/res/layout/multi_field_time_picker_dialog.xml +++ /dev/null @@ -1,122 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/content/public/android/java/res/layout/two_field_date_picker.xml b/content/public/android/java/res/layout/two_field_date_picker.xml deleted file mode 100644 index 0660251..0000000 --- a/content/public/android/java/res/layout/two_field_date_picker.xml +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/content/public/android/java/resource_map/org/chromium/content/R.java b/content/public/android/java/resource_map/org/chromium/content/R.java index fec2cea..74ff861 100644 --- a/content/public/android/java/resource_map/org/chromium/content/R.java +++ b/content/public/android/java/resource_map/org/chromium/content/R.java @@ -57,10 +57,6 @@ public final class R { } /** layouts */ public static final class layout { - public static int date_time_picker_dialog; - public static int date_time_suggestion; - public static int two_field_date_picker; - public static int multi_field_time_picker_dialog; public static int validation_message_bubble; } /** menus */ @@ -70,24 +66,16 @@ public final class R { /** strings */ public static final class string { public static int accessibility_content_view; - public static int accessibility_date_picker_month; - public static int accessibility_date_picker_week; - public static int accessibility_date_picker_year; public static int accessibility_datetime_picker_date; public static int accessibility_datetime_picker_time; public static int actionbar_share; public static int actionbar_web_search; - public static int date_picker_dialog_clear; - public static int date_picker_dialog_set; - public static int date_picker_dialog_title; - public static int date_picker_dialog_other_button_label; public static int date_time_picker_dialog_title; public static int media_player_error_button; public static int media_player_error_text_invalid_progressive_playback; public static int media_player_error_text_unknown; public static int media_player_error_title; public static int media_player_loading_video; - public static int month_picker_dialog_title; public static int profiler_error_toast; public static int profiler_no_storage_toast; public static int profiler_started_toast; @@ -97,8 +85,6 @@ public final class R { public static int time_picker_dialog_minute_second_separator; public static int time_picker_dialog_second_subsecond_separator; public static int time_picker_dialog_pm; - public static int time_picker_dialog_title; - public static int week_picker_dialog_title; } /** styles */ public static final class style { diff --git a/content/public/android/java/src/org/chromium/content/browser/input/ChromeDatePickerDialog.java b/content/public/android/java/src/org/chromium/content/browser/input/ChromeDatePickerDialog.java deleted file mode 100644 index 8e7987b..0000000 --- a/content/public/android/java/src/org/chromium/content/browser/input/ChromeDatePickerDialog.java +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2014 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.content.Context; -import android.content.DialogInterface; -import android.widget.DatePicker; - -/** - * The behavior of the DatePickerDialog changed after JellyBean so it now calls - * OndateSetListener.onDateSet() even when the dialog is dismissed (e.g. back button, tap - * outside). This class will call the listener instead of the DatePickerDialog only when the - * BUTTON_POSITIVE has been clicked. - */ -class ChromeDatePickerDialog extends android.app.DatePickerDialog { - private final OnDateSetListener mCallBack; - - public ChromeDatePickerDialog(Context context, - OnDateSetListener callBack, - int year, - int monthOfYear, - int dayOfMonth) { - super(context, 0, callBack, year, monthOfYear, dayOfMonth); - mCallBack = callBack; - } - - /** - * The superclass DatePickerDialog has null for OnDateSetListener so we need to call the - * listener manually. - */ - @Override - public void onClick(DialogInterface dialog, int which) { - if (which == BUTTON_POSITIVE && mCallBack != null) { - DatePicker datePicker = getDatePicker(); - datePicker.clearFocus(); - mCallBack.onDateSet(datePicker, datePicker.getYear(), - datePicker.getMonth(), datePicker.getDayOfMonth()); - } - } -} diff --git a/content/public/android/java/src/org/chromium/content/browser/input/DateDialogNormalizer.java b/content/public/android/java/src/org/chromium/content/browser/input/DateDialogNormalizer.java deleted file mode 100644 index 709d0e7..0000000 --- a/content/public/android/java/src/org/chromium/content/browser/input/DateDialogNormalizer.java +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2013 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.widget.DatePicker; -import android.widget.DatePicker.OnDateChangedListener; - -import java.util.Calendar; -import java.util.TimeZone; - -/** - * Normalize a date dialog so that it respect min and max. - */ -class DateDialogNormalizer { - - private static void setLimits(DatePicker picker, long minMillis, long maxMillis) { - // DatePicker intervals are non inclusive, the DatePicker will throw an - // exception when setting the min/max attribute to the current date - // so make sure this never happens - if (maxMillis <= minMillis) { - return; - } - Calendar minCal = trimToDate(minMillis); - Calendar maxCal = trimToDate(maxMillis); - int currentYear = picker.getYear(); - int currentMonth = picker.getMonth(); - int currentDayOfMonth = picker.getDayOfMonth(); - picker.updateDate(maxCal.get(Calendar.YEAR), - maxCal.get(Calendar.MONTH), - maxCal.get(Calendar.DAY_OF_MONTH)); - picker.setMinDate(minCal.getTimeInMillis()); - picker.updateDate(minCal.get(Calendar.YEAR), - minCal.get(Calendar.MONTH), - minCal.get(Calendar.DAY_OF_MONTH)); - picker.setMaxDate(maxCal.getTimeInMillis()); - - // Restore the current date, this will keep the min/max settings - // previously set into account. - picker.updateDate(currentYear, currentMonth, currentDayOfMonth); - } - - private static Calendar trimToDate(long time) { - Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT")); - cal.clear(); - cal.setTimeInMillis(time); - Calendar result = Calendar.getInstance(TimeZone.getTimeZone("GMT")); - result.clear(); - result.set(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH), - 0, 0, 0); - return result; - } - - /** - * Normalizes an existing DateDialogPicker changing the default date if - * needed to comply with the {@code min} and {@code max} attributes. - */ - static void normalize(DatePicker picker, OnDateChangedListener listener, - int year, int month, int day, int hour, int minute, long minMillis, long maxMillis) { - Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("GMT")); - calendar.clear(); - calendar.set(year, month, day, hour, minute, 0); - if (calendar.getTimeInMillis() < minMillis) { - calendar.clear(); - calendar.setTimeInMillis(minMillis); - } else if (calendar.getTimeInMillis() > maxMillis) { - calendar.clear(); - calendar.setTimeInMillis(maxMillis); - } - picker.init( - calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH), - calendar.get(Calendar.DAY_OF_MONTH), listener); - - setLimits(picker, minMillis, maxMillis); - } -} diff --git a/content/public/android/java/src/org/chromium/content/browser/input/DateTimeChooserAndroid.java b/content/public/android/java/src/org/chromium/content/browser/input/DateTimeChooserAndroid.java index a8642ec..a673d32 100644 --- a/content/public/android/java/src/org/chromium/content/browser/input/DateTimeChooserAndroid.java +++ b/content/public/android/java/src/org/chromium/content/browser/input/DateTimeChooserAndroid.java @@ -9,6 +9,8 @@ import android.content.Context; import org.chromium.base.CalledByNative; import org.chromium.base.JNINamespace; import org.chromium.content.browser.ContentViewCore; +import org.chromium.ui.picker.DateTimeSuggestion; +import org.chromium.ui.picker.InputDialogContainer; /** * Plumbing for the different date/time dialog adapters. diff --git a/content/public/android/java/src/org/chromium/content/browser/input/DateTimePickerDialog.java b/content/public/android/java/src/org/chromium/content/browser/input/DateTimePickerDialog.java deleted file mode 100644 index c52090b..0000000 --- a/content/public/android/java/src/org/chromium/content/browser/input/DateTimePickerDialog.java +++ /dev/null @@ -1,147 +0,0 @@ -// Copyright 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.content.Context; -import android.content.DialogInterface; -import android.content.DialogInterface.OnClickListener; -import android.text.format.Time; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.DatePicker; -import android.widget.DatePicker.OnDateChangedListener; -import android.widget.TimePicker; -import android.widget.TimePicker.OnTimeChangedListener; - -import org.chromium.content.R; - -class DateTimePickerDialog extends AlertDialog implements OnClickListener, - OnDateChangedListener, OnTimeChangedListener { - private final DatePicker mDatePicker; - private final TimePicker mTimePicker; - private final OnDateTimeSetListener mCallBack; - - private final long mMinTimeMillis; - private final long mMaxTimeMillis; - - /** - * The callback used to indicate the user is done filling in the date. - */ - public interface OnDateTimeSetListener { - - /** - * @param dateView The DatePicker view associated with this listener. - * @param timeView The TimePicker view associated with this listener. - * @param year The year that was set. - * @param monthOfYear The month that was set (0-11) for compatibility - * with {@link java.util.Calendar}. - * @param dayOfMonth The day of the month that was set. - * @param hourOfDay The hour that was set. - * @param minute The minute that was set. - */ - void onDateTimeSet(DatePicker dateView, TimePicker timeView, int year, int monthOfYear, - int dayOfMonth, int hourOfDay, int minute); - } - - /** - * @param context The context the dialog is to run in. - * @param callBack How the parent is notified that the date is set. - * @param year The initial year of the dialog. - * @param monthOfYear The initial month of the dialog. - * @param dayOfMonth The initial day of the dialog. - */ - public DateTimePickerDialog(Context context, - OnDateTimeSetListener callBack, - int year, - int monthOfYear, - int dayOfMonth, - int hourOfDay, int minute, boolean is24HourView, - double min, double max) { - super(context, 0); - - mMinTimeMillis = (long) min; - mMaxTimeMillis = (long) max; - - mCallBack = callBack; - - setButton(BUTTON_POSITIVE, context.getText( - R.string.date_picker_dialog_set), this); - setButton(BUTTON_NEGATIVE, context.getText(android.R.string.cancel), - (OnClickListener) null); - setIcon(0); - setTitle(context.getText(R.string.date_time_picker_dialog_title)); - - LayoutInflater inflater = - (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - View view = inflater.inflate(R.layout.date_time_picker_dialog, null); - setView(view); - mDatePicker = (DatePicker) view.findViewById(R.id.date_picker); - DateDialogNormalizer.normalize(mDatePicker, this, - year, monthOfYear, dayOfMonth, hourOfDay, minute, mMinTimeMillis, mMaxTimeMillis); - - mTimePicker = (TimePicker) view.findViewById(R.id.time_picker); - mTimePicker.setIs24HourView(is24HourView); - mTimePicker.setCurrentHour(hourOfDay); - mTimePicker.setCurrentMinute(minute); - mTimePicker.setOnTimeChangedListener(this); - onTimeChanged(mTimePicker, mTimePicker.getCurrentHour(), - mTimePicker.getCurrentMinute()); - } - - @Override - public void onClick(DialogInterface dialog, int which) { - tryNotifyDateTimeSet(); - } - - private void tryNotifyDateTimeSet() { - if (mCallBack != null) { - mDatePicker.clearFocus(); - mCallBack.onDateTimeSet(mDatePicker, mTimePicker, mDatePicker.getYear(), - mDatePicker.getMonth(), mDatePicker.getDayOfMonth(), - mTimePicker.getCurrentHour(), mTimePicker.getCurrentMinute()); - } - } - - @Override - public void onDateChanged(DatePicker view, int year, - int month, int day) { - // Signal a time change so the max/min checks can be applied. - if (mTimePicker != null) { - onTimeChanged(mTimePicker, mTimePicker.getCurrentHour(), - mTimePicker.getCurrentMinute()); - } - } - - @Override - public void onTimeChanged(TimePicker view, int hourOfDay, int minute) { - Time time = new Time(); - time.set(0, mTimePicker.getCurrentMinute(), - mTimePicker.getCurrentHour(), mDatePicker.getDayOfMonth(), - mDatePicker.getMonth(), mDatePicker.getYear()); - - if (time.toMillis(true) < mMinTimeMillis) { - time.set(mMinTimeMillis); - } else if (time.toMillis(true) > mMaxTimeMillis) { - time.set(mMaxTimeMillis); - } - mTimePicker.setCurrentHour(time.hour); - mTimePicker.setCurrentMinute(time.minute); - } - - /** - * Sets the current date. - * - * @param year The date year. - * @param monthOfYear The date month. - * @param dayOfMonth The date day of month. - */ - public void updateDateTime(int year, int monthOfYear, int dayOfMonth, - int hourOfDay, int minutOfHour) { - mDatePicker.updateDate(year, monthOfYear, dayOfMonth); - mTimePicker.setCurrentHour(hourOfDay); - mTimePicker.setCurrentMinute(minutOfHour); - } -} diff --git a/content/public/android/java/src/org/chromium/content/browser/input/DateTimeSuggestion.java b/content/public/android/java/src/org/chromium/content/browser/input/DateTimeSuggestion.java deleted file mode 100644 index e46b58e..0000000 --- a/content/public/android/java/src/org/chromium/content/browser/input/DateTimeSuggestion.java +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2013 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; - -/** - * Date/time suggestion container used to store information for each suggestion that will be shown - * in the suggestion list dialog. Keep in sync with date_time_suggestion.h. - */ -class DateTimeSuggestion { - private final double mValue; - private final String mLocalizedValue; - private final String mLabel; - - /** - * Constructs a color suggestion container. - * @param value The suggested date/time value. - * @param localizedValue The suggested value localized. - * @param label The label for the suggestion. - */ - DateTimeSuggestion(double value, String localizedValue, String label) { - mValue = value; - mLocalizedValue = localizedValue; - mLabel = label; - } - - double value() { - return mValue; - } - - String localizedValue() { - return mLocalizedValue; - } - - String label() { - return mLabel; - } - - @Override - public boolean equals(Object object) { - if (!(object instanceof DateTimeSuggestion)) { - return false; - } - final DateTimeSuggestion other = (DateTimeSuggestion) object; - return mValue == other.mValue && - mLocalizedValue == other.mLocalizedValue && - mLabel == other.mLabel; - } - - @Override - public int hashCode() { - int hash = 31; - hash = 37 * hash + (int) mValue; - hash = 37 * hash + mLocalizedValue.hashCode(); - hash = 37 * hash + mLabel.hashCode(); - return hash; - } -} diff --git a/content/public/android/java/src/org/chromium/content/browser/input/DateTimeSuggestionListAdapter.java b/content/public/android/java/src/org/chromium/content/browser/input/DateTimeSuggestionListAdapter.java deleted file mode 100644 index 5a8d200..0000000 --- a/content/public/android/java/src/org/chromium/content/browser/input/DateTimeSuggestionListAdapter.java +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2013 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.content.Context; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ArrayAdapter; -import android.widget.TextView; - -import org.chromium.content.R; - -import java.util.List; - -/** - * Date/time suggestion adapter for the suggestion dialog. - */ -class DateTimeSuggestionListAdapter extends ArrayAdapter { - private final Context mContext; - - DateTimeSuggestionListAdapter(Context context, List objects) { - super(context, R.layout.date_time_suggestion, objects); - mContext = context; - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - View layout = convertView; - if (convertView == null) { - LayoutInflater inflater = LayoutInflater.from(mContext); - layout = inflater.inflate(R.layout.date_time_suggestion, parent, false); - } - TextView labelView = (TextView) layout.findViewById(R.id.date_time_suggestion_value); - TextView sublabelView = (TextView) layout.findViewById(R.id.date_time_suggestion_label); - - if (position == getCount() - 1) { - labelView.setText(mContext.getText(R.string.date_picker_dialog_other_button_label)); - sublabelView.setText(""); - } else { - labelView.setText(getItem(position).localizedValue()); - sublabelView.setText(getItem(position).label()); - } - - return layout; - } - - @Override - public int getCount() { - return super.getCount() + 1; - } -} diff --git a/content/public/android/java/src/org/chromium/content/browser/input/ImeAdapter.java b/content/public/android/java/src/org/chromium/content/browser/input/ImeAdapter.java index 25fb337..f7a5891 100644 --- a/content/public/android/java/src/org/chromium/content/browser/input/ImeAdapter.java +++ b/content/public/android/java/src/org/chromium/content/browser/input/ImeAdapter.java @@ -22,6 +22,7 @@ import java.lang.CharSequence; import org.chromium.base.CalledByNative; import org.chromium.base.JNINamespace; import org.chromium.base.VisibleForTesting; +import org.chromium.ui.picker.InputDialogContainer; /** * Adapts and plumbs android IME service onto the chrome text input API. diff --git a/content/public/android/java/src/org/chromium/content/browser/input/InputDialogContainer.java b/content/public/android/java/src/org/chromium/content/browser/input/InputDialogContainer.java deleted file mode 100644 index 49123b2..0000000 --- a/content/public/android/java/src/org/chromium/content/browser/input/InputDialogContainer.java +++ /dev/null @@ -1,384 +0,0 @@ -// Copyright 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.OnDateSetListener; -import android.content.Context; -import android.content.DialogInterface; -import android.content.DialogInterface.OnDismissListener; -import android.text.format.DateFormat; -import android.view.View; -import android.widget.AdapterView; -import android.widget.DatePicker; -import android.widget.ListView; -import android.widget.TimePicker; - -import org.chromium.content.R; -import org.chromium.content.browser.input.DateTimePickerDialog.OnDateTimeSetListener; -import org.chromium.content.browser.input.MultiFieldTimePickerDialog.OnMultiFieldTimeSetListener; - -import java.util.Arrays; -import java.util.Calendar; -import java.util.Date; -import java.util.GregorianCalendar; -import java.util.TimeZone; -import java.util.concurrent.TimeUnit; - -/** - * Opens the approprate date/time picker dialog for the given dialog type. - */ -public class InputDialogContainer { - - interface InputActionDelegate { - void cancelDateTimeDialog(); - void replaceDateTime(double value); - } - - 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 final Context mContext; - - // Prevents sending two notifications (from onClick and from onDismiss) - private boolean mDialogAlreadyDismissed; - - private AlertDialog mDialog; - private final 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; - } - - void showPickerDialog(final int dialogType, double dialogValue, - double min, double max, double step) { - Calendar cal; - // |dialogValue|, |min|, |max| mean different things depending on the |dialogType|. - // For input type=month is the number of months since 1970. - // For input type=time it is milliseconds since midnight. - // For other types they are just milliseconds since 1970. - // If |dialogValue| is NaN it means an empty value. We will show the current time. - if (Double.isNaN(dialogValue)) { - cal = Calendar.getInstance(); - cal.set(Calendar.MILLISECOND, 0); - } else { - if (dialogType == sTextInputTypeMonth) { - cal = MonthPicker.createDateFromValue(dialogValue); - } else if (dialogType == sTextInputTypeWeek) { - cal = WeekPicker.createDateFromValue(dialogValue); - } else { - GregorianCalendar gregorianCalendar = - new GregorianCalendar(TimeZone.getTimeZone("UTC")); - // According to the HTML spec we only use the Gregorian calendar - // so we ignore the Julian/Gregorian transition. - gregorianCalendar.setGregorianChange(new Date(Long.MIN_VALUE)); - gregorianCalendar.setTimeInMillis((long) dialogValue); - cal = gregorianCalendar; - } - } - if (dialogType == sTextInputTypeDate) { - showPickerDialog(dialogType, - cal.get(Calendar.YEAR), - cal.get(Calendar.MONTH), - cal.get(Calendar.DAY_OF_MONTH), - 0, 0, 0, 0, 0, min, max, step); - } else if (dialogType == sTextInputTypeTime) { - showPickerDialog(dialogType, 0, 0, 0, - cal.get(Calendar.HOUR_OF_DAY), - cal.get(Calendar.MINUTE), - 0, 0, 0, min, max, step); - } else if (dialogType == sTextInputTypeDateTime || - dialogType == sTextInputTypeDateTimeLocal) { - showPickerDialog(dialogType, - cal.get(Calendar.YEAR), - cal.get(Calendar.MONTH), - cal.get(Calendar.DAY_OF_MONTH), - cal.get(Calendar.HOUR_OF_DAY), - cal.get(Calendar.MINUTE), - cal.get(Calendar.SECOND), - cal.get(Calendar.MILLISECOND), - 0, min, max, step); - } else if (dialogType == sTextInputTypeMonth) { - showPickerDialog(dialogType, cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), 0, - 0, 0, 0, 0, 0, min, max, step); - } else if (dialogType == sTextInputTypeWeek) { - int year = WeekPicker.getISOWeekYearForDate(cal); - int week = WeekPicker.getWeekForDate(cal); - showPickerDialog(dialogType, year, 0, 0, 0, 0, 0, 0, week, min, max, step); - } - } - - void showSuggestionDialog(final int dialogType, - final double dialogValue, - final double min, final double max, final double step, - DateTimeSuggestion[] suggestions) { - ListView suggestionListView = new ListView(mContext); - final DateTimeSuggestionListAdapter adapter = - new DateTimeSuggestionListAdapter(mContext, Arrays.asList(suggestions)); - suggestionListView.setAdapter(adapter); - suggestionListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { - @Override - public void onItemClick(AdapterView parent, View view, int position, long id) { - if (position == adapter.getCount() - 1) { - dismissDialog(); - showPickerDialog(dialogType, dialogValue, min, max, step); - } else { - double suggestionValue = adapter.getItem(position).value(); - mInputActionDelegate.replaceDateTime(suggestionValue); - dismissDialog(); - mDialogAlreadyDismissed = true; - } - } - }); - - int dialogTitleId = R.string.date_picker_dialog_title; - if (dialogType == sTextInputTypeTime) { - dialogTitleId = R.string.time_picker_dialog_title; - } else if (dialogType == sTextInputTypeDateTime || - dialogType == sTextInputTypeDateTimeLocal) { - dialogTitleId = R.string.date_time_picker_dialog_title; - } else if (dialogType == sTextInputTypeMonth) { - dialogTitleId = R.string.month_picker_dialog_title; - } else if (dialogType == sTextInputTypeWeek) { - dialogTitleId = R.string.week_picker_dialog_title; - } - - mDialog = new AlertDialog.Builder(mContext) - .setTitle(dialogTitleId) - .setView(suggestionListView) - .setNegativeButton(mContext.getText(android.R.string.cancel), - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - dismissDialog(); - } - }) - .create(); - - mDialog.setOnDismissListener(new DialogInterface.OnDismissListener() { - @Override - public void onDismiss(DialogInterface dialog) { - if (mDialog == dialog && !mDialogAlreadyDismissed) { - mDialogAlreadyDismissed = true; - mInputActionDelegate.cancelDateTimeDialog(); - } - } - }); - mDialogAlreadyDismissed = false; - mDialog.show(); - } - - void showDialog(final int type, final double value, - double min, double max, double step, - DateTimeSuggestion[] suggestions) { - // When the web page asks to show a dialog while there is one already open, - // dismiss the old one. - dismissDialog(); - if (suggestions == null) { - showPickerDialog(type, value, min, max, step); - } else { - showSuggestionDialog(type, value, min, max, step, suggestions); - } - } - - void showPickerDialog(final int dialogType, - int year, int month, int monthDay, - int hourOfDay, int minute, int second, int millis, int week, - double min, double max, double step) { - if (isDialogShowing()) mDialog.dismiss(); - - int stepTime = (int) step; - - if (dialogType == sTextInputTypeDate) { - ChromeDatePickerDialog dialog = new ChromeDatePickerDialog(mContext, - new DateListener(dialogType), - year, month, monthDay); - DateDialogNormalizer.normalize(dialog.getDatePicker(), dialog, - year, month, monthDay, - 0, 0, - (long) min, (long) max); - - dialog.setTitle(mContext.getText(R.string.date_picker_dialog_title)); - mDialog = dialog; - } else if (dialogType == sTextInputTypeTime) { - mDialog = new MultiFieldTimePickerDialog( - mContext, 0 /* theme */ , - hourOfDay, minute, second, millis, - (int) min, (int) max, stepTime, - DateFormat.is24HourFormat(mContext), - new FullTimeListener(dialogType)); - } else if (dialogType == sTextInputTypeDateTime || - dialogType == sTextInputTypeDateTimeLocal) { - mDialog = new DateTimePickerDialog(mContext, - new DateTimeListener(dialogType), - year, month, monthDay, - hourOfDay, minute, - DateFormat.is24HourFormat(mContext), min, max); - } else if (dialogType == sTextInputTypeMonth) { - mDialog = new MonthPickerDialog(mContext, new MonthOrWeekListener(dialogType), - year, month, min, max); - } else if (dialogType == sTextInputTypeWeek) { - mDialog = new WeekPickerDialog(mContext, new MonthOrWeekListener(dialogType), - year, week, min, max); - } - - 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), - (DialogInterface.OnClickListener) null); - - 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(Double.NaN); - } - }); - - mDialog.setOnDismissListener( - new OnDismissListener() { - @Override - public void onDismiss(final DialogInterface dialog) { - if (!mDialogAlreadyDismissed) { - mDialogAlreadyDismissed = true; - mInputActionDelegate.cancelDateTimeDialog(); - } - } - }); - - 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) { - setFieldDateTimeValue(mDialogType, year, month, monthDay, 0, 0, 0, 0, 0); - } - } - - 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) { - setFieldDateTimeValue(mDialogType, 0, 0, 0, hourOfDay, minute, second, milli, 0); - } - } - - 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) { - setFieldDateTimeValue(mDialogType, year, month, monthDay, hourOfDay, minute, 0, 0, 0); - } - } - - private class MonthOrWeekListener implements TwoFieldDatePickerDialog.OnValueSetListener { - private final int mDialogType; - - MonthOrWeekListener(int dialogType) { - mDialogType = dialogType; - } - - @Override - public void onValueSet(int year, int positionInYear) { - if (mDialogType == sTextInputTypeMonth) { - setFieldDateTimeValue(mDialogType, year, positionInYear, 0, 0, 0, 0, 0, 0); - } else { - setFieldDateTimeValue(mDialogType, year, 0, 0, 0, 0, 0, 0, positionInYear); - } - } - } - - protected void setFieldDateTimeValue(int dialogType, - int year, int month, int monthDay, - int hourOfDay, int minute, int second, int millis, - int week) { - // Prevents more than one callback being sent to the native - // side when the dialog triggers multiple events. - if (mDialogAlreadyDismissed) - return; - mDialogAlreadyDismissed = true; - - if (dialogType == sTextInputTypeMonth) { - mInputActionDelegate.replaceDateTime((year - 1970) * 12 + month); - } else if (dialogType == sTextInputTypeWeek) { - mInputActionDelegate.replaceDateTime( - WeekPicker.createDateFromWeek(year, week).getTimeInMillis()); - } else if (dialogType == sTextInputTypeTime) { - mInputActionDelegate.replaceDateTime(TimeUnit.HOURS.toMillis(hourOfDay) + - TimeUnit.MINUTES.toMillis(minute) + - TimeUnit.SECONDS.toMillis(second) + - millis); - } else { - Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - cal.clear(); - cal.set(Calendar.YEAR, year); - cal.set(Calendar.MONTH, month); - cal.set(Calendar.DAY_OF_MONTH, monthDay); - cal.set(Calendar.HOUR_OF_DAY, hourOfDay); - cal.set(Calendar.MINUTE, minute); - cal.set(Calendar.SECOND, second); - cal.set(Calendar.MILLISECOND, millis); - mInputActionDelegate.replaceDateTime(cal.getTimeInMillis()); - } - } -} diff --git a/content/public/android/java/src/org/chromium/content/browser/input/MonthPicker.java b/content/public/android/java/src/org/chromium/content/browser/input/MonthPicker.java deleted file mode 100644 index 593aa00..0000000 --- a/content/public/android/java/src/org/chromium/content/browser/input/MonthPicker.java +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright 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.content.Context; - -import org.chromium.content.R; - -import java.text.DateFormatSymbols; -import java.util.Arrays; -import java.util.Calendar; -import java.util.Locale; -import java.util.TimeZone; - -public class MonthPicker extends TwoFieldDatePicker { - private static final int MONTHS_NUMBER = 12; - - private final String[] mShortMonths; - - public MonthPicker(Context context, double minValue, double maxValue) { - super(context, minValue, maxValue); - - getPositionInYearSpinner().setContentDescription( - getResources().getString(R.string.accessibility_date_picker_month)); - - // initialization based on locale - mShortMonths = - DateFormatSymbols.getInstance(Locale.getDefault()).getShortMonths(); - - // initialize to current date - Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - init(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), null); - } - - /** - * Creates a date object from the |value| which is months since epoch. - */ - public static Calendar createDateFromValue(double value) { - int year = (int) Math.min(value / 12 + 1970, Integer.MAX_VALUE); - int month = (int) (value % 12); - Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - cal.clear(); - cal.set(year, month, 1); - return cal; - } - - @Override - protected Calendar getDateForValue(double value) { - return MonthPicker.createDateFromValue(value); - } - - @Override - protected void setCurrentDate(int year, int month) { - Calendar date = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - date.set(year, month, 1); - if (date.before(getMinDate())) { - setCurrentDate(getMinDate()); - } else if (date.after(getMaxDate())) { - setCurrentDate(getMaxDate()); - } else { - setCurrentDate(date); - } - } - - @Override - protected void updateSpinners() { - super.updateSpinners(); - - // make sure the month names are a zero based array - // with the months in the month spinner - String[] displayedValues = Arrays.copyOfRange(mShortMonths, - getPositionInYearSpinner().getMinValue(), - getPositionInYearSpinner().getMaxValue() + 1); - getPositionInYearSpinner().setDisplayedValues(displayedValues); - } - - /** - * @return The selected month. - */ - public int getMonth() { - return getCurrentDate().get(Calendar.MONTH); - } - - @Override - public int getPositionInYear() { - return getMonth(); - } - - @Override - protected int getMaxYear() { - return getMaxDate().get(Calendar.YEAR); - } - - @Override - protected int getMinYear() { - return getMinDate().get(Calendar.YEAR); - } - - - @Override - protected int getMaxPositionInYear(int year) { - if (year == getMaxDate().get(Calendar.YEAR)) { - return getMaxDate().get(Calendar.MONTH); - } - return MONTHS_NUMBER - 1; - } - - @Override - protected int getMinPositionInYear(int year) { - if (year == getMinDate().get(Calendar.YEAR)) { - return getMinDate().get(Calendar.MONTH); - } - return 0; - } -} diff --git a/content/public/android/java/src/org/chromium/content/browser/input/MonthPickerDialog.java b/content/public/android/java/src/org/chromium/content/browser/input/MonthPickerDialog.java deleted file mode 100644 index 40f45b9..0000000 --- a/content/public/android/java/src/org/chromium/content/browser/input/MonthPickerDialog.java +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 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.content.Context; - -import org.chromium.content.R; - -public class MonthPickerDialog extends TwoFieldDatePickerDialog { - - /** - * @param context The context the dialog is to run in. - * @param callBack How the parent is notified that the date is set. - * @param year The initial year of the dialog. - * @param monthOfYear The initial month of the dialog. - */ - public MonthPickerDialog(Context context, OnValueSetListener callBack, - int year, int monthOfYear, double minMonth, double maxMonth) { - super(context, callBack, year, monthOfYear, minMonth, maxMonth); - setTitle(R.string.month_picker_dialog_title); - } - - @Override - protected TwoFieldDatePicker createPicker(Context context, double minValue, double maxValue) { - return new MonthPicker(context, minValue, maxValue); - } - - /** - * Gets the {@link MonthPicker} contained in this dialog. - * - * @return The calendar view. - */ - public MonthPicker getMonthPicker() { - return (MonthPicker) mPicker; - } -} diff --git a/content/public/android/java/src/org/chromium/content/browser/input/MultiFieldTimePickerDialog.java b/content/public/android/java/src/org/chromium/content/browser/input/MultiFieldTimePickerDialog.java deleted file mode 100644 index e415a62..0000000 --- a/content/public/android/java/src/org/chromium/content/browser/input/MultiFieldTimePickerDialog.java +++ /dev/null @@ -1,275 +0,0 @@ -// Copyright 2013 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.content.Context; -import android.content.DialogInterface; -import android.content.DialogInterface.OnClickListener; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.NumberPicker; - -import org.chromium.content.R; - -import java.util.ArrayList; - -/** - * A time picker dialog with upto 5 number pickers left to right: - * hour, minute, second, milli, AM/PM. - * - * If is24hourFormat is true then AM/PM picker is not displayed and - * hour range is 0..23. Otherwise hour range is 1..12. - * The milli picker is not displayed if step >= SECOND_IN_MILLIS - * The second picker is not displayed if step >= MINUTE_IN_MILLIS. - */ -public class MultiFieldTimePickerDialog - extends AlertDialog implements OnClickListener { - - private final NumberPicker mHourSpinner; - private final NumberPicker mMinuteSpinner; - private final NumberPicker mSecSpinner; - private final NumberPicker mMilliSpinner; - private final NumberPicker mAmPmSpinner; - private final OnMultiFieldTimeSetListener mListener; - private final int mStep; - private final int mBaseMilli; - private final boolean mIs24hourFormat; - - public interface OnMultiFieldTimeSetListener { - void onTimeSet(int hourOfDay, int minute, int second, int milli); - } - - private static final int SECOND_IN_MILLIS = 1000; - private static final int MINUTE_IN_MILLIS = 60 * SECOND_IN_MILLIS; - private static final int HOUR_IN_MILLIS = 60 * MINUTE_IN_MILLIS; - - public MultiFieldTimePickerDialog( - Context context, - int theme, - int hour, int minute, int second, int milli, - int min, int max, int step, boolean is24hourFormat, - OnMultiFieldTimeSetListener listener) { - super(context, theme); - mListener = listener; - mStep = step; - mIs24hourFormat = is24hourFormat; - - if (min >= max) { - min = 0; - max = 24 * HOUR_IN_MILLIS - 1; - } - if (step < 0 || step >= 24 * HOUR_IN_MILLIS) { - step = MINUTE_IN_MILLIS; - } - - LayoutInflater inflater = - (LayoutInflater) context.getSystemService( - Context.LAYOUT_INFLATER_SERVICE); - View view = inflater.inflate(R.layout.multi_field_time_picker_dialog, null); - setView(view); - - mHourSpinner = (NumberPicker) view.findViewById(R.id.hour); - mMinuteSpinner = (NumberPicker) view.findViewById(R.id.minute); - mSecSpinner = (NumberPicker) view.findViewById(R.id.second); - mMilliSpinner = (NumberPicker) view.findViewById(R.id.milli); - mAmPmSpinner = (NumberPicker) view.findViewById(R.id.ampm); - - int minHour = min / HOUR_IN_MILLIS; - int maxHour = max / HOUR_IN_MILLIS; - min -= minHour * HOUR_IN_MILLIS; - max -= maxHour * HOUR_IN_MILLIS; - - if (minHour == maxHour) { - mHourSpinner.setEnabled(false); - hour = minHour; - } - - if (is24hourFormat) { - mAmPmSpinner.setVisibility(View.GONE); - } else { - int minAmPm = minHour / 12; - int maxAmPm = maxHour / 12; - int amPm = hour / 12; - mAmPmSpinner.setMinValue(minAmPm); - mAmPmSpinner.setMaxValue(maxAmPm); - mAmPmSpinner.setDisplayedValues(new String[] { - context.getString(R.string.time_picker_dialog_am), - context.getString(R.string.time_picker_dialog_pm) - }); - - hour %= 12; - if (hour == 0) { - hour = 12; - } - if (minAmPm == maxAmPm) { - mAmPmSpinner.setEnabled(false); - amPm = minAmPm; - - minHour %= 12; - maxHour %= 12; - if (minHour == 0 && maxHour == 0) { - minHour = 12; - maxHour = 12; - } else if (minHour == 0) { - minHour = maxHour; - maxHour = 12; - } else if (maxHour == 0) { - maxHour = 12; - } - } else { - minHour = 1; - maxHour = 12; - } - mAmPmSpinner.setValue(amPm); - } - - if (minHour == maxHour) { - mHourSpinner.setEnabled(false); - } - mHourSpinner.setMinValue(minHour); - mHourSpinner.setMaxValue(maxHour); - mHourSpinner.setValue(hour); - - NumberFormatter twoDigitPaddingFormatter = new NumberFormatter("%02d"); - - int minMinute = min / MINUTE_IN_MILLIS; - int maxMinute = max / MINUTE_IN_MILLIS; - min -= minMinute * MINUTE_IN_MILLIS; - max -= maxMinute * MINUTE_IN_MILLIS; - - if (minHour == maxHour) { - mMinuteSpinner.setMinValue(minMinute); - mMinuteSpinner.setMaxValue(maxMinute); - if (minMinute == maxMinute) { - // Set this otherwise the box is empty until you stroke it. - mMinuteSpinner.setDisplayedValues( - new String[] { twoDigitPaddingFormatter.format(minMinute) }); - mMinuteSpinner.setEnabled(false); - minute = minMinute; - } - } else { - mMinuteSpinner.setMinValue(0); - mMinuteSpinner.setMaxValue(59); - } - - if (step >= HOUR_IN_MILLIS) { - mMinuteSpinner.setEnabled(false); - } - - mMinuteSpinner.setValue(minute); - mMinuteSpinner.setFormatter(twoDigitPaddingFormatter); - - if (step >= MINUTE_IN_MILLIS) { - // Remove the ':' in front of the second spinner as well. - view.findViewById(R.id.second_colon).setVisibility(View.GONE); - mSecSpinner.setVisibility(View.GONE); - } - - int minSecond = min / SECOND_IN_MILLIS; - int maxSecond = max / SECOND_IN_MILLIS; - min -= minSecond * SECOND_IN_MILLIS; - max -= maxSecond * SECOND_IN_MILLIS; - - if (minHour == maxHour && minMinute == maxMinute) { - mSecSpinner.setMinValue(minSecond); - mSecSpinner.setMaxValue(maxSecond); - if (minSecond == maxSecond) { - // Set this otherwise the box is empty until you stroke it. - mSecSpinner.setDisplayedValues( - new String[] { twoDigitPaddingFormatter.format(minSecond) }); - mSecSpinner.setEnabled(false); - second = minSecond; - } - } else { - mSecSpinner.setMinValue(0); - mSecSpinner.setMaxValue(59); - } - - mSecSpinner.setValue(second); - mSecSpinner.setFormatter(twoDigitPaddingFormatter); - - if (step >= SECOND_IN_MILLIS) { - // Remove the '.' in front of the milli spinner as well. - view.findViewById(R.id.second_dot).setVisibility(View.GONE); - mMilliSpinner.setVisibility(View.GONE); - } - - // Round to the nearest step. - milli = ((milli + step / 2) / step) * step; - if (step == 1 || step == 10 || step == 100) { - if (minHour == maxHour && minMinute == maxMinute && - minSecond == maxSecond) { - mMilliSpinner.setMinValue(min / step); - mMilliSpinner.setMaxValue(max / step); - - if (min == max) { - mMilliSpinner.setEnabled(false); - milli = min; - } - } else { - mMilliSpinner.setMinValue(0); - mMilliSpinner.setMaxValue(999 / step); - } - - if (step == 1) { - mMilliSpinner.setFormatter(new NumberFormatter("%03d")); - } else if (step == 10) { - mMilliSpinner.setFormatter(new NumberFormatter("%02d")); - } else if (step == 100) { - mMilliSpinner.setFormatter(new NumberFormatter("%d")); - } - mMilliSpinner.setValue(milli / step); - mBaseMilli = 0; - } else if (step < SECOND_IN_MILLIS) { - // Non-decimal step value. - ArrayList strValue = new ArrayList(); - for (int i = min; i < max; i += step) { - strValue.add(String.format("%03d", i)); - } - mMilliSpinner.setMinValue(0); - mMilliSpinner.setMaxValue(strValue.size() - 1); - mMilliSpinner.setValue((milli - min) / step); - mMilliSpinner.setDisplayedValues( - strValue.toArray(new String[strValue.size()])); - mBaseMilli = min; - } else { - mBaseMilli = 0; - } - } - - @Override - public void onClick(DialogInterface dialog, int which) { - notifyDateSet(); - } - - private void notifyDateSet() { - int hour = mHourSpinner.getValue(); - int minute = mMinuteSpinner.getValue(); - int sec = mSecSpinner.getValue(); - int milli = mMilliSpinner.getValue() * mStep + mBaseMilli; - if (!mIs24hourFormat) { - int ampm = mAmPmSpinner.getValue(); - if (hour == 12) { - hour = 0; - } - hour += ampm * 12; - } - mListener.onTimeSet(hour, minute, sec, milli); - } - - private static class NumberFormatter implements NumberPicker.Formatter { - private final String mFormat; - - NumberFormatter(String format) { - mFormat = format; - } - - @Override - public String format(int value) { - return String.format(mFormat, value); - } - } -} diff --git a/content/public/android/java/src/org/chromium/content/browser/input/TwoFieldDatePicker.java b/content/public/android/java/src/org/chromium/content/browser/input/TwoFieldDatePicker.java deleted file mode 100644 index 8730e81..0000000 --- a/content/public/android/java/src/org/chromium/content/browser/input/TwoFieldDatePicker.java +++ /dev/null @@ -1,250 +0,0 @@ -// Copyright 2013 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.content.Context; -import android.text.format.DateUtils; -import android.view.LayoutInflater; -import android.view.accessibility.AccessibilityEvent; -import android.widget.FrameLayout; -import android.widget.NumberPicker; -import android.widget.NumberPicker.OnValueChangeListener; - -import org.chromium.content.R; - -import java.util.Calendar; -import java.util.TimeZone; - -/** - * This class is heavily based on android.widget.DatePicker. - */ -public abstract class TwoFieldDatePicker extends FrameLayout { - - private final NumberPicker mPositionInYearSpinner; - - private final NumberPicker mYearSpinner; - - private OnMonthOrWeekChangedListener mMonthOrWeekChangedListener; - - // It'd be nice to use android.text.Time like in other Dialogs but - // it suffers from the 2038 effect so it would prevent us from - // having dates over 2038. - private Calendar mMinDate; - - private Calendar mMaxDate; - - private Calendar mCurrentDate; - - /** - * The callback used to indicate the user changes\d the date. - */ - public interface OnMonthOrWeekChangedListener { - - /** - * Called upon a date change. - * - * @param view The view associated with this listener. - * @param year The year that was set. - * @param positionInYear The month or week in year. - */ - void onMonthOrWeekChanged(TwoFieldDatePicker view, int year, int positionInYear); - } - - public TwoFieldDatePicker(Context context, double minValue, double maxValue) { - super(context, null, android.R.attr.datePickerStyle); - - LayoutInflater inflater = (LayoutInflater) context - .getSystemService(Context.LAYOUT_INFLATER_SERVICE); - inflater.inflate(R.layout.two_field_date_picker, this, true); - - OnValueChangeListener onChangeListener = new OnValueChangeListener() { - @Override - public void onValueChange(NumberPicker picker, int oldVal, int newVal) { - int year = getYear(); - int positionInYear = getPositionInYear(); - // take care of wrapping of days and months to update greater fields - if (picker == mPositionInYearSpinner) { - positionInYear = newVal; - if (oldVal == picker.getMaxValue() && newVal == picker.getMinValue()) { - year += 1; - positionInYear = getMinPositionInYear(year); - } else if (oldVal == picker.getMinValue() && newVal == picker.getMaxValue()) { - year -= 1; - positionInYear = getMaxPositionInYear(year); - } - } else if (picker == mYearSpinner) { - year = newVal; - } else { - throw new IllegalArgumentException(); - } - - // now set the date to the adjusted one - setCurrentDate(year, positionInYear); - updateSpinners(); - notifyDateChanged(); - } - }; - - mCurrentDate = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - if (minValue >= maxValue) { - mMinDate = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - mMinDate.set(0, 0, 1); - mMaxDate = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - mMaxDate.set(9999, 0, 1); - } else { - mMinDate = getDateForValue(minValue); - mMaxDate = getDateForValue(maxValue); - } - - // month - mPositionInYearSpinner = (NumberPicker) findViewById(R.id.position_in_year); - mPositionInYearSpinner.setOnLongPressUpdateInterval(200); - mPositionInYearSpinner.setOnValueChangedListener(onChangeListener); - - // year - mYearSpinner = (NumberPicker) findViewById(R.id.year); - mYearSpinner.setOnLongPressUpdateInterval(100); - mYearSpinner.setOnValueChangedListener(onChangeListener); - } - - /** - * Initialize the state. If the provided values designate an inconsistent - * date the values are normalized before updating the spinners. - * - * @param year The initial year. - * @param positionInYear The initial month starting from zero or week in year. - * @param onMonthOrWeekChangedListener How user is notified date is changed by - * user, can be null. - */ - public void init(int year, int positionInYear, - OnMonthOrWeekChangedListener onMonthOrWeekChangedListener) { - setCurrentDate(year, positionInYear); - updateSpinners(); - mMonthOrWeekChangedListener = onMonthOrWeekChangedListener; - } - - public boolean isNewDate(int year, int positionInYear) { - return (getYear() != year || getPositionInYear() != positionInYear); - } - - /** - * Subclasses know the semantics of @value, and need to return - * a Calendar corresponding to it. - */ - protected abstract Calendar getDateForValue(double value); - - /** - * Updates the current date. - * - * @param year The year. - * @param positionInYear The month or week in year. - */ - public void updateDate(int year, int positionInYear) { - if (!isNewDate(year, positionInYear)) { - return; - } - setCurrentDate(year, positionInYear); - updateSpinners(); - notifyDateChanged(); - } - - /** - * Subclasses know the semantics of @positionInYear, and need to update @mCurrentDate to the - * appropriate date. - */ - protected abstract void setCurrentDate(int year, int positionInYear); - - protected void setCurrentDate(Calendar date) { - mCurrentDate = date; - } - - @Override - public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { - onPopulateAccessibilityEvent(event); - return true; - } - - @Override - public void onPopulateAccessibilityEvent(AccessibilityEvent event) { - super.onPopulateAccessibilityEvent(event); - - final int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR; - String selectedDateUtterance = DateUtils.formatDateTime(getContext(), - mCurrentDate.getTimeInMillis(), flags); - event.getText().add(selectedDateUtterance); - } - - /** - * @return The selected year. - */ - public int getYear() { - return mCurrentDate.get(Calendar.YEAR); - } - - /** - * @return The selected month or week. - */ - public abstract int getPositionInYear(); - - protected abstract int getMaxYear(); - - protected abstract int getMinYear(); - - protected abstract int getMaxPositionInYear(int year); - - protected abstract int getMinPositionInYear(int year); - - protected Calendar getMaxDate() { - return mMaxDate; - } - - protected Calendar getMinDate() { - return mMinDate; - } - - protected Calendar getCurrentDate() { - return mCurrentDate; - } - - protected NumberPicker getPositionInYearSpinner() { - return mPositionInYearSpinner; - } - - protected NumberPicker getYearSpinner() { - return mYearSpinner; - } - - /** - * This method should be subclassed to update the spinners based on mCurrentDate. - */ - protected void updateSpinners() { - mPositionInYearSpinner.setDisplayedValues(null); - - // set the spinner ranges respecting the min and max dates - mPositionInYearSpinner.setMinValue(getMinPositionInYear(getYear())); - mPositionInYearSpinner.setMaxValue(getMaxPositionInYear(getYear())); - mPositionInYearSpinner.setWrapSelectorWheel( - !mCurrentDate.equals(mMinDate) && !mCurrentDate.equals(mMaxDate)); - - // year spinner range does not change based on the current date - mYearSpinner.setMinValue(getMinYear()); - mYearSpinner.setMaxValue(getMaxYear()); - mYearSpinner.setWrapSelectorWheel(false); - - // set the spinner values - mYearSpinner.setValue(getYear()); - mPositionInYearSpinner.setValue(getPositionInYear()); - } - - /** - * Notifies the listener, if such, for a change in the selected date. - */ - protected void notifyDateChanged() { - sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED); - if (mMonthOrWeekChangedListener != null) { - mMonthOrWeekChangedListener.onMonthOrWeekChanged(this, getYear(), getPositionInYear()); - } - } -} diff --git a/content/public/android/java/src/org/chromium/content/browser/input/TwoFieldDatePickerDialog.java b/content/public/android/java/src/org/chromium/content/browser/input/TwoFieldDatePickerDialog.java deleted file mode 100644 index 1e84dee..0000000 --- a/content/public/android/java/src/org/chromium/content/browser/input/TwoFieldDatePickerDialog.java +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright 2013 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.content.Context; -import android.content.DialogInterface; -import android.content.DialogInterface.OnClickListener; - -import org.chromium.content.R; -import org.chromium.content.browser.input.TwoFieldDatePicker.OnMonthOrWeekChangedListener; - -public abstract class TwoFieldDatePickerDialog extends AlertDialog implements OnClickListener, - OnMonthOrWeekChangedListener { - - private static final String YEAR = "year"; - private static final String POSITION_IN_YEAR = "position_in_year"; - - protected final TwoFieldDatePicker mPicker; - protected final OnValueSetListener mCallBack; - - /** - * The callback used to indicate the user is done filling in the date. - */ - public interface OnValueSetListener { - - /** - * @param year The year that was set. - * @param positionInYear The position in the year that was set. - */ - void onValueSet(int year, int positionInYear); - } - - /** - * @param context The context the dialog is to run in. - * @param callBack How the parent is notified that the date is set. - * @param year The initial year of the dialog. - * @param weekOfYear The initial week of the dialog. - */ - public TwoFieldDatePickerDialog(Context context, - OnValueSetListener callBack, - int year, - int positionInYear, - double minValue, - double maxValue) { - this(context, 0, callBack, year, positionInYear, minValue, maxValue); - } - - /** - * @param context The context the dialog is to run in. - * @param theme the theme to apply to this dialog - * @param callBack How the parent is notified that the date is set. - * @param year The initial year of the dialog. - * @param weekOfYear The initial week of the dialog. - */ - public TwoFieldDatePickerDialog(Context context, - int theme, - OnValueSetListener callBack, - int year, - int positionInYear, - double minValue, - double maxValue) { - super(context, theme); - - mCallBack = callBack; - - setButton(BUTTON_POSITIVE, context.getText( - R.string.date_picker_dialog_set), this); - setButton(BUTTON_NEGATIVE, context.getText(android.R.string.cancel), - (OnClickListener) null); - setIcon(0); - - mPicker = createPicker(context, minValue, maxValue); - setView(mPicker); - mPicker.init(year, positionInYear, this); - } - - protected TwoFieldDatePicker createPicker(Context context, double minValue, double maxValue) { - return null; - } - - @Override - public void onClick(DialogInterface dialog, int which) { - tryNotifyDateSet(); - } - - /** - * Notifies the listener, if such, that a date has been set. - */ - protected void tryNotifyDateSet() { - if (mCallBack != null) { - mPicker.clearFocus(); - mCallBack.onValueSet(mPicker.getYear(), mPicker.getPositionInYear()); - } - } - - @Override - public void onMonthOrWeekChanged(TwoFieldDatePicker view, int year, int positionInYear) { - mPicker.init(year, positionInYear, null); - } - - /** - * Sets the current date. - * - * @param year The date week year. - * @param weekOfYear The date week. - */ - public void updateDate(int year, int weekOfYear) { - mPicker.updateDate(year, weekOfYear); - } -} diff --git a/content/public/android/java/src/org/chromium/content/browser/input/WeekPicker.java b/content/public/android/java/src/org/chromium/content/browser/input/WeekPicker.java deleted file mode 100644 index a53460f..0000000 --- a/content/public/android/java/src/org/chromium/content/browser/input/WeekPicker.java +++ /dev/null @@ -1,141 +0,0 @@ -// Copyright 2013 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.content.Context; - -import org.chromium.content.R; - -import java.util.Calendar; -import java.util.TimeZone; - -// This class is heavily based on android.widget.DatePicker. -public class WeekPicker extends TwoFieldDatePicker { - - public WeekPicker(Context context, double minValue, double maxValue) { - super(context, minValue, maxValue); - - getPositionInYearSpinner().setContentDescription( - getResources().getString(R.string.accessibility_date_picker_week)); - - // initialize to current date - Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - cal.setFirstDayOfWeek(Calendar.MONDAY); - cal.setMinimalDaysInFirstWeek(4); - cal.setTimeInMillis(System.currentTimeMillis()); - init(getISOWeekYearForDate(cal), getWeekForDate(cal), null); - } - - /** - * Creates a date object from the |year| and |week|. - */ - public static Calendar createDateFromWeek(int year, int week) { - Calendar date = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - date.clear(); - date.setFirstDayOfWeek(Calendar.MONDAY); - date.setMinimalDaysInFirstWeek(4); - date.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY); - date.set(Calendar.YEAR, year); - date.set(Calendar.WEEK_OF_YEAR, week); - return date; - } - - /** - * Creates a date object from the |value| which is milliseconds since epoch. - */ - public static Calendar createDateFromValue(double value) { - Calendar date = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - date.clear(); - date.setFirstDayOfWeek(Calendar.MONDAY); - date.setMinimalDaysInFirstWeek(4); - date.setTimeInMillis((long) value); - return date; - } - - @Override - protected Calendar getDateForValue(double value) { - return WeekPicker.createDateFromValue(value); - } - - public static int getISOWeekYearForDate(Calendar date) { - int year = date.get(Calendar.YEAR); - int month = date.get(Calendar.MONTH); - int week = date.get(Calendar.WEEK_OF_YEAR); - if (month == 0 && week > 51) { - year--; - } else if (month == 11 && week == 1) { - year++; - } - return year; - } - - public static int getWeekForDate(Calendar date) { - return date.get(Calendar.WEEK_OF_YEAR); - } - - @Override - protected void setCurrentDate(int year, int week) { - Calendar date = createDateFromWeek(year, week); - if (date.before(getMinDate())) { - setCurrentDate(getMinDate()); - } else if (date.after(getMaxDate())) { - setCurrentDate(getMaxDate()); - } else { - setCurrentDate(date); - } - } - - private int getNumberOfWeeks(int year) { - // Create a date in the middle of the year, where the week year matches the year. - Calendar date = createDateFromWeek(year, 20); - return date.getActualMaximum(Calendar.WEEK_OF_YEAR); - } - - /** - * @return The selected year. - */ - @Override - public int getYear() { - return getISOWeekYearForDate(getCurrentDate()); - } - - /** - * @return The selected week. - */ - public int getWeek() { - return getWeekForDate(getCurrentDate()); - } - - @Override - public int getPositionInYear() { - return getWeek(); - } - - @Override - protected int getMaxYear() { - return getISOWeekYearForDate(getMaxDate()); - } - - @Override - protected int getMinYear() { - return getISOWeekYearForDate(getMinDate()); - } - - @Override - protected int getMaxPositionInYear(int year) { - if (year == getISOWeekYearForDate(getMaxDate())) { - return getWeekForDate(getMaxDate()); - } - return getNumberOfWeeks(year); - } - - @Override - protected int getMinPositionInYear(int year) { - if (year == getISOWeekYearForDate(getMinDate())) { - return getWeekForDate(getMinDate()); - } - return 1; - } -} diff --git a/content/public/android/java/src/org/chromium/content/browser/input/WeekPickerDialog.java b/content/public/android/java/src/org/chromium/content/browser/input/WeekPickerDialog.java deleted file mode 100644 index 0a7240e26..0000000 --- a/content/public/android/java/src/org/chromium/content/browser/input/WeekPickerDialog.java +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2013 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.content.Context; - -import org.chromium.content.R; - -public class WeekPickerDialog extends TwoFieldDatePickerDialog { - - /** - * @param context The context the dialog is to run in. - * @param callBack How the parent is notified that the date is set. - * @param year The initial year of the dialog. - * @param weekOfYear The initial week of the dialog. - */ - public WeekPickerDialog(Context context, - OnValueSetListener callBack, - int year, int weekOfYear, - double minValue, double maxValue) { - this(context, 0, callBack, year, weekOfYear, minValue, maxValue); - } - - /** - * @param context The context the dialog is to run in. - * @param theme the theme to apply to this dialog - * @param callBack How the parent is notified that the date is set. - * @param year The initial year of the dialog. - * @param weekOfYear The initial week of the dialog. - */ - public WeekPickerDialog(Context context, - int theme, - OnValueSetListener callBack, - int year, - int weekOfYear, - double minValue, double maxValue) { - super(context, theme, callBack, year, weekOfYear, minValue, maxValue); - setTitle(R.string.week_picker_dialog_title); - } - - @Override - protected TwoFieldDatePicker createPicker(Context context, double minValue, double maxValue) { - return new WeekPicker(context, minValue, maxValue); - } - - /** - * Gets the {@link WeekPicker} contained in this dialog. - * - * @return The calendar view. - */ - public WeekPicker getWeekPicker() { - return (WeekPicker) mPicker; - } -} diff --git a/content/public/android/java/strings/android_content_strings.grd b/content/public/android/java/strings/android_content_strings.grd index b8c9481..ecb927c 100644 --- a/content/public/android/java/strings/android_content_strings.grd +++ b/content/public/android/java/strings/android_content_strings.grd @@ -102,75 +102,6 @@ Web View - - Set date - - - Set - - - Clear - - - Other - - - Set date and time - - - Set time - - - AM - - - PM - - - : - - - : - - - . - - - Set month - - - Set week - - - Date - - - Time - - - Month - - - Week - - - Year - - - Hour - - - Minute - - - Second - - - Millisecond - - - AM/PM - Cannot play video diff --git a/content/public/android/javatests/src/org/chromium/content/browser/input/InputDialogContainerTest.java b/content/public/android/javatests/src/org/chromium/content/browser/input/InputDialogContainerTest.java index aafef35..46aea21 100644 --- a/content/public/android/javatests/src/org/chromium/content/browser/input/InputDialogContainerTest.java +++ b/content/public/android/javatests/src/org/chromium/content/browser/input/InputDialogContainerTest.java @@ -9,6 +9,7 @@ import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.SmallTest; import org.chromium.base.test.util.Feature; +import org.chromium.ui.picker.InputDialogContainer; public class InputDialogContainerTest extends AndroidTestCase { private static final int TEXT_INPUT_TYPE_DATE = 0; @@ -373,7 +374,7 @@ public class InputDialogContainerTest extends AndroidTestCase { } @Override - void showPickerDialog(final int dialogType, + protected void showPickerDialog(final int dialogType, int year, int month, int monthDay, int hourOfDay, int minute, int second, int millis, int week, double min, double max, double step) { diff --git a/ui/android/java/res/layout/date_time_picker_dialog.xml b/ui/android/java/res/layout/date_time_picker_dialog.xml new file mode 100644 index 0000000..a021753 --- /dev/null +++ b/ui/android/java/res/layout/date_time_picker_dialog.xml @@ -0,0 +1,33 @@ + + + + + + + + + + diff --git a/ui/android/java/res/layout/multi_field_time_picker_dialog.xml b/ui/android/java/res/layout/multi_field_time_picker_dialog.xml new file mode 100644 index 0000000..e037a09 --- /dev/null +++ b/ui/android/java/res/layout/multi_field_time_picker_dialog.xml @@ -0,0 +1,122 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/ui/android/java/res/layout/two_field_date_picker.xml b/ui/android/java/res/layout/two_field_date_picker.xml new file mode 100644 index 0000000..0660251 --- /dev/null +++ b/ui/android/java/res/layout/two_field_date_picker.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + diff --git a/ui/android/java/resource_map/org/chromium/ui/R.java b/ui/android/java/resource_map/org/chromium/ui/R.java index b2e2b7b..1cce99d 100644 --- a/ui/android/java/resource_map/org/chromium/ui/R.java +++ b/ui/android/java/resource_map/org/chromium/ui/R.java @@ -16,6 +16,9 @@ package org.chromium.ui; */ public final class R { public static final class string { + public static int accessibility_date_picker_month; + public static int accessibility_date_picker_year; + public static int accessibility_date_picker_week; public static int copy_to_clipboard_failure_message; public static int low_memory_error; public static int opening_file_error; @@ -34,6 +37,13 @@ public final class R { public static int color_picker_button_yellow; public static int color_picker_button_black; public static int color_picker_button_white; + public static int date_picker_dialog_set; + public static int date_picker_dialog_other_button_label; + public static int date_picker_dialog_clear; + public static int date_picker_dialog_title; + public static int month_picker_dialog_title; + public static int time_picker_dialog_title; + public static int week_picker_dialog_title; } public static final class id { public static int dropdown_label; diff --git a/ui/android/java/src/org/chromium/ui/picker/ChromeDatePickerDialog.java b/ui/android/java/src/org/chromium/ui/picker/ChromeDatePickerDialog.java new file mode 100644 index 0000000..f236ee1 --- /dev/null +++ b/ui/android/java/src/org/chromium/ui/picker/ChromeDatePickerDialog.java @@ -0,0 +1,42 @@ +// Copyright 2014 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.ui.picker; + +import android.content.Context; +import android.content.DialogInterface; +import android.widget.DatePicker; + +/** + * The behavior of the DatePickerDialog changed after JellyBean so it now calls + * OndateSetListener.onDateSet() even when the dialog is dismissed (e.g. back button, tap + * outside). This class will call the listener instead of the DatePickerDialog only when the + * BUTTON_POSITIVE has been clicked. + */ +class ChromeDatePickerDialog extends android.app.DatePickerDialog { + private final OnDateSetListener mCallBack; + + public ChromeDatePickerDialog(Context context, + OnDateSetListener callBack, + int year, + int monthOfYear, + int dayOfMonth) { + super(context, 0, callBack, year, monthOfYear, dayOfMonth); + mCallBack = callBack; + } + + /** + * The superclass DatePickerDialog has null for OnDateSetListener so we need to call the + * listener manually. + */ + @Override + public void onClick(DialogInterface dialog, int which) { + if (which == BUTTON_POSITIVE && mCallBack != null) { + DatePicker datePicker = getDatePicker(); + datePicker.clearFocus(); + mCallBack.onDateSet(datePicker, datePicker.getYear(), + datePicker.getMonth(), datePicker.getDayOfMonth()); + } + } +} diff --git a/ui/android/java/src/org/chromium/ui/picker/DateDialogNormalizer.java b/ui/android/java/src/org/chromium/ui/picker/DateDialogNormalizer.java new file mode 100644 index 0000000..25a7495 --- /dev/null +++ b/ui/android/java/src/org/chromium/ui/picker/DateDialogNormalizer.java @@ -0,0 +1,77 @@ +// Copyright 2013 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.ui.picker; + +import android.widget.DatePicker; +import android.widget.DatePicker.OnDateChangedListener; + +import java.util.Calendar; +import java.util.TimeZone; + +/** + * Normalize a date dialog so that it respect min and max. + */ +public class DateDialogNormalizer { + + private static void setLimits(DatePicker picker, long minMillis, long maxMillis) { + // DatePicker intervals are non inclusive, the DatePicker will throw an + // exception when setting the min/max attribute to the current date + // so make sure this never happens + if (maxMillis <= minMillis) { + return; + } + Calendar minCal = trimToDate(minMillis); + Calendar maxCal = trimToDate(maxMillis); + int currentYear = picker.getYear(); + int currentMonth = picker.getMonth(); + int currentDayOfMonth = picker.getDayOfMonth(); + picker.updateDate(maxCal.get(Calendar.YEAR), + maxCal.get(Calendar.MONTH), + maxCal.get(Calendar.DAY_OF_MONTH)); + picker.setMinDate(minCal.getTimeInMillis()); + picker.updateDate(minCal.get(Calendar.YEAR), + minCal.get(Calendar.MONTH), + minCal.get(Calendar.DAY_OF_MONTH)); + picker.setMaxDate(maxCal.getTimeInMillis()); + + // Restore the current date, this will keep the min/max settings + // previously set into account. + picker.updateDate(currentYear, currentMonth, currentDayOfMonth); + } + + private static Calendar trimToDate(long time) { + Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT")); + cal.clear(); + cal.setTimeInMillis(time); + Calendar result = Calendar.getInstance(TimeZone.getTimeZone("GMT")); + result.clear(); + result.set(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH), + 0, 0, 0); + return result; + } + + /** + * Normalizes an existing DateDialogPicker changing the default date if + * needed to comply with the {@code min} and {@code max} attributes. + */ + public static void normalize(DatePicker picker, OnDateChangedListener listener, + int year, int month, int day, int hour, int minute, long minMillis, long maxMillis) { + Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("GMT")); + calendar.clear(); + calendar.set(year, month, day, hour, minute, 0); + if (calendar.getTimeInMillis() < minMillis) { + calendar.clear(); + calendar.setTimeInMillis(minMillis); + } else if (calendar.getTimeInMillis() > maxMillis) { + calendar.clear(); + calendar.setTimeInMillis(maxMillis); + } + picker.init( + calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH), + calendar.get(Calendar.DAY_OF_MONTH), listener); + + setLimits(picker, minMillis, maxMillis); + } +} diff --git a/ui/android/java/src/org/chromium/ui/picker/DateTimePickerDialog.java b/ui/android/java/src/org/chromium/ui/picker/DateTimePickerDialog.java new file mode 100644 index 0000000..c9dde07 --- /dev/null +++ b/ui/android/java/src/org/chromium/ui/picker/DateTimePickerDialog.java @@ -0,0 +1,147 @@ +// Copyright 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.ui.picker; + +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.DialogInterface.OnClickListener; +import android.text.format.Time; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.DatePicker; +import android.widget.DatePicker.OnDateChangedListener; +import android.widget.TimePicker; +import android.widget.TimePicker.OnTimeChangedListener; + +import org.chromium.ui.R; + +public class DateTimePickerDialog extends AlertDialog implements OnClickListener, + OnDateChangedListener, OnTimeChangedListener { + private final DatePicker mDatePicker; + private final TimePicker mTimePicker; + private final OnDateTimeSetListener mCallBack; + + private final long mMinTimeMillis; + private final long mMaxTimeMillis; + + /** + * The callback used to indicate the user is done filling in the date. + */ + public interface OnDateTimeSetListener { + + /** + * @param dateView The DatePicker view associated with this listener. + * @param timeView The TimePicker view associated with this listener. + * @param year The year that was set. + * @param monthOfYear The month that was set (0-11) for compatibility + * with {@link java.util.Calendar}. + * @param dayOfMonth The day of the month that was set. + * @param hourOfDay The hour that was set. + * @param minute The minute that was set. + */ + void onDateTimeSet(DatePicker dateView, TimePicker timeView, int year, int monthOfYear, + int dayOfMonth, int hourOfDay, int minute); + } + + /** + * @param context The context the dialog is to run in. + * @param callBack How the parent is notified that the date is set. + * @param year The initial year of the dialog. + * @param monthOfYear The initial month of the dialog. + * @param dayOfMonth The initial day of the dialog. + */ + public DateTimePickerDialog(Context context, + OnDateTimeSetListener callBack, + int year, + int monthOfYear, + int dayOfMonth, + int hourOfDay, int minute, boolean is24HourView, + double min, double max) { + super(context, 0); + + mMinTimeMillis = (long) min; + mMaxTimeMillis = (long) max; + + mCallBack = callBack; + + setButton(BUTTON_POSITIVE, context.getText( + R.string.date_picker_dialog_set), this); + setButton(BUTTON_NEGATIVE, context.getText(android.R.string.cancel), + (OnClickListener) null); + setIcon(0); + setTitle(context.getText(R.string.date_time_picker_dialog_title)); + + LayoutInflater inflater = + (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + View view = inflater.inflate(R.layout.date_time_picker_dialog, null); + setView(view); + mDatePicker = (DatePicker) view.findViewById(R.id.date_picker); + DateDialogNormalizer.normalize(mDatePicker, this, + year, monthOfYear, dayOfMonth, hourOfDay, minute, mMinTimeMillis, mMaxTimeMillis); + + mTimePicker = (TimePicker) view.findViewById(R.id.time_picker); + mTimePicker.setIs24HourView(is24HourView); + mTimePicker.setCurrentHour(hourOfDay); + mTimePicker.setCurrentMinute(minute); + mTimePicker.setOnTimeChangedListener(this); + onTimeChanged(mTimePicker, mTimePicker.getCurrentHour(), + mTimePicker.getCurrentMinute()); + } + + @Override + public void onClick(DialogInterface dialog, int which) { + tryNotifyDateTimeSet(); + } + + private void tryNotifyDateTimeSet() { + if (mCallBack != null) { + mDatePicker.clearFocus(); + mCallBack.onDateTimeSet(mDatePicker, mTimePicker, mDatePicker.getYear(), + mDatePicker.getMonth(), mDatePicker.getDayOfMonth(), + mTimePicker.getCurrentHour(), mTimePicker.getCurrentMinute()); + } + } + + @Override + public void onDateChanged(DatePicker view, int year, + int month, int day) { + // Signal a time change so the max/min checks can be applied. + if (mTimePicker != null) { + onTimeChanged(mTimePicker, mTimePicker.getCurrentHour(), + mTimePicker.getCurrentMinute()); + } + } + + @Override + public void onTimeChanged(TimePicker view, int hourOfDay, int minute) { + Time time = new Time(); + time.set(0, mTimePicker.getCurrentMinute(), + mTimePicker.getCurrentHour(), mDatePicker.getDayOfMonth(), + mDatePicker.getMonth(), mDatePicker.getYear()); + + if (time.toMillis(true) < mMinTimeMillis) { + time.set(mMinTimeMillis); + } else if (time.toMillis(true) > mMaxTimeMillis) { + time.set(mMaxTimeMillis); + } + mTimePicker.setCurrentHour(time.hour); + mTimePicker.setCurrentMinute(time.minute); + } + + /** + * Sets the current date. + * + * @param year The date year. + * @param monthOfYear The date month. + * @param dayOfMonth The date day of month. + */ + public void updateDateTime(int year, int monthOfYear, int dayOfMonth, + int hourOfDay, int minutOfHour) { + mDatePicker.updateDate(year, monthOfYear, dayOfMonth); + mTimePicker.setCurrentHour(hourOfDay); + mTimePicker.setCurrentMinute(minutOfHour); + } +} diff --git a/ui/android/java/src/org/chromium/ui/picker/DateTimeSuggestion.java b/ui/android/java/src/org/chromium/ui/picker/DateTimeSuggestion.java new file mode 100644 index 0000000..4c81468 --- /dev/null +++ b/ui/android/java/src/org/chromium/ui/picker/DateTimeSuggestion.java @@ -0,0 +1,59 @@ +// Copyright 2013 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.ui.picker; + +/** + * Date/time suggestion container used to store information for each suggestion that will be shown + * in the suggestion list dialog. Keep in sync with date_time_suggestion.h. + */ +public class DateTimeSuggestion { + private final double mValue; + private final String mLocalizedValue; + private final String mLabel; + + /** + * Constructs a color suggestion container. + * @param value The suggested date/time value. + * @param localizedValue The suggested value localized. + * @param label The label for the suggestion. + */ + public DateTimeSuggestion(double value, String localizedValue, String label) { + mValue = value; + mLocalizedValue = localizedValue; + mLabel = label; + } + + double value() { + return mValue; + } + + String localizedValue() { + return mLocalizedValue; + } + + String label() { + return mLabel; + } + + @Override + public boolean equals(Object object) { + if (!(object instanceof DateTimeSuggestion)) { + return false; + } + final DateTimeSuggestion other = (DateTimeSuggestion) object; + return mValue == other.mValue && + mLocalizedValue == other.mLocalizedValue && + mLabel == other.mLabel; + } + + @Override + public int hashCode() { + int hash = 31; + hash = 37 * hash + (int) mValue; + hash = 37 * hash + mLocalizedValue.hashCode(); + hash = 37 * hash + mLabel.hashCode(); + return hash; + } +} diff --git a/ui/android/java/src/org/chromium/ui/picker/DateTimeSuggestionListAdapter.java b/ui/android/java/src/org/chromium/ui/picker/DateTimeSuggestionListAdapter.java new file mode 100644 index 0000000..c7a166c --- /dev/null +++ b/ui/android/java/src/org/chromium/ui/picker/DateTimeSuggestionListAdapter.java @@ -0,0 +1,54 @@ +// Copyright 2013 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.ui.picker; + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.TextView; + +import org.chromium.ui.R; + +import java.util.List; + +/** + * Date/time suggestion adapter for the suggestion dialog. + */ +class DateTimeSuggestionListAdapter extends ArrayAdapter { + private final Context mContext; + + DateTimeSuggestionListAdapter(Context context, List objects) { + super(context, R.layout.date_time_suggestion, objects); + mContext = context; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + View layout = convertView; + if (convertView == null) { + LayoutInflater inflater = LayoutInflater.from(mContext); + layout = inflater.inflate(R.layout.date_time_suggestion, parent, false); + } + TextView labelView = (TextView) layout.findViewById(R.id.date_time_suggestion_value); + TextView sublabelView = (TextView) layout.findViewById(R.id.date_time_suggestion_label); + + if (position == getCount() - 1) { + labelView.setText(mContext.getText(R.string.date_picker_dialog_other_button_label)); + sublabelView.setText(""); + } else { + labelView.setText(getItem(position).localizedValue()); + sublabelView.setText(getItem(position).label()); + } + + return layout; + } + + @Override + public int getCount() { + return super.getCount() + 1; + } +} diff --git a/ui/android/java/src/org/chromium/ui/picker/InputDialogContainer.java b/ui/android/java/src/org/chromium/ui/picker/InputDialogContainer.java new file mode 100644 index 0000000..797e03b --- /dev/null +++ b/ui/android/java/src/org/chromium/ui/picker/InputDialogContainer.java @@ -0,0 +1,383 @@ +// Copyright 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.ui.picker; + +import android.app.AlertDialog; +import android.app.DatePickerDialog.OnDateSetListener; +import android.content.Context; +import android.content.DialogInterface; +import android.content.DialogInterface.OnDismissListener; +import android.text.format.DateFormat; +import android.view.View; +import android.widget.AdapterView; +import android.widget.DatePicker; +import android.widget.ListView; +import android.widget.TimePicker; + +import org.chromium.ui.R; +import org.chromium.ui.picker.DateTimePickerDialog.OnDateTimeSetListener; +import org.chromium.ui.picker.MultiFieldTimePickerDialog.OnMultiFieldTimeSetListener; + +import java.util.Arrays; +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.TimeZone; +import java.util.concurrent.TimeUnit; + +/** + * Opens the appropriate date/time picker dialog for the given dialog type. + */ +public class InputDialogContainer { + + public interface InputActionDelegate { + void cancelDateTimeDialog(); + void replaceDateTime(double value); + } + + 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 final Context mContext; + + // Prevents sending two notifications (from onClick and from onDismiss) + private boolean mDialogAlreadyDismissed; + + private AlertDialog mDialog; + private final InputActionDelegate mInputActionDelegate; + + public 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; + } + + public static boolean isDialogInputType(int type) { + return type == sTextInputTypeDate || type == sTextInputTypeTime + || type == sTextInputTypeDateTime || type == sTextInputTypeDateTimeLocal + || type == sTextInputTypeMonth || type == sTextInputTypeWeek; + } + + public InputDialogContainer(Context context, InputActionDelegate inputActionDelegate) { + mContext = context; + mInputActionDelegate = inputActionDelegate; + } + + public void showPickerDialog(final int dialogType, double dialogValue, + double min, double max, double step) { + Calendar cal; + // |dialogValue|, |min|, |max| mean different things depending on the |dialogType|. + // For input type=month is the number of months since 1970. + // For input type=time it is milliseconds since midnight. + // For other types they are just milliseconds since 1970. + // If |dialogValue| is NaN it means an empty value. We will show the current time. + if (Double.isNaN(dialogValue)) { + cal = Calendar.getInstance(); + cal.set(Calendar.MILLISECOND, 0); + } else { + if (dialogType == sTextInputTypeMonth) { + cal = MonthPicker.createDateFromValue(dialogValue); + } else if (dialogType == sTextInputTypeWeek) { + cal = WeekPicker.createDateFromValue(dialogValue); + } else { + GregorianCalendar gregorianCalendar = + new GregorianCalendar(TimeZone.getTimeZone("UTC")); + // According to the HTML spec we only use the Gregorian calendar + // so we ignore the Julian/Gregorian transition. + gregorianCalendar.setGregorianChange(new Date(Long.MIN_VALUE)); + gregorianCalendar.setTimeInMillis((long) dialogValue); + cal = gregorianCalendar; + } + } + if (dialogType == sTextInputTypeDate) { + showPickerDialog(dialogType, + cal.get(Calendar.YEAR), + cal.get(Calendar.MONTH), + cal.get(Calendar.DAY_OF_MONTH), + 0, 0, 0, 0, 0, min, max, step); + } else if (dialogType == sTextInputTypeTime) { + showPickerDialog(dialogType, 0, 0, 0, + cal.get(Calendar.HOUR_OF_DAY), + cal.get(Calendar.MINUTE), + 0, 0, 0, min, max, step); + } else if (dialogType == sTextInputTypeDateTime || + dialogType == sTextInputTypeDateTimeLocal) { + showPickerDialog(dialogType, + cal.get(Calendar.YEAR), + cal.get(Calendar.MONTH), + cal.get(Calendar.DAY_OF_MONTH), + cal.get(Calendar.HOUR_OF_DAY), + cal.get(Calendar.MINUTE), + cal.get(Calendar.SECOND), + cal.get(Calendar.MILLISECOND), + 0, min, max, step); + } else if (dialogType == sTextInputTypeMonth) { + showPickerDialog(dialogType, cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), 0, + 0, 0, 0, 0, 0, min, max, step); + } else if (dialogType == sTextInputTypeWeek) { + int year = WeekPicker.getISOWeekYearForDate(cal); + int week = WeekPicker.getWeekForDate(cal); + showPickerDialog(dialogType, year, 0, 0, 0, 0, 0, 0, week, min, max, step); + } + } + + void showSuggestionDialog(final int dialogType, + final double dialogValue, + final double min, final double max, final double step, + DateTimeSuggestion[] suggestions) { + ListView suggestionListView = new ListView(mContext); + final DateTimeSuggestionListAdapter adapter = + new DateTimeSuggestionListAdapter(mContext, Arrays.asList(suggestions)); + suggestionListView.setAdapter(adapter); + suggestionListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + if (position == adapter.getCount() - 1) { + dismissDialog(); + showPickerDialog(dialogType, dialogValue, min, max, step); + } else { + double suggestionValue = adapter.getItem(position).value(); + mInputActionDelegate.replaceDateTime(suggestionValue); + dismissDialog(); + mDialogAlreadyDismissed = true; + } + } + }); + + int dialogTitleId = R.string.date_picker_dialog_title; + if (dialogType == sTextInputTypeTime) { + dialogTitleId = R.string.time_picker_dialog_title; + } else if (dialogType == sTextInputTypeDateTime || + dialogType == sTextInputTypeDateTimeLocal) { + dialogTitleId = R.string.date_time_picker_dialog_title; + } else if (dialogType == sTextInputTypeMonth) { + dialogTitleId = R.string.month_picker_dialog_title; + } else if (dialogType == sTextInputTypeWeek) { + dialogTitleId = R.string.week_picker_dialog_title; + } + + mDialog = new AlertDialog.Builder(mContext) + .setTitle(dialogTitleId) + .setView(suggestionListView) + .setNegativeButton(mContext.getText(android.R.string.cancel), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dismissDialog(); + } + }) + .create(); + + mDialog.setOnDismissListener(new DialogInterface.OnDismissListener() { + @Override + public void onDismiss(DialogInterface dialog) { + if (mDialog == dialog && !mDialogAlreadyDismissed) { + mDialogAlreadyDismissed = true; + mInputActionDelegate.cancelDateTimeDialog(); + } + } + }); + mDialogAlreadyDismissed = false; + mDialog.show(); + } + + public void showDialog(final int type, final double value, + double min, double max, double step, + DateTimeSuggestion[] suggestions) { + // When the web page asks to show a dialog while there is one already open, + // dismiss the old one. + dismissDialog(); + if (suggestions == null) { + showPickerDialog(type, value, min, max, step); + } else { + showSuggestionDialog(type, value, min, max, step, suggestions); + } + } + + protected void showPickerDialog(final int dialogType, + int year, int month, int monthDay, + int hourOfDay, int minute, int second, int millis, int week, + double min, double max, double step) { + if (isDialogShowing()) mDialog.dismiss(); + + int stepTime = (int) step; + + if (dialogType == sTextInputTypeDate) { + ChromeDatePickerDialog dialog = new ChromeDatePickerDialog(mContext, + new DateListener(dialogType), + year, month, monthDay); + DateDialogNormalizer.normalize(dialog.getDatePicker(), dialog, + year, month, monthDay, + 0, 0, + (long) min, (long) max); + + dialog.setTitle(mContext.getText(R.string.date_picker_dialog_title)); + mDialog = dialog; + } else if (dialogType == sTextInputTypeTime) { + mDialog = new MultiFieldTimePickerDialog( + mContext, 0 /* theme */ , + hourOfDay, minute, second, millis, + (int) min, (int) max, stepTime, + DateFormat.is24HourFormat(mContext), + new FullTimeListener(dialogType)); + } else if (dialogType == sTextInputTypeDateTime || + dialogType == sTextInputTypeDateTimeLocal) { + mDialog = new DateTimePickerDialog(mContext, + new DateTimeListener(dialogType), + year, month, monthDay, + hourOfDay, minute, + DateFormat.is24HourFormat(mContext), min, max); + } else if (dialogType == sTextInputTypeMonth) { + mDialog = new MonthPickerDialog(mContext, new MonthOrWeekListener(dialogType), + year, month, min, max); + } else if (dialogType == sTextInputTypeWeek) { + mDialog = new WeekPickerDialog(mContext, new MonthOrWeekListener(dialogType), + year, week, min, max); + } + + 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), + (DialogInterface.OnClickListener) null); + + 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(Double.NaN); + } + }); + + mDialog.setOnDismissListener( + new OnDismissListener() { + @Override + public void onDismiss(final DialogInterface dialog) { + if (!mDialogAlreadyDismissed) { + mDialogAlreadyDismissed = true; + mInputActionDelegate.cancelDateTimeDialog(); + } + } + }); + + 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) { + setFieldDateTimeValue(mDialogType, year, month, monthDay, 0, 0, 0, 0, 0); + } + } + + 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) { + setFieldDateTimeValue(mDialogType, 0, 0, 0, hourOfDay, minute, second, milli, 0); + } + } + + 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) { + setFieldDateTimeValue(mDialogType, year, month, monthDay, hourOfDay, minute, 0, 0, 0); + } + } + + private class MonthOrWeekListener implements TwoFieldDatePickerDialog.OnValueSetListener { + private final int mDialogType; + + MonthOrWeekListener(int dialogType) { + mDialogType = dialogType; + } + + @Override + public void onValueSet(int year, int positionInYear) { + if (mDialogType == sTextInputTypeMonth) { + setFieldDateTimeValue(mDialogType, year, positionInYear, 0, 0, 0, 0, 0, 0); + } else { + setFieldDateTimeValue(mDialogType, year, 0, 0, 0, 0, 0, 0, positionInYear); + } + } + } + + protected void setFieldDateTimeValue(int dialogType, + int year, int month, int monthDay, + int hourOfDay, int minute, int second, int millis, + int week) { + // Prevents more than one callback being sent to the native + // side when the dialog triggers multiple events. + if (mDialogAlreadyDismissed) + return; + mDialogAlreadyDismissed = true; + + if (dialogType == sTextInputTypeMonth) { + mInputActionDelegate.replaceDateTime((year - 1970) * 12 + month); + } else if (dialogType == sTextInputTypeWeek) { + mInputActionDelegate.replaceDateTime( + WeekPicker.createDateFromWeek(year, week).getTimeInMillis()); + } else if (dialogType == sTextInputTypeTime) { + mInputActionDelegate.replaceDateTime(TimeUnit.HOURS.toMillis(hourOfDay) + + TimeUnit.MINUTES.toMillis(minute) + + TimeUnit.SECONDS.toMillis(second) + + millis); + } else { + Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC")); + cal.clear(); + cal.set(Calendar.YEAR, year); + cal.set(Calendar.MONTH, month); + cal.set(Calendar.DAY_OF_MONTH, monthDay); + cal.set(Calendar.HOUR_OF_DAY, hourOfDay); + cal.set(Calendar.MINUTE, minute); + cal.set(Calendar.SECOND, second); + cal.set(Calendar.MILLISECOND, millis); + mInputActionDelegate.replaceDateTime(cal.getTimeInMillis()); + } + } +} diff --git a/ui/android/java/src/org/chromium/ui/picker/MonthPicker.java b/ui/android/java/src/org/chromium/ui/picker/MonthPicker.java new file mode 100644 index 0000000..416217c --- /dev/null +++ b/ui/android/java/src/org/chromium/ui/picker/MonthPicker.java @@ -0,0 +1,117 @@ +// Copyright 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.ui.picker; + +import android.content.Context; + +import org.chromium.ui.R; + +import java.text.DateFormatSymbols; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Locale; +import java.util.TimeZone; + +public class MonthPicker extends TwoFieldDatePicker { + private static final int MONTHS_NUMBER = 12; + + private final String[] mShortMonths; + + public MonthPicker(Context context, double minValue, double maxValue) { + super(context, minValue, maxValue); + + getPositionInYearSpinner().setContentDescription( + getResources().getString(R.string.accessibility_date_picker_month)); + + // initialization based on locale + mShortMonths = + DateFormatSymbols.getInstance(Locale.getDefault()).getShortMonths(); + + // initialize to current date + Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC")); + init(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), null); + } + + /** + * Creates a date object from the |value| which is months since epoch. + */ + public static Calendar createDateFromValue(double value) { + int year = (int) Math.min(value / 12 + 1970, Integer.MAX_VALUE); + int month = (int) (value % 12); + Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC")); + cal.clear(); + cal.set(year, month, 1); + return cal; + } + + @Override + protected Calendar getDateForValue(double value) { + return MonthPicker.createDateFromValue(value); + } + + @Override + protected void setCurrentDate(int year, int month) { + Calendar date = Calendar.getInstance(TimeZone.getTimeZone("UTC")); + date.set(year, month, 1); + if (date.before(getMinDate())) { + setCurrentDate(getMinDate()); + } else if (date.after(getMaxDate())) { + setCurrentDate(getMaxDate()); + } else { + setCurrentDate(date); + } + } + + @Override + protected void updateSpinners() { + super.updateSpinners(); + + // make sure the month names are a zero based array + // with the months in the month spinner + String[] displayedValues = Arrays.copyOfRange(mShortMonths, + getPositionInYearSpinner().getMinValue(), + getPositionInYearSpinner().getMaxValue() + 1); + getPositionInYearSpinner().setDisplayedValues(displayedValues); + } + + /** + * @return The selected month. + */ + public int getMonth() { + return getCurrentDate().get(Calendar.MONTH); + } + + @Override + public int getPositionInYear() { + return getMonth(); + } + + @Override + protected int getMaxYear() { + return getMaxDate().get(Calendar.YEAR); + } + + @Override + protected int getMinYear() { + return getMinDate().get(Calendar.YEAR); + } + + + @Override + protected int getMaxPositionInYear(int year) { + if (year == getMaxDate().get(Calendar.YEAR)) { + return getMaxDate().get(Calendar.MONTH); + } + return MONTHS_NUMBER - 1; + } + + @Override + protected int getMinPositionInYear(int year) { + if (year == getMinDate().get(Calendar.YEAR)) { + return getMinDate().get(Calendar.MONTH); + } + return 0; + } +} diff --git a/ui/android/java/src/org/chromium/ui/picker/MonthPickerDialog.java b/ui/android/java/src/org/chromium/ui/picker/MonthPickerDialog.java new file mode 100644 index 0000000..58aafa6 --- /dev/null +++ b/ui/android/java/src/org/chromium/ui/picker/MonthPickerDialog.java @@ -0,0 +1,38 @@ +// Copyright 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.ui.picker; + +import android.content.Context; + +import org.chromium.ui.R; + +public class MonthPickerDialog extends TwoFieldDatePickerDialog { + + /** + * @param context The context the dialog is to run in. + * @param callBack How the parent is notified that the date is set. + * @param year The initial year of the dialog. + * @param monthOfYear The initial month of the dialog. + */ + public MonthPickerDialog(Context context, OnValueSetListener callBack, + int year, int monthOfYear, double minMonth, double maxMonth) { + super(context, callBack, year, monthOfYear, minMonth, maxMonth); + setTitle(R.string.month_picker_dialog_title); + } + + @Override + protected TwoFieldDatePicker createPicker(Context context, double minValue, double maxValue) { + return new MonthPicker(context, minValue, maxValue); + } + + /** + * Gets the {@link MonthPicker} contained in this dialog. + * + * @return The calendar view. + */ + public MonthPicker getMonthPicker() { + return (MonthPicker) mPicker; + } +} diff --git a/ui/android/java/src/org/chromium/ui/picker/MultiFieldTimePickerDialog.java b/ui/android/java/src/org/chromium/ui/picker/MultiFieldTimePickerDialog.java new file mode 100644 index 0000000..860829f --- /dev/null +++ b/ui/android/java/src/org/chromium/ui/picker/MultiFieldTimePickerDialog.java @@ -0,0 +1,275 @@ +// Copyright 2013 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.ui.picker; + +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.DialogInterface.OnClickListener; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.NumberPicker; + +import org.chromium.ui.R; + +import java.util.ArrayList; + +/** + * A time picker dialog with upto 5 number pickers left to right: + * hour, minute, second, milli, AM/PM. + * + * If is24hourFormat is true then AM/PM picker is not displayed and + * hour range is 0..23. Otherwise hour range is 1..12. + * The milli picker is not displayed if step >= SECOND_IN_MILLIS + * The second picker is not displayed if step >= MINUTE_IN_MILLIS. + */ +public class MultiFieldTimePickerDialog + extends AlertDialog implements OnClickListener { + + private final NumberPicker mHourSpinner; + private final NumberPicker mMinuteSpinner; + private final NumberPicker mSecSpinner; + private final NumberPicker mMilliSpinner; + private final NumberPicker mAmPmSpinner; + private final OnMultiFieldTimeSetListener mListener; + private final int mStep; + private final int mBaseMilli; + private final boolean mIs24hourFormat; + + public interface OnMultiFieldTimeSetListener { + void onTimeSet(int hourOfDay, int minute, int second, int milli); + } + + private static final int SECOND_IN_MILLIS = 1000; + private static final int MINUTE_IN_MILLIS = 60 * SECOND_IN_MILLIS; + private static final int HOUR_IN_MILLIS = 60 * MINUTE_IN_MILLIS; + + public MultiFieldTimePickerDialog( + Context context, + int theme, + int hour, int minute, int second, int milli, + int min, int max, int step, boolean is24hourFormat, + OnMultiFieldTimeSetListener listener) { + super(context, theme); + mListener = listener; + mStep = step; + mIs24hourFormat = is24hourFormat; + + if (min >= max) { + min = 0; + max = 24 * HOUR_IN_MILLIS - 1; + } + if (step < 0 || step >= 24 * HOUR_IN_MILLIS) { + step = MINUTE_IN_MILLIS; + } + + LayoutInflater inflater = + (LayoutInflater) context.getSystemService( + Context.LAYOUT_INFLATER_SERVICE); + View view = inflater.inflate(R.layout.multi_field_time_picker_dialog, null); + setView(view); + + mHourSpinner = (NumberPicker) view.findViewById(R.id.hour); + mMinuteSpinner = (NumberPicker) view.findViewById(R.id.minute); + mSecSpinner = (NumberPicker) view.findViewById(R.id.second); + mMilliSpinner = (NumberPicker) view.findViewById(R.id.milli); + mAmPmSpinner = (NumberPicker) view.findViewById(R.id.ampm); + + int minHour = min / HOUR_IN_MILLIS; + int maxHour = max / HOUR_IN_MILLIS; + min -= minHour * HOUR_IN_MILLIS; + max -= maxHour * HOUR_IN_MILLIS; + + if (minHour == maxHour) { + mHourSpinner.setEnabled(false); + hour = minHour; + } + + if (is24hourFormat) { + mAmPmSpinner.setVisibility(View.GONE); + } else { + int minAmPm = minHour / 12; + int maxAmPm = maxHour / 12; + int amPm = hour / 12; + mAmPmSpinner.setMinValue(minAmPm); + mAmPmSpinner.setMaxValue(maxAmPm); + mAmPmSpinner.setDisplayedValues(new String[] { + context.getString(R.string.time_picker_dialog_am), + context.getString(R.string.time_picker_dialog_pm) + }); + + hour %= 12; + if (hour == 0) { + hour = 12; + } + if (minAmPm == maxAmPm) { + mAmPmSpinner.setEnabled(false); + amPm = minAmPm; + + minHour %= 12; + maxHour %= 12; + if (minHour == 0 && maxHour == 0) { + minHour = 12; + maxHour = 12; + } else if (minHour == 0) { + minHour = maxHour; + maxHour = 12; + } else if (maxHour == 0) { + maxHour = 12; + } + } else { + minHour = 1; + maxHour = 12; + } + mAmPmSpinner.setValue(amPm); + } + + if (minHour == maxHour) { + mHourSpinner.setEnabled(false); + } + mHourSpinner.setMinValue(minHour); + mHourSpinner.setMaxValue(maxHour); + mHourSpinner.setValue(hour); + + NumberFormatter twoDigitPaddingFormatter = new NumberFormatter("%02d"); + + int minMinute = min / MINUTE_IN_MILLIS; + int maxMinute = max / MINUTE_IN_MILLIS; + min -= minMinute * MINUTE_IN_MILLIS; + max -= maxMinute * MINUTE_IN_MILLIS; + + if (minHour == maxHour) { + mMinuteSpinner.setMinValue(minMinute); + mMinuteSpinner.setMaxValue(maxMinute); + if (minMinute == maxMinute) { + // Set this otherwise the box is empty until you stroke it. + mMinuteSpinner.setDisplayedValues( + new String[] { twoDigitPaddingFormatter.format(minMinute) }); + mMinuteSpinner.setEnabled(false); + minute = minMinute; + } + } else { + mMinuteSpinner.setMinValue(0); + mMinuteSpinner.setMaxValue(59); + } + + if (step >= HOUR_IN_MILLIS) { + mMinuteSpinner.setEnabled(false); + } + + mMinuteSpinner.setValue(minute); + mMinuteSpinner.setFormatter(twoDigitPaddingFormatter); + + if (step >= MINUTE_IN_MILLIS) { + // Remove the ':' in front of the second spinner as well. + view.findViewById(R.id.second_colon).setVisibility(View.GONE); + mSecSpinner.setVisibility(View.GONE); + } + + int minSecond = min / SECOND_IN_MILLIS; + int maxSecond = max / SECOND_IN_MILLIS; + min -= minSecond * SECOND_IN_MILLIS; + max -= maxSecond * SECOND_IN_MILLIS; + + if (minHour == maxHour && minMinute == maxMinute) { + mSecSpinner.setMinValue(minSecond); + mSecSpinner.setMaxValue(maxSecond); + if (minSecond == maxSecond) { + // Set this otherwise the box is empty until you stroke it. + mSecSpinner.setDisplayedValues( + new String[] { twoDigitPaddingFormatter.format(minSecond) }); + mSecSpinner.setEnabled(false); + second = minSecond; + } + } else { + mSecSpinner.setMinValue(0); + mSecSpinner.setMaxValue(59); + } + + mSecSpinner.setValue(second); + mSecSpinner.setFormatter(twoDigitPaddingFormatter); + + if (step >= SECOND_IN_MILLIS) { + // Remove the '.' in front of the milli spinner as well. + view.findViewById(R.id.second_dot).setVisibility(View.GONE); + mMilliSpinner.setVisibility(View.GONE); + } + + // Round to the nearest step. + milli = ((milli + step / 2) / step) * step; + if (step == 1 || step == 10 || step == 100) { + if (minHour == maxHour && minMinute == maxMinute && + minSecond == maxSecond) { + mMilliSpinner.setMinValue(min / step); + mMilliSpinner.setMaxValue(max / step); + + if (min == max) { + mMilliSpinner.setEnabled(false); + milli = min; + } + } else { + mMilliSpinner.setMinValue(0); + mMilliSpinner.setMaxValue(999 / step); + } + + if (step == 1) { + mMilliSpinner.setFormatter(new NumberFormatter("%03d")); + } else if (step == 10) { + mMilliSpinner.setFormatter(new NumberFormatter("%02d")); + } else if (step == 100) { + mMilliSpinner.setFormatter(new NumberFormatter("%d")); + } + mMilliSpinner.setValue(milli / step); + mBaseMilli = 0; + } else if (step < SECOND_IN_MILLIS) { + // Non-decimal step value. + ArrayList strValue = new ArrayList(); + for (int i = min; i < max; i += step) { + strValue.add(String.format("%03d", i)); + } + mMilliSpinner.setMinValue(0); + mMilliSpinner.setMaxValue(strValue.size() - 1); + mMilliSpinner.setValue((milli - min) / step); + mMilliSpinner.setDisplayedValues( + strValue.toArray(new String[strValue.size()])); + mBaseMilli = min; + } else { + mBaseMilli = 0; + } + } + + @Override + public void onClick(DialogInterface dialog, int which) { + notifyDateSet(); + } + + private void notifyDateSet() { + int hour = mHourSpinner.getValue(); + int minute = mMinuteSpinner.getValue(); + int sec = mSecSpinner.getValue(); + int milli = mMilliSpinner.getValue() * mStep + mBaseMilli; + if (!mIs24hourFormat) { + int ampm = mAmPmSpinner.getValue(); + if (hour == 12) { + hour = 0; + } + hour += ampm * 12; + } + mListener.onTimeSet(hour, minute, sec, milli); + } + + private static class NumberFormatter implements NumberPicker.Formatter { + private final String mFormat; + + NumberFormatter(String format) { + mFormat = format; + } + + @Override + public String format(int value) { + return String.format(mFormat, value); + } + } +} diff --git a/ui/android/java/src/org/chromium/ui/picker/OWNERS b/ui/android/java/src/org/chromium/ui/picker/OWNERS new file mode 100644 index 0000000..a4238ac --- /dev/null +++ b/ui/android/java/src/org/chromium/ui/picker/OWNERS @@ -0,0 +1,2 @@ +aurimas@chromium.org +miguelg@chromium.org diff --git a/ui/android/java/src/org/chromium/ui/picker/TwoFieldDatePicker.java b/ui/android/java/src/org/chromium/ui/picker/TwoFieldDatePicker.java new file mode 100644 index 0000000..4e84683 --- /dev/null +++ b/ui/android/java/src/org/chromium/ui/picker/TwoFieldDatePicker.java @@ -0,0 +1,250 @@ +// Copyright 2013 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.ui.picker; + +import android.content.Context; +import android.text.format.DateUtils; +import android.view.LayoutInflater; +import android.view.accessibility.AccessibilityEvent; +import android.widget.FrameLayout; +import android.widget.NumberPicker; +import android.widget.NumberPicker.OnValueChangeListener; + +import org.chromium.ui.R; + +import java.util.Calendar; +import java.util.TimeZone; + +/** + * This class is heavily based on android.widget.DatePicker. + */ +public abstract class TwoFieldDatePicker extends FrameLayout { + + private final NumberPicker mPositionInYearSpinner; + + private final NumberPicker mYearSpinner; + + private OnMonthOrWeekChangedListener mMonthOrWeekChangedListener; + + // It'd be nice to use android.text.Time like in other Dialogs but + // it suffers from the 2038 effect so it would prevent us from + // having dates over 2038. + private Calendar mMinDate; + + private Calendar mMaxDate; + + private Calendar mCurrentDate; + + /** + * The callback used to indicate the user changes\d the date. + */ + public interface OnMonthOrWeekChangedListener { + + /** + * Called upon a date change. + * + * @param view The view associated with this listener. + * @param year The year that was set. + * @param positionInYear The month or week in year. + */ + void onMonthOrWeekChanged(TwoFieldDatePicker view, int year, int positionInYear); + } + + public TwoFieldDatePicker(Context context, double minValue, double maxValue) { + super(context, null, android.R.attr.datePickerStyle); + + LayoutInflater inflater = (LayoutInflater) context + .getSystemService(Context.LAYOUT_INFLATER_SERVICE); + inflater.inflate(R.layout.two_field_date_picker, this, true); + + OnValueChangeListener onChangeListener = new OnValueChangeListener() { + @Override + public void onValueChange(NumberPicker picker, int oldVal, int newVal) { + int year = getYear(); + int positionInYear = getPositionInYear(); + // take care of wrapping of days and months to update greater fields + if (picker == mPositionInYearSpinner) { + positionInYear = newVal; + if (oldVal == picker.getMaxValue() && newVal == picker.getMinValue()) { + year += 1; + positionInYear = getMinPositionInYear(year); + } else if (oldVal == picker.getMinValue() && newVal == picker.getMaxValue()) { + year -= 1; + positionInYear = getMaxPositionInYear(year); + } + } else if (picker == mYearSpinner) { + year = newVal; + } else { + throw new IllegalArgumentException(); + } + + // now set the date to the adjusted one + setCurrentDate(year, positionInYear); + updateSpinners(); + notifyDateChanged(); + } + }; + + mCurrentDate = Calendar.getInstance(TimeZone.getTimeZone("UTC")); + if (minValue >= maxValue) { + mMinDate = Calendar.getInstance(TimeZone.getTimeZone("UTC")); + mMinDate.set(0, 0, 1); + mMaxDate = Calendar.getInstance(TimeZone.getTimeZone("UTC")); + mMaxDate.set(9999, 0, 1); + } else { + mMinDate = getDateForValue(minValue); + mMaxDate = getDateForValue(maxValue); + } + + // month + mPositionInYearSpinner = (NumberPicker) findViewById(R.id.position_in_year); + mPositionInYearSpinner.setOnLongPressUpdateInterval(200); + mPositionInYearSpinner.setOnValueChangedListener(onChangeListener); + + // year + mYearSpinner = (NumberPicker) findViewById(R.id.year); + mYearSpinner.setOnLongPressUpdateInterval(100); + mYearSpinner.setOnValueChangedListener(onChangeListener); + } + + /** + * Initialize the state. If the provided values designate an inconsistent + * date the values are normalized before updating the spinners. + * + * @param year The initial year. + * @param positionInYear The initial month starting from zero or week in year. + * @param onMonthOrWeekChangedListener How user is notified date is changed by + * user, can be null. + */ + public void init(int year, int positionInYear, + OnMonthOrWeekChangedListener onMonthOrWeekChangedListener) { + setCurrentDate(year, positionInYear); + updateSpinners(); + mMonthOrWeekChangedListener = onMonthOrWeekChangedListener; + } + + public boolean isNewDate(int year, int positionInYear) { + return (getYear() != year || getPositionInYear() != positionInYear); + } + + /** + * Subclasses know the semantics of @value, and need to return + * a Calendar corresponding to it. + */ + protected abstract Calendar getDateForValue(double value); + + /** + * Updates the current date. + * + * @param year The year. + * @param positionInYear The month or week in year. + */ + public void updateDate(int year, int positionInYear) { + if (!isNewDate(year, positionInYear)) { + return; + } + setCurrentDate(year, positionInYear); + updateSpinners(); + notifyDateChanged(); + } + + /** + * Subclasses know the semantics of @positionInYear, and need to update @mCurrentDate to the + * appropriate date. + */ + protected abstract void setCurrentDate(int year, int positionInYear); + + protected void setCurrentDate(Calendar date) { + mCurrentDate = date; + } + + @Override + public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { + onPopulateAccessibilityEvent(event); + return true; + } + + @Override + public void onPopulateAccessibilityEvent(AccessibilityEvent event) { + super.onPopulateAccessibilityEvent(event); + + final int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR; + String selectedDateUtterance = DateUtils.formatDateTime(getContext(), + mCurrentDate.getTimeInMillis(), flags); + event.getText().add(selectedDateUtterance); + } + + /** + * @return The selected year. + */ + public int getYear() { + return mCurrentDate.get(Calendar.YEAR); + } + + /** + * @return The selected month or week. + */ + public abstract int getPositionInYear(); + + protected abstract int getMaxYear(); + + protected abstract int getMinYear(); + + protected abstract int getMaxPositionInYear(int year); + + protected abstract int getMinPositionInYear(int year); + + protected Calendar getMaxDate() { + return mMaxDate; + } + + protected Calendar getMinDate() { + return mMinDate; + } + + protected Calendar getCurrentDate() { + return mCurrentDate; + } + + protected NumberPicker getPositionInYearSpinner() { + return mPositionInYearSpinner; + } + + protected NumberPicker getYearSpinner() { + return mYearSpinner; + } + + /** + * This method should be subclassed to update the spinners based on mCurrentDate. + */ + protected void updateSpinners() { + mPositionInYearSpinner.setDisplayedValues(null); + + // set the spinner ranges respecting the min and max dates + mPositionInYearSpinner.setMinValue(getMinPositionInYear(getYear())); + mPositionInYearSpinner.setMaxValue(getMaxPositionInYear(getYear())); + mPositionInYearSpinner.setWrapSelectorWheel( + !mCurrentDate.equals(mMinDate) && !mCurrentDate.equals(mMaxDate)); + + // year spinner range does not change based on the current date + mYearSpinner.setMinValue(getMinYear()); + mYearSpinner.setMaxValue(getMaxYear()); + mYearSpinner.setWrapSelectorWheel(false); + + // set the spinner values + mYearSpinner.setValue(getYear()); + mPositionInYearSpinner.setValue(getPositionInYear()); + } + + /** + * Notifies the listener, if such, for a change in the selected date. + */ + protected void notifyDateChanged() { + sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED); + if (mMonthOrWeekChangedListener != null) { + mMonthOrWeekChangedListener.onMonthOrWeekChanged(this, getYear(), getPositionInYear()); + } + } +} diff --git a/ui/android/java/src/org/chromium/ui/picker/TwoFieldDatePickerDialog.java b/ui/android/java/src/org/chromium/ui/picker/TwoFieldDatePickerDialog.java new file mode 100644 index 0000000..e3f01e7 --- /dev/null +++ b/ui/android/java/src/org/chromium/ui/picker/TwoFieldDatePickerDialog.java @@ -0,0 +1,113 @@ +// Copyright 2013 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.ui.picker; + +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.DialogInterface.OnClickListener; + +import org.chromium.ui.R; +import org.chromium.ui.picker.TwoFieldDatePicker.OnMonthOrWeekChangedListener; + +public abstract class TwoFieldDatePickerDialog extends AlertDialog implements OnClickListener, + OnMonthOrWeekChangedListener { + + private static final String YEAR = "year"; + private static final String POSITION_IN_YEAR = "position_in_year"; + + protected final TwoFieldDatePicker mPicker; + protected final OnValueSetListener mCallBack; + + /** + * The callback used to indicate the user is done filling in the date. + */ + public interface OnValueSetListener { + + /** + * @param year The year that was set. + * @param positionInYear The position in the year that was set. + */ + void onValueSet(int year, int positionInYear); + } + + /** + * @param context The context the dialog is to run in. + * @param callBack How the parent is notified that the date is set. + * @param year The initial year of the dialog. + * @param weekOfYear The initial week of the dialog. + */ + public TwoFieldDatePickerDialog(Context context, + OnValueSetListener callBack, + int year, + int positionInYear, + double minValue, + double maxValue) { + this(context, 0, callBack, year, positionInYear, minValue, maxValue); + } + + /** + * @param context The context the dialog is to run in. + * @param theme the theme to apply to this dialog + * @param callBack How the parent is notified that the date is set. + * @param year The initial year of the dialog. + * @param weekOfYear The initial week of the dialog. + */ + public TwoFieldDatePickerDialog(Context context, + int theme, + OnValueSetListener callBack, + int year, + int positionInYear, + double minValue, + double maxValue) { + super(context, theme); + + mCallBack = callBack; + + setButton(BUTTON_POSITIVE, context.getText( + R.string.date_picker_dialog_set), this); + setButton(BUTTON_NEGATIVE, context.getText(android.R.string.cancel), + (OnClickListener) null); + setIcon(0); + + mPicker = createPicker(context, minValue, maxValue); + setView(mPicker); + mPicker.init(year, positionInYear, this); + } + + protected TwoFieldDatePicker createPicker(Context context, double minValue, double maxValue) { + return null; + } + + @Override + public void onClick(DialogInterface dialog, int which) { + tryNotifyDateSet(); + } + + /** + * Notifies the listener, if such, that a date has been set. + */ + protected void tryNotifyDateSet() { + if (mCallBack != null) { + mPicker.clearFocus(); + mCallBack.onValueSet(mPicker.getYear(), mPicker.getPositionInYear()); + } + } + + @Override + public void onMonthOrWeekChanged(TwoFieldDatePicker view, int year, int positionInYear) { + mPicker.init(year, positionInYear, null); + } + + /** + * Sets the current date. + * + * @param year The date week year. + * @param weekOfYear The date week. + */ + public void updateDate(int year, int weekOfYear) { + mPicker.updateDate(year, weekOfYear); + } +} diff --git a/ui/android/java/src/org/chromium/ui/picker/WeekPicker.java b/ui/android/java/src/org/chromium/ui/picker/WeekPicker.java new file mode 100644 index 0000000..bb9a25d --- /dev/null +++ b/ui/android/java/src/org/chromium/ui/picker/WeekPicker.java @@ -0,0 +1,141 @@ +// Copyright 2013 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.ui.picker; + +import android.content.Context; + +import org.chromium.ui.R; + +import java.util.Calendar; +import java.util.TimeZone; + +// This class is heavily based on android.widget.DatePicker. +public class WeekPicker extends TwoFieldDatePicker { + + public WeekPicker(Context context, double minValue, double maxValue) { + super(context, minValue, maxValue); + + getPositionInYearSpinner().setContentDescription( + getResources().getString(R.string.accessibility_date_picker_week)); + + // initialize to current date + Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC")); + cal.setFirstDayOfWeek(Calendar.MONDAY); + cal.setMinimalDaysInFirstWeek(4); + cal.setTimeInMillis(System.currentTimeMillis()); + init(getISOWeekYearForDate(cal), getWeekForDate(cal), null); + } + + /** + * Creates a date object from the |year| and |week|. + */ + public static Calendar createDateFromWeek(int year, int week) { + Calendar date = Calendar.getInstance(TimeZone.getTimeZone("UTC")); + date.clear(); + date.setFirstDayOfWeek(Calendar.MONDAY); + date.setMinimalDaysInFirstWeek(4); + date.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY); + date.set(Calendar.YEAR, year); + date.set(Calendar.WEEK_OF_YEAR, week); + return date; + } + + /** + * Creates a date object from the |value| which is milliseconds since epoch. + */ + public static Calendar createDateFromValue(double value) { + Calendar date = Calendar.getInstance(TimeZone.getTimeZone("UTC")); + date.clear(); + date.setFirstDayOfWeek(Calendar.MONDAY); + date.setMinimalDaysInFirstWeek(4); + date.setTimeInMillis((long) value); + return date; + } + + @Override + protected Calendar getDateForValue(double value) { + return WeekPicker.createDateFromValue(value); + } + + public static int getISOWeekYearForDate(Calendar date) { + int year = date.get(Calendar.YEAR); + int month = date.get(Calendar.MONTH); + int week = date.get(Calendar.WEEK_OF_YEAR); + if (month == 0 && week > 51) { + year--; + } else if (month == 11 && week == 1) { + year++; + } + return year; + } + + public static int getWeekForDate(Calendar date) { + return date.get(Calendar.WEEK_OF_YEAR); + } + + @Override + protected void setCurrentDate(int year, int week) { + Calendar date = createDateFromWeek(year, week); + if (date.before(getMinDate())) { + setCurrentDate(getMinDate()); + } else if (date.after(getMaxDate())) { + setCurrentDate(getMaxDate()); + } else { + setCurrentDate(date); + } + } + + private int getNumberOfWeeks(int year) { + // Create a date in the middle of the year, where the week year matches the year. + Calendar date = createDateFromWeek(year, 20); + return date.getActualMaximum(Calendar.WEEK_OF_YEAR); + } + + /** + * @return The selected year. + */ + @Override + public int getYear() { + return getISOWeekYearForDate(getCurrentDate()); + } + + /** + * @return The selected week. + */ + public int getWeek() { + return getWeekForDate(getCurrentDate()); + } + + @Override + public int getPositionInYear() { + return getWeek(); + } + + @Override + protected int getMaxYear() { + return getISOWeekYearForDate(getMaxDate()); + } + + @Override + protected int getMinYear() { + return getISOWeekYearForDate(getMinDate()); + } + + @Override + protected int getMaxPositionInYear(int year) { + if (year == getISOWeekYearForDate(getMaxDate())) { + return getWeekForDate(getMaxDate()); + } + return getNumberOfWeeks(year); + } + + @Override + protected int getMinPositionInYear(int year) { + if (year == getISOWeekYearForDate(getMinDate())) { + return getWeekForDate(getMinDate()); + } + return 1; + } +} diff --git a/ui/android/java/src/org/chromium/ui/picker/WeekPickerDialog.java b/ui/android/java/src/org/chromium/ui/picker/WeekPickerDialog.java new file mode 100644 index 0000000..e769c1a --- /dev/null +++ b/ui/android/java/src/org/chromium/ui/picker/WeekPickerDialog.java @@ -0,0 +1,56 @@ +// Copyright 2013 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.ui.picker; + +import android.content.Context; + +import org.chromium.ui.R; + +public class WeekPickerDialog extends TwoFieldDatePickerDialog { + + /** + * @param context The context the dialog is to run in. + * @param callBack How the parent is notified that the date is set. + * @param year The initial year of the dialog. + * @param weekOfYear The initial week of the dialog. + */ + public WeekPickerDialog(Context context, + OnValueSetListener callBack, + int year, int weekOfYear, + double minValue, double maxValue) { + this(context, 0, callBack, year, weekOfYear, minValue, maxValue); + } + + /** + * @param context The context the dialog is to run in. + * @param theme the theme to apply to this dialog + * @param callBack How the parent is notified that the date is set. + * @param year The initial year of the dialog. + * @param weekOfYear The initial week of the dialog. + */ + public WeekPickerDialog(Context context, + int theme, + OnValueSetListener callBack, + int year, + int weekOfYear, + double minValue, double maxValue) { + super(context, theme, callBack, year, weekOfYear, minValue, maxValue); + setTitle(R.string.week_picker_dialog_title); + } + + @Override + protected TwoFieldDatePicker createPicker(Context context, double minValue, double maxValue) { + return new WeekPicker(context, minValue, maxValue); + } + + /** + * Gets the {@link WeekPicker} contained in this dialog. + * + * @return The calendar view. + */ + public WeekPicker getWeekPicker() { + return (WeekPicker) mPicker; + } +} diff --git a/ui/android/java/strings/android_ui_strings.grd b/ui/android/java/strings/android_ui_strings.grd index 2ad816e..c2a42a5 100644 --- a/ui/android/java/strings/android_ui_strings.grd +++ b/ui/android/java/strings/android_ui_strings.grd @@ -147,6 +147,75 @@ Failed to copy to the clipboard + + Month + + + Year + + + Set + + + Set month + + + Week + + + Set week + + + AM + + + PM + + + Set time + + + Hour + + + Minute + + + Second + + + Millisecond + + + AM/PM + + + : + + + : + + + . + + + Set date and time + + + Date + + + Time + + + Other + + + Set date + + + Clear + -- cgit v1.1