diff options
author | anton@chromium.org <anton@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-08-16 14:59:09 +0000 |
---|---|---|
committer | anton@chromium.org <anton@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-08-16 14:59:09 +0000 |
commit | ee59cbecde3b8b22c15609f51528173482a819df (patch) | |
tree | 65dc6d5cb1801898fe65ae6fa09527816c18dab7 | |
parent | b54de4ef76ec4b8e5ab7f9121a09b98adb92f088 (diff) | |
download | chromium_src-ee59cbecde3b8b22c15609f51528173482a819df.zip chromium_src-ee59cbecde3b8b22c15609f51528173482a819df.tar.gz chromium_src-ee59cbecde3b8b22c15609f51528173482a819df.tar.bz2 |
Support for seconds and milliseconds in the time input dialog.
C++ reformatting by "git cl format".
Previously LGTM in https://codereview.chromium.org/21907003/
TBR=jochen,bulach,palmer
BUG=164563
TEST=RendererDateTimePickerTest.*
Review URL: https://chromiumcodereview.appspot.com/22921005
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@218020 0039d316-1c4b-4281-b951-d872f2087c98
15 files changed, 811 insertions, 162 deletions
diff --git a/content/browser/android/date_time_chooser_android.cc b/content/browser/android/date_time_chooser_android.cc index 0c3ba2d..7c91ff0 100644 --- a/content/browser/android/date_time_chooser_android.cc +++ b/content/browser/android/date_time_chooser_android.cc @@ -24,7 +24,14 @@ class DateTimeChooserAndroid::DateTimeIPCSender : explicit DateTimeIPCSender(RenderViewHost* sender); virtual ~DateTimeIPCSender() {} void ReplaceDateTime(int dialog_type, - int year, int month, int day, int hour, int minute, int second, int week); + int year, + int month, + int day, + int hour, + int minute, + int second, + int milli, + int week); void CancelDialog(); private: @@ -36,9 +43,15 @@ DateTimeChooserAndroid::DateTimeIPCSender::DateTimeIPCSender( : RenderViewHostObserver(sender) { } -void DateTimeChooserAndroid::DateTimeIPCSender::ReplaceDateTime( - int dialog_type, - int year, int month, int day, int hour, int minute, int second, int week) { +void DateTimeChooserAndroid::DateTimeIPCSender::ReplaceDateTime(int dialog_type, + int year, + int month, + int day, + int hour, + int minute, + int second, + int milli, + int week) { ViewHostMsg_DateTimeDialogValue_Params value; value.year = year; value.month = month; @@ -46,6 +59,7 @@ void DateTimeChooserAndroid::DateTimeIPCSender::ReplaceDateTime( value.hour = hour; value.minute = minute; value.second = second; + value.milli = milli; value.week = week; value.dialog_type = dialog_type; Send(new ViewMsg_ReplaceDateTime(routing_id(), value)); @@ -76,30 +90,60 @@ void DateTimeChooserAndroid::InitializeDateInputTypes( text_input_type_time, text_input_type_week); } -void DateTimeChooserAndroid::ReplaceDateTime( - JNIEnv* env, jobject, int dialog_type, - int year, int month, int day, int hour, int minute, int second, int week) { +void DateTimeChooserAndroid::ReplaceDateTime(JNIEnv* env, + jobject, + int dialog_type, + int year, + int month, + int day, + int hour, + int minute, + int second, + int milli, + int week) { sender_->ReplaceDateTime( - dialog_type, year, month, day, hour, minute, second, week); + dialog_type, year, month, day, hour, minute, second, milli, week); } void DateTimeChooserAndroid::CancelDialog(JNIEnv* env, jobject) { sender_->CancelDialog(); } -void DateTimeChooserAndroid::ShowDialog( - ContentViewCore* content, RenderViewHost* sender, - int type, int year, int month, int day, - int hour, int minute, int second, int week, double min, double max) { +void DateTimeChooserAndroid::ShowDialog(ContentViewCore* content, + RenderViewHost* sender, + int type, + int year, + int month, + int day, + int hour, + int minute, + int second, + int milli, + int week, + double min, + double max, + double step) { if (sender_) delete sender_; sender_ = new DateTimeIPCSender(sender); JNIEnv* env = AttachCurrentThread(); j_date_time_chooser_.Reset(Java_DateTimeChooserAndroid_createDateTimeChooser( - env, content->GetJavaObject().obj(), + env, + content->GetJavaObject().obj(), reinterpret_cast<intptr_t>(this), - type, year, month, day, hour, minute, second, week, min, max)); + type, + year, + month, + day, + hour, + minute, + second, + milli, + week, + min, + max, + step)); } // ---------------------------------------------------------------------------- diff --git a/content/browser/android/date_time_chooser_android.h b/content/browser/android/date_time_chooser_android.h index 646ba3c..68cb456 100644 --- a/content/browser/android/date_time_chooser_android.h +++ b/content/browser/android/date_time_chooser_android.h @@ -24,14 +24,31 @@ class DateTimeChooserAndroid { // DateTimeChooser implementation: void ShowDialog(ContentViewCore* content, RenderViewHost* sender, - int type, int year, int month, int day, - int hour, int minute, int second, int week, - double min, double max); + int type, + int year, + int month, + int day, + int hour, + int minute, + int second, + int milli, + int week, + double min, + double max, + double step); // Replaces the current value with the one passed the different fields - void ReplaceDateTime(JNIEnv* env, jobject, jint dialog_type, - jint year, jint month, jint day, - jint hour, jint minute, jint second, jint week); + void ReplaceDateTime(JNIEnv* env, + jobject, + jint dialog_type, + jint year, + jint month, + jint day, + jint hour, + jint minute, + jint second, + jint milli, + jint week); // Closes the dialog without propagating any changes. void CancelDialog(JNIEnv* env, jobject); diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc index cac5385..3ed080d 100644 --- a/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc @@ -2428,10 +2428,20 @@ void WebContentsImpl::OnFindMatchRectsReply( void WebContentsImpl::OnOpenDateTimeDialog( const ViewHostMsg_DateTimeDialogValue_Params& value) { - date_time_chooser_->ShowDialog( - ContentViewCore::FromWebContents(this), GetRenderViewHost(), - value.dialog_type, value.year, value.month, value.day, value.hour, - value.minute, value.second, value.week, value.minimum, value.maximum); + date_time_chooser_->ShowDialog(ContentViewCore::FromWebContents(this), + GetRenderViewHost(), + value.dialog_type, + value.year, + value.month, + value.day, + value.hour, + value.minute, + value.second, + value.milli, + value.week, + value.minimum, + value.maximum, + value.step); } #endif diff --git a/content/common/view_messages.h b/content/common/view_messages.h index d1b558bf..2414164 100644 --- a/content/common/view_messages.h +++ b/content/common/view_messages.h @@ -390,9 +390,11 @@ IPC_STRUCT_BEGIN(ViewHostMsg_DateTimeDialogValue_Params) IPC_STRUCT_MEMBER(int, hour) IPC_STRUCT_MEMBER(int, minute) IPC_STRUCT_MEMBER(int, second) + IPC_STRUCT_MEMBER(int, milli) IPC_STRUCT_MEMBER(int, week) IPC_STRUCT_MEMBER(double, minimum) IPC_STRUCT_MEMBER(double, maximum) + IPC_STRUCT_MEMBER(double, step) IPC_STRUCT_END() IPC_STRUCT_BEGIN(ViewHostMsg_DidFailProvisionalLoadWithError_Params) 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 new file mode 100644 index 0000000..f6dfec3 --- /dev/null +++ b/content/public/android/java/res/layout/multi_field_time_picker_dialog.xml @@ -0,0 +1,122 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_vertical" + android:orientation="vertical" + android:gravity="start"> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/time_picker_dialog_title" + android:textSize="20sp" + android:paddingStart="5dp" + android:paddingEnd="5dp" + android:paddingBottom="5dp" + android:paddingTop="5dp" + android:gravity="start" + android:textColor="#33B5E5" + /> + + <View + android:layout_width="fill_parent" + android:layout_height="2dp" + android:paddingBottom="5dp" + android:paddingTop="5dp" + android:background="#33B5E5"/> + + <LinearLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_horizontal" + android:orientation="horizontal" + android:gravity="center"> + + <NumberPicker + android:id="@+id/hour" + android:layout_width="0dip" + android:layout_weight=".2" + android:textSize="12sp" + android:layout_height="wrap_content" + android:layout_marginStart="4dip" + android:layout_marginEnd="4dip" + android:focusable="true" + android:focusableInTouchMode="true" + android:contentDescription="@string/accessibility_time_picker_hour" + /> + <TextView + android:layout_width="wrap_content" + android:textSize="12sp" + android:layout_height="wrap_content" + android:text="@string/time_picker_dialog_hour_minute_separator" + /> + <NumberPicker + android:id="@+id/minute" + android:layout_width="0dip" + android:layout_weight=".2" + android:textSize="12sp" + android:layout_height="wrap_content" + android:layout_marginStart="4dip" + android:layout_marginEnd="4dip" + android:focusable="true" + android:focusableInTouchMode="true" + android:contentDescription="@string/accessibility_time_picker_minute" + /> + <TextView + android:id="@+id/second_colon" + android:layout_width="wrap_content" + android:textSize="12sp" + android:layout_height="wrap_content" + android:text="@string/time_picker_dialog_minute_second_separator" /> + <NumberPicker + android:id="@+id/second" + android:layout_width="0dip" + android:layout_weight=".2" + android:textSize="12sp" + android:layout_height="wrap_content" + android:layout_marginStart="4dip" + android:layout_marginEnd="4dip" + android:focusable="true" + android:focusableInTouchMode="true" + android:contentDescription="@string/accessibility_time_picker_second" + /> + <TextView + android:id="@+id/second_dot" + android:layout_width="wrap_content" + android:textSize="12sp" + android:layout_height="wrap_content" + android:text="@string/time_picker_dialog_second_subsecond_separator" + /> + <NumberPicker + android:id="@+id/milli" + android:layout_width="0dip" + android:layout_weight=".2" + android:textSize="12sp" + android:layout_height="wrap_content" + android:layout_marginStart="4dip" + android:layout_marginEnd="4dip" + android:focusable="true" + android:focusableInTouchMode="true" + android:contentDescription="@string/accessibility_time_picker_milli" + /> + <NumberPicker + android:id="@+id/ampm" + android:layout_width="0dip" + android:layout_weight=".2" + android:textSize="12sp" + android:layout_height="wrap_content" + android:layout_marginStart="4dip" + android:layout_marginEnd="4dip" + android:focusable="true" + android:focusableInTouchMode="true" + android:contentDescription="@string/accessibility_time_picker_ampm" + /> + </LinearLayout> +</LinearLayout> 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 ea93683..4b0ef83 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 @@ -25,15 +25,23 @@ public final class R { public static int ondemand_overlay; } public static final class id { + public static int ampm; public static int date_picker; - public static int position_in_year; + public static int hour; + public static int milli; + public static int minute; public static int pickers; + public static int position_in_year; + public static int second; + public static int second_colon; + public static int second_dot; public static int time_picker; public static int year; } public static final class layout { public static int date_time_picker_dialog; public static int two_field_date_picker; + public static int multi_field_time_picker_dialog; } public static final class string { public static int accessibility_content_view; @@ -54,6 +62,11 @@ public final class R { public static int media_player_error_title; public static int media_player_loading_video; public static int month_picker_dialog_title; + public static int time_picker_dialog_am; + public static int time_picker_dialog_hour_minute_separator; + 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 week_picker_dialog_title; } } 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 0c58217..9ca9f95 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 @@ -28,10 +28,11 @@ class DateTimeChooserAndroid { @Override public void replaceDateTime( int dialogType, - int year, int month, int day, int hour, int minute, int second, int week) { + int year, int month, int day, int hour, int minute, + int second, int milli, int week) { nativeReplaceDateTime(mNativeDateTimeChooserAndroid, dialogType, - year, month, day, hour, minute, second, week); + year, month, day, hour, minute, second, milli, week); } @Override @@ -42,9 +43,11 @@ class DateTimeChooserAndroid { } private void showDialog(int dialogType, int year, int month, int monthDay, - int hour, int minute, int second, int week, double min, double max) { - mInputDialogContainer.showDialog(dialogType, year, month, monthDay, - hour, minute, second, week, min, max); + int hour, int minute, int second, int milli, + int week, double min, double max, double step) { + mInputDialogContainer.showDialog( + dialogType, year, month, monthDay, + hour, minute, second, milli, week, min, max, step); } @CalledByNative @@ -52,16 +55,21 @@ class DateTimeChooserAndroid { ContentViewCore contentViewCore, int nativeDateTimeChooserAndroid, int dialogType, int year, int month, int day, - int hour, int minute, int second, int week, double min, double max) { + int hour, int minute, int second, int milli, int week, + double min, double max, double step) { DateTimeChooserAndroid chooser = new DateTimeChooserAndroid( - contentViewCore.getContext(), nativeDateTimeChooserAndroid); - chooser.showDialog(dialogType, year, month, day, hour, minute, second, week, min, max); + contentViewCore.getContext(), + nativeDateTimeChooserAndroid); + chooser.showDialog( + dialogType, year, month, day, hour, minute, second, milli, + week, min, max, step); return chooser; } @CalledByNative - private static void initializeDateInputTypes(int textInputTypeDate, int textInputTypeDateTime, + private static void initializeDateInputTypes( + int textInputTypeDate, int textInputTypeDateTime, int textInputTypeDateTimeLocal, int textInputTypeMonth, int textInputTypeTime, int textInputTypeWeek) { InputDialogContainer.initializeInputTypes(textInputTypeDate, @@ -69,9 +77,10 @@ class DateTimeChooserAndroid { textInputTypeMonth, textInputTypeTime, textInputTypeWeek); } - private native void nativeReplaceDateTime(int nativeDateTimeChooserAndroid, - int dialogType, - int year, int month, int day, int hour, int minute, int second, int week); + private native void nativeReplaceDateTime( + int nativeDateTimeChooserAndroid, int dialogType, + int year, int month, int day, int hour, int minute, + int second, int milli, int week); private native void nativeCancelDialog(int nativeDateTimeChooserAndroid); } 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 index 07dd7e0..fffe130 100644 --- 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 @@ -19,6 +19,7 @@ import android.widget.DatePicker; import android.widget.TimePicker; import org.chromium.content.browser.input.DateTimePickerDialog.OnDateTimeSetListener; +import org.chromium.content.browser.input.MultiFieldTimePickerDialog.OnMultiFieldTimeSetListener; import org.chromium.content.browser.input.TwoFieldDatePickerDialog; import org.chromium.content.R; @@ -32,7 +33,7 @@ public class InputDialogContainer { interface InputActionDelegate { void cancelDateTimeDialog(); void replaceDateTime(int dialogType, - int year, int month, int day, int hour, int minute, int second, int week); + int year, int month, int day, int hour, int minute, int second, int milli, int week); } // Default values used in Time representations of selected date/time before formatting. @@ -93,7 +94,7 @@ public class InputDialogContainer { } private Time normalizeTime(int year, int month, int monthDay, - int hour, int minute, int second) { + int hour, int minute, int second) { Time result = new Time(); if (year == 0 && month == 0 && monthDay == 0 && hour == 0 && minute == 0 && second == 0) { @@ -108,7 +109,8 @@ public class InputDialogContainer { } void showDialog(final int dialogType, int year, int month, int monthDay, - int hour, int minute, int second, int week, double min, double max) { + int hour, int minute, int second, int milli, int week, + double min, double max, double step) { if (isDialogShowing()) mDialog.dismiss(); // Java Date dialogs like longs but Blink prefers doubles.. @@ -118,7 +120,12 @@ public class InputDialogContainer { // In any case the cast here is safe given the above restrictions. long minTime = (long) min; long maxTime = (long) max; + int stepTime = (int) step; + if (milli > 1000) { + second += milli / 1000; + milli %= 1000; + } Time time = normalizeTime(year, month, monthDay, hour, minute, second); if (dialogType == sTextInputTypeDate) { DatePickerDialog dialog = new DatePickerDialog(mContext, @@ -129,9 +136,12 @@ public class InputDialogContainer { dialog.setTitle(mContext.getText(R.string.date_picker_dialog_title)); mDialog = dialog; } else if (dialogType == sTextInputTypeTime) { - mDialog = TimeDialog.create(mContext, new TimeListener(dialogType), - time.hour, time.minute, DateFormat.is24HourFormat(mContext), - minTime, maxTime); + mDialog = new MultiFieldTimePickerDialog( + mContext, 0 /* theme */ , + time.hour, time.minute, time.second, milli, + (int) minTime, (int) maxTime, stepTime, + DateFormat.is24HourFormat(mContext), + new FullTimeListener(dialogType)); } else if (dialogType == sTextInputTypeDateTime || dialogType == sTextInputTypeDateTimeLocal) { mDialog = new DateTimePickerDialog(mContext, @@ -172,7 +182,7 @@ public class InputDialogContainer { @Override public void onClick(DialogInterface dialog, int which) { mDialogAlreadyDismissed = true; - mInputActionDelegate.replaceDateTime(dialogType, 0, 0, 0, 0, 0, 0, 0); + mInputActionDelegate.replaceDateTime(dialogType, 0, 0, 0, 0, 0, 0, 0, 0); } }); @@ -223,6 +233,22 @@ public class InputDialogContainer { } } + private class FullTimeListener implements OnMultiFieldTimeSetListener { + private final int mDialogType; + FullTimeListener(int dialogType) { + mDialogType = dialogType; + } + + @Override + public void onTimeSet(int hourOfDay, int minute, int second, int milli) { + if (!mDialogAlreadyDismissed) { + setFieldDateTimeValue(mDialogType, + YEAR_DEFAULT, MONTH_DEFAULT, MONTHDAY_DEFAULT, + hourOfDay, minute, second, milli, WEEK_DEFAULT, HTML_TIME_FORMAT); + } + } + } + private class DateTimeListener implements OnDateTimeSetListener { private final boolean mLocal; private final int mDialogType; @@ -274,6 +300,17 @@ public class InputDialogContainer { mDialogAlreadyDismissed = true; mInputActionDelegate.replaceDateTime(dialogType, - year, month, monthDay, hourOfDay, minute, 0 /* second */, week); + year, month, monthDay, hourOfDay, minute, 0 /* second */, 0 /* milli */, week); + } + + private void setFieldDateTimeValue(int dialogType, + int year, int month, int monthDay, int hourOfDay, + int minute, int second, int milli, int week, String dateFormat) { + // Prevents more than one callback being sent to the native + // side when the dialog triggers multiple events. + mDialogAlreadyDismissed = true; + + mInputActionDelegate.replaceDateTime( + dialogType, year, month, monthDay, hourOfDay, minute, second, milli, week); } } 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 new file mode 100644 index 0000000..f2ab32b --- /dev/null +++ b/content/public/android/java/src/org/chromium/content/browser/input/MultiFieldTimePickerDialog.java @@ -0,0 +1,288 @@ +// 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.os.Build; +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 final static int SECOND_IN_MILLIS = 1000; + private final static int MINUTE_IN_MILLIS = 60 * SECOND_IN_MILLIS; + private final static 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<String> strValue = new ArrayList<String>(); + 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); + } + + @Override + protected void onStop() { + if (Build.VERSION.SDK_INT >= 16) { + // The default behavior of dialogs changed in JellyBean and onwards. + // Dismissing a dialog (by pressing back for example) + // applies the chosen date. This code is added here so that the custom + // pickers behave the same as the internal DatePickerDialog. + notifyDateSet(); + } + super.onStop(); + } + + 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/TimeDialog.java b/content/public/android/java/src/org/chromium/content/browser/input/TimeDialog.java deleted file mode 100644 index 60d196f..0000000 --- a/content/public/android/java/src/org/chromium/content/browser/input/TimeDialog.java +++ /dev/null @@ -1,70 +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.TimePickerDialog; -import android.content.Context; -import android.text.format.Time; -import android.widget.TimePicker; - -/** - * Wrapper on {@code TimePickerDialog} to control min and max times. - */ -public class TimeDialog extends TimePickerDialog { - - private Time mMinTime; - private Time mMaxTime; - - public static TimeDialog create(Context context, OnTimeSetListener callBack, - int hour, int minute, boolean is24HourView, long min, long max) { - Time time = getBoundedTime(hour, minute, min, max); - return new TimeDialog(context, callBack, time.hour, time.minute, - is24HourView, min, max); - } - - private TimeDialog( - Context context, OnTimeSetListener callBack, - int hourOfDay, int minute, boolean is24HourView, long min, long max) { - super(context, callBack, hourOfDay, minute, is24HourView); - if (min >= max) { - mMinTime = getTimeForHourAndMinute(0, 0); - mMaxTime = getTimeForHourAndMinute(23, 59); - } else { - mMinTime = getTimeForMillis(min); - mMaxTime = getTimeForMillis(max); - } - } - - @Override - public void onTimeChanged(TimePicker view, int hourOfDay, int minute) { - Time time = getBoundedTime(hourOfDay, minute, - mMinTime.toMillis(true), mMaxTime.toMillis(true)); - super.onTimeChanged(view, time.hour, time.minute); - updateTime(time.hour, time.minute); - } - - private static Time getBoundedTime(int hour, int minute, - long min, long max) { - Time time = getTimeForHourAndMinute(hour, minute); - if (time.toMillis(true) < min) { - return getTimeForMillis(min); - } else if (time.toMillis(true) > max) { - return getTimeForMillis(max); - } - return time; - } - - private static Time getTimeForMillis(long millis) { - Time time = new Time("GMT"); - time.set(millis); - return time; - } - - private static Time getTimeForHourAndMinute(int hour, int minute) { - Time time = new Time("GMT"); - time.set(0, minute, hour, 1, 0, 1970); - return time; - } -} diff --git a/content/public/android/java/strings/android_content_strings.grd b/content/public/android/java/strings/android_content_strings.grd index 69aabb4..6589a0f 100644 --- a/content/public/android/java/strings/android_content_strings.grd +++ b/content/public/android/java/strings/android_content_strings.grd @@ -11,10 +11,10 @@ <message desc="Content description for the content view that holds the web contents [CHAR-LIMIT=32]" name="IDS_ACCESSIBILITY_CONTENT_VIEW"> Web View </message> - <message desc="Label for 'set' button in date picker dialog, used to replace the contents of a field with the chosen date [CHAR-LIMIT=12]" name="IDS_DATE_PICKER_DIALOG_SET"> + <message desc="Label for 'set' button in date picker dialog and time picker dialog, used to replace the contents of a field with the chosen date or time [CHAR-LIMIT=12]" name="IDS_DATE_PICKER_DIALOG_SET"> Set </message> - <message desc="Label for 'clear' button in date picker dialog, used to replace the contents of a field with the empty string [CHAR-LIMIT=12]" name="IDS_DATE_PICKER_DIALOG_CLEAR"> + <message desc="Label for 'clear' button in date picker dialog and time picker dialog, used to replace the contents of a field with the empty string [CHAR-LIMIT=12]" name="IDS_DATE_PICKER_DIALOG_CLEAR"> Clear </message> <message desc="Title for the date picker dialog, which can be used to choose a date. [CHAR-LIMIT=32]" name="IDS_DATE_PICKER_DIALOG_TITLE"> @@ -23,6 +23,24 @@ <message desc="Title for the date/time picker dialog, which can be used to choose a date and time. [CHAR-LIMIT=32]" name="IDS_DATE_TIME_PICKER_DIALOG_TITLE"> Set date and time </message> + <message desc="Title for the time picker dialog, which can be used to choose a time. [CHAR-LIMIT=32]" name="IDS_TIME_PICKER_DIALOG_TITLE"> + Set time + </message> + <message desc="Value for AM in AM/PM in the time picker dialog. AM represents the morning (ante-meridiem). [CHAR-LIMIT=2]" name="IDS_TIME_PICKER_DIALOG_AM"> + AM + </message> + <message desc="Value for PM in AM/PM in the time picker dialog. PM represents the afternoon (post-meridiem). [CHAR-LIMIT=2]" name="IDS_TIME_PICKER_DIALOG_PM"> + PM + </message> + <message desc="Separator used between hours and minutes in the time pickerr dialog (hh:mm) [CHAR-LIMIT=1]" name="IDS_TIME_PICKER_DIALOG_HOUR_MINUTE_SEPARATOR"> + : + </message> + <message desc="Separator used between minutes and seconds in the time picker dialog (hh:mm:ss) [CHAR-LIMIT=1]" name="IDS_TIME_PICKER_DIALOG_MINUTE_SECOND_SEPARATOR"> + : + </message> + <message desc="Separator used between seconds and subseconds in the time picker dialog (hh:mm:ss.sss) [CHAR-LIMIT=1]" name="IDS_TIME_PICKER_DIALOG_SECOND_SUBSECOND_SEPARATOR"> + . + </message> <message desc="Title for the month picker dialog, which can be used to choose a month. [CHAR-LIMIT=32]" name="IDS_MONTH_PICKER_DIALOG_TITLE"> Set month </message> @@ -44,6 +62,21 @@ <message desc="Content description for the date time picker year component. [CHAR-LIMIT=32]" name="IDS_ACCESSIBILITY_DATE_PICKER_YEAR"> Year </message> + <message desc="Content description for the time picker hour component. [CHAR-LIMIT=32]" name="IDS_ACCESSIBILITY_TIME_PICKER_HOUR"> + Hour + </message> + <message desc="Content description for the time picker minute component. [CHAR-LIMIT=32]" name="IDS_ACCESSIBILITY_TIME_PICKER_MINUTE"> + Minute + </message> + <message desc="Content description for the time picker second component. [CHAR-LIMIT=32]" name="IDS_ACCESSIBILITY_TIME_PICKER_SECOND"> + Second + </message> + <message desc="Content description for the time picker milli component. [CHAR-LIMIT=32]" name="IDS_ACCESSIBILITY_TIME_PICKER_MILLI"> + Millisecond + </message> + <message desc="Content description for the time picker AM/PM component. [CHAR-LIMIT=32]" name="IDS_ACCESSIBILITY_TIME_PICKER_AMPM"> + AM/PM + </message> <message desc="NO DESCRIPTION [CHAR-LIMIT=32]" name="IDS_MEDIA_PLAYER_ERROR_TITLE"> Cannot play video </message> diff --git a/content/renderer/date_time_formatter.cc b/content/renderer/date_time_formatter.cc index 7cdc4bd..a0ce77b 100644 --- a/content/renderer/date_time_formatter.cc +++ b/content/renderer/date_time_formatter.cc @@ -28,10 +28,61 @@ void DateTimeFormatter::CreatePatternMap() { patterns_[ui::TEXT_INPUT_TYPE_WEEK] = "Y-'W'ww"; } +// Returns true if icu_value parses as a valid for the specified date/time +// pattern. The date/time pattern given is for icu::SimpleDateFormat. +static bool TryPattern(const char* pattern, + const icu::UnicodeString& icu_value) { + icu::UnicodeString time_pattern = pattern; + UErrorCode success = U_ZERO_ERROR; + icu::SimpleDateFormat formatter(time_pattern, success); + formatter.parse(icu_value, success); + return success == U_ZERO_ERROR; +} + +// For a time value represented as a string find the longest time +// pattern which matches it. A valid time can have hours and minutes +// or hours, minutes and seconds or hour, minutes, seconds and upto 3 +// digits of fractional seconds. Specify step in milliseconds, it is 1000 +// times the value specified as "step" in the "<input type=time step=...> +// HTML fragment. A value of 60000 or more indicates that seconds +// are not expected and a value of 1000 or more indicates that fractional +// seconds are not expected. +static const char* FindLongestTimePatternWhichMatches(const std::string& value, + double step) { + const char* pattern = "HH:mm"; + if (step >= 60000) + return pattern; + + icu::UnicodeString icu_value = icu::UnicodeString::fromUTF8( + icu::StringPiece(value.data(), value.size())); + const char* last_pattern = pattern; + pattern = "HH:mm:ss"; + if (!TryPattern(pattern, icu_value)) + return last_pattern; + if (step >= 1000) + return pattern; + last_pattern = pattern; + pattern = "HH:mm:ss.S"; + if (!TryPattern(pattern, icu_value)) + return last_pattern; + last_pattern = pattern; + pattern = "HH:mm:ss.SS"; + if (!TryPattern(pattern, icu_value)) + return last_pattern; + last_pattern = pattern; + pattern = "HH:mm:ss.SSS"; + if (!TryPattern(pattern, icu_value)) + return last_pattern; + return pattern; +} + DateTimeFormatter::DateTimeFormatter( const WebKit::WebDateTimeChooserParams& source) - : formatted_string_(source.currentValue.utf8()) { + : formatted_string_(source.currentValue.utf8()) { CreatePatternMap(); + if (source.type == WebKit::WebDateTimeInputTypeTime) + time_pattern_ = + FindLongestTimePatternWhichMatches(formatted_string_, source.step); ExtractType(source); if (!ParseValues()) { type_ = ui::TEXT_INPUT_TYPE_NONE; @@ -40,22 +91,41 @@ DateTimeFormatter::DateTimeFormatter( } } -DateTimeFormatter::DateTimeFormatter( - ui::TextInputType type, - int year, int month, int day, int hour, int minute, int second, - int week_year, int week) - : type_(type), - year_(year), - month_(month), - day_(day), - hour_(hour), - minute_(minute), - second_(second), - week_year_(week_year), - week_(week) { +DateTimeFormatter::DateTimeFormatter(ui::TextInputType type, + int year, + int month, + int day, + int hour, + int minute, + int second, + int milli, + int week_year, + int week) + : type_(type), + year_(year), + month_(month), + day_(day), + hour_(hour), + minute_(minute), + second_(second), + milli_(milli), + week_year_(week_year), + week_(week) { CreatePatternMap(); - pattern_ = type_ > 0 && type_ <= ui::TEXT_INPUT_TYPE_MAX ? - &patterns_[type_] : &patterns_[ui::TEXT_INPUT_TYPE_NONE]; + if (type_ == ui::TEXT_INPUT_TYPE_TIME && (second != 0 || milli != 0)) { + if (milli == 0) + time_pattern_ = "HH:mm:ss"; + else if (milli % 100 == 0) + time_pattern_ = "HH:mm:ss.S"; + else if (milli % 10 == 0) + time_pattern_ = "HH:mm:ss.SS"; + else + time_pattern_ = "HH:mm:ss.SSS"; + pattern_ = &time_pattern_; + } else { + pattern_ = type_ > 0 && type_ <= ui::TEXT_INPUT_TYPE_MAX ? + &patterns_[type_] : &patterns_[ui::TEXT_INPUT_TYPE_NONE]; + } formatted_string_ = FormatString(); } @@ -87,9 +157,9 @@ int DateTimeFormatter::GetSecond() const { return second_; } -int DateTimeFormatter::GetWeekYear() const { - return week_year_; -} +int DateTimeFormatter::GetMilli() const { return milli_; } + +int DateTimeFormatter::GetWeekYear() const { return week_year_; } int DateTimeFormatter::GetWeek() const { return week_; @@ -105,9 +175,8 @@ const std::string& DateTimeFormatter::GetFormattedValue() const { const std::string DateTimeFormatter::FormatString() const { UErrorCode success = U_ZERO_ERROR; - if (year_ == 0 && month_ == 0 && day_ == 0 && - hour_ == 0 && minute_ == 0 && second_ == 0 && - week_year_ == 0 && week_ == 0) { + if (year_ == 0 && month_ == 0 && day_ == 0 && hour_ == 0 && minute_ == 0 && + second_ == 0 && milli_ == 0 && week_year_ == 0 && week_ == 0) { return std::string(); } @@ -129,6 +198,7 @@ const std::string DateTimeFormatter::FormatString() const { calendar.set(UCAL_HOUR_OF_DAY, hour_); calendar.set(UCAL_MINUTE, minute_); calendar.set(UCAL_SECOND, second_); + calendar.set(UCAL_MILLISECOND, milli_); } icu::SimpleDateFormat formatter(*pattern_, success); icu::UnicodeString formatted_time; @@ -194,7 +264,8 @@ bool DateTimeFormatter::ParseValues() { icu::UnicodeString icu_value = icu::UnicodeString::fromUTF8( icu::StringPiece(formatted_string_.data(), formatted_string_.size())); if (type_ > 0 && type_ <= ui::TEXT_INPUT_TYPE_MAX) { - const icu::UnicodeString pattern = patterns_[type_]; + const icu::UnicodeString pattern = + type_ == ui::TEXT_INPUT_TYPE_TIME ? time_pattern_ : patterns_[type_]; icu::SimpleDateFormat formatter(pattern, success); formatter.parse(icu_value, success); if (success <= U_ZERO_ERROR) { @@ -205,6 +276,7 @@ bool DateTimeFormatter::ParseValues() { hour_ = ExtractValue(cal, UCAL_HOUR_OF_DAY); // 24h format minute_ = ExtractValue(cal, UCAL_MINUTE); second_ = ExtractValue(cal, UCAL_SECOND); + milli_ = ExtractValue(cal, UCAL_MILLISECOND); week_year_ = ExtractValue(cal, UCAL_YEAR_WOY); week_ = ExtractValue(cal, UCAL_WEEK_OF_YEAR); } @@ -220,6 +292,7 @@ void DateTimeFormatter::ClearAll() { hour_ = 0; minute_ = 0; second_ = 0; + milli_ = 0; week_year_ = 0; week_ = 0; } diff --git a/content/renderer/date_time_formatter.h b/content/renderer/date_time_formatter.h index ad4c020..b2b7341 100644 --- a/content/renderer/date_time_formatter.h +++ b/content/renderer/date_time_formatter.h @@ -20,15 +20,21 @@ struct WebDateTimeChooserParams; namespace content { // Converts between a text string representing a date/time and -// a set of year/month/day/hour/minute/second and vice versa. +// a set of year/month/day/hour/minute/second/milli and vice versa. // It is timezone agnostic. class CONTENT_EXPORT DateTimeFormatter { public: explicit DateTimeFormatter(const WebKit::WebDateTimeChooserParams& source); - DateTimeFormatter( - ui::TextInputType type, - int year, int month, int day, int hour, int minute, int second, - int week_year, int week); + DateTimeFormatter(ui::TextInputType type, + int year, + int month, + int day, + int hour, + int minute, + int second, + int milli, + int week_year, + int week); ~DateTimeFormatter(); int GetYear() const; @@ -37,6 +43,7 @@ class CONTENT_EXPORT DateTimeFormatter { int GetHour() const; int GetMinute() const; int GetSecond() const; + int GetMilli() const; int GetWeekYear() const; int GetWeek() const; ui::TextInputType GetType() const; @@ -53,12 +60,14 @@ class CONTENT_EXPORT DateTimeFormatter { ui::TextInputType type_; icu::UnicodeString patterns_[ui::TEXT_INPUT_TYPE_MAX + 1]; + icu::UnicodeString time_pattern_; int year_; int month_; int day_; int hour_; int minute_; int second_; + int milli_; int week_year_; int week_; const icu::UnicodeString* pattern_; diff --git a/content/renderer/date_time_formatter_unittest.cc b/content/renderer/date_time_formatter_unittest.cc index 5b61b71..0d8b07f 100644 --- a/content/renderer/date_time_formatter_unittest.cc +++ b/content/renderer/date_time_formatter_unittest.cc @@ -73,6 +73,42 @@ TEST(RendererDateTimePickerTest, TestParserValidStringInputs) { EXPECT_EQ(2013, sut4.GetWeekYear()); EXPECT_EQ(15, sut4.GetWeek()); EXPECT_EQ(ui::TEXT_INPUT_TYPE_WEEK, sut4.GetType()); + + params.currentValue = "12:15"; + params.type = WebKit::WebDateTimeInputTypeTime; + DateTimeFormatter sut5(params); + EXPECT_EQ(12, sut5.GetHour()); + EXPECT_EQ(15, sut5.GetMinute()); + EXPECT_EQ(0, sut5.GetSecond()); + EXPECT_EQ(0, sut5.GetMilli()); + EXPECT_EQ(ui::TEXT_INPUT_TYPE_TIME, sut5.GetType()); + + params.currentValue = "12:15:02"; + params.type = WebKit::WebDateTimeInputTypeTime; + DateTimeFormatter sut6(params); + EXPECT_EQ(12, sut6.GetHour()); + EXPECT_EQ(15, sut6.GetMinute()); + EXPECT_EQ(02, sut6.GetSecond()); + EXPECT_EQ(0, sut6.GetMilli()); + EXPECT_EQ(ui::TEXT_INPUT_TYPE_TIME, sut6.GetType()); + + params.currentValue = "12:15:02.1"; + params.type = WebKit::WebDateTimeInputTypeTime; + DateTimeFormatter sut7(params); + EXPECT_EQ(12, sut7.GetHour()); + EXPECT_EQ(15, sut7.GetMinute()); + EXPECT_EQ(02, sut7.GetSecond()); + EXPECT_EQ(100, sut7.GetMilli()); + EXPECT_EQ(ui::TEXT_INPUT_TYPE_TIME, sut7.GetType()); + + params.currentValue = "12:15:02.123"; + params.type = WebKit::WebDateTimeInputTypeTime; + DateTimeFormatter sut8(params); + EXPECT_EQ(12, sut8.GetHour()); + EXPECT_EQ(15, sut8.GetMinute()); + EXPECT_EQ(02, sut8.GetSecond()); + EXPECT_EQ(123, sut8.GetMilli()); + EXPECT_EQ(ui::TEXT_INPUT_TYPE_TIME, sut8.GetType()); } @@ -130,42 +166,60 @@ TEST(RendererDateTimePickerTest, TestParserInvalidStringInputs) { TEST(RendererDateTimePickerTest, TestParserValidDateInputs) { - DateTimeFormatter sut(ui::TEXT_INPUT_TYPE_MONTH, 2012, 11, 1, 0, 0, 0, 0, 0); + DateTimeFormatter sut( + ui::TEXT_INPUT_TYPE_MONTH, 2012, 11, 1, 0, 0, 0, 0, 0, 0); EXPECT_EQ("2012-12", sut.GetFormattedValue()); - - DateTimeFormatter sut2(ui::TEXT_INPUT_TYPE_DATE_TIME_LOCAL, - 2013, 3, 23, 15, 47, 0, 0, 0); + DateTimeFormatter sut2( + ui::TEXT_INPUT_TYPE_DATE_TIME_LOCAL, 2013, 3, 23, 15, 47, 0, 0, 0, 0); EXPECT_EQ("2013-04-23T15:47", sut2.GetFormattedValue()); - DateTimeFormatter sut3(ui::TEXT_INPUT_TYPE_WEEK, 0, 0, 0, 0, 0, 0, 2012, 2); + DateTimeFormatter sut3( + ui::TEXT_INPUT_TYPE_WEEK, 0, 0, 0, 0, 0, 0, 0, 2012, 2); EXPECT_EQ("2012-W02", sut3.GetFormattedValue()); } +TEST(RendererDateTimePickerTest, TestParserValidTimeInputs) { + DateTimeFormatter sut( + ui::TEXT_INPUT_TYPE_TIME, 0, 0, 0, 12, 15, 0, 0, 0, 0); + EXPECT_EQ("12:15", sut.GetFormattedValue()); + + DateTimeFormatter sut2( + ui::TEXT_INPUT_TYPE_TIME, 0, 0, 0, 12, 15, 02, 0, 0, 0); + EXPECT_EQ("12:15:02", sut2.GetFormattedValue()); + + DateTimeFormatter sut3( + ui::TEXT_INPUT_TYPE_TIME, 0, 0, 0, 12, 15, 02, 123, 0, 0); + EXPECT_EQ("12:15:02.123", sut3.GetFormattedValue()); +} + TEST(RendererDateTimePickerTest, TestParserInvalidDateInputs) { - DateTimeFormatter sut(ui::TEXT_INPUT_TYPE_WEEK, 0, 0, 0, 0, 0, 0, 0, 0); + DateTimeFormatter sut(ui::TEXT_INPUT_TYPE_WEEK, 0, 0, 0, 0, 0, 0, 0, 0, 0); EXPECT_EQ("", sut.GetFormattedValue()); - DateTimeFormatter sut2(ui::TEXT_INPUT_TYPE_NONE, 2013, 3, 23, 0, 0, 0, 0, 0); + DateTimeFormatter sut2( + ui::TEXT_INPUT_TYPE_NONE, 2013, 3, 23, 0, 0, 0, 0, 0, 0); EXPECT_EQ("", sut2.GetFormattedValue()); - DateTimeFormatter sut3(ui::TEXT_INPUT_TYPE_NONE, 2013, 14, 32, 0, 0, 0, 0, 0); + DateTimeFormatter sut3( + ui::TEXT_INPUT_TYPE_NONE, 2013, 14, 32, 0, 0, 0, 0, 0, 0); EXPECT_EQ("", sut3.GetFormattedValue()); - DateTimeFormatter sut4(ui::TEXT_INPUT_TYPE_DATE, 0, 0, 0, 0, 0, 0, 0, 0); + DateTimeFormatter sut4(ui::TEXT_INPUT_TYPE_DATE, 0, 0, 0, 0, 0, 0, 0, 0, 0); EXPECT_EQ("", sut4.GetFormattedValue()); - DateTimeFormatter sut5(ui::TEXT_INPUT_TYPE_TIME, 0, 0, 0, 0, 0, 0, 0, 0); + DateTimeFormatter sut5(ui::TEXT_INPUT_TYPE_TIME, 0, 0, 0, 0, 0, 0, 0, 0, 0); EXPECT_EQ("", sut5.GetFormattedValue()); - DateTimeFormatter sut6(ui::TEXT_INPUT_TYPE_PASSWORD, 23, 0, 0, 0, 5, 0, 0, 0); + DateTimeFormatter sut6( + ui::TEXT_INPUT_TYPE_PASSWORD, 23, 0, 0, 0, 5, 0, 0, 0, 0); EXPECT_EQ("", sut6.GetFormattedValue()); - DateTimeFormatter sut7(ui::TEXT_INPUT_TYPE_MAX, 23, 0, 0, 0, 5, 0, 0, 0); + DateTimeFormatter sut7(ui::TEXT_INPUT_TYPE_MAX, 23, 0, 0, 0, 5, 0, 0, 0, 0); EXPECT_EQ("", sut7.GetFormattedValue()); DateTimeFormatter sut8( - static_cast<ui::TextInputType>(10000), 23, 0, 0, 0, 5, 0, 0, 0); + static_cast<ui::TextInputType>(10000), 23, 0, 0, 0, 5, 0, 0, 0, 0); EXPECT_EQ("", sut8.GetFormattedValue()); } } // namespace content diff --git a/content/renderer/renderer_date_time_picker.cc b/content/renderer/renderer_date_time_picker.cc index 47d22f7..86d268a 100644 --- a/content/renderer/renderer_date_time_picker.cc +++ b/content/renderer/renderer_date_time_picker.cc @@ -43,9 +43,11 @@ bool RendererDateTimePicker::Open() { message.hour = parser.GetHour(); message.minute = parser.GetMinute(); message.second = parser.GetSecond(); + message.milli = parser.GetMilli(); } message.minimum = chooser_params_.minimum; message.maximum = chooser_params_.maximum; + message.step = chooser_params_.step; Send(new ViewHostMsg_OpenDateTimeDialog(routing_id(), message)); return true; } @@ -64,10 +66,16 @@ bool RendererDateTimePicker::OnMessageReceived( void RendererDateTimePicker::OnReplaceDateTime( const ViewHostMsg_DateTimeDialogValue_Params& value) { - DateTimeFormatter formatter( - static_cast<ui::TextInputType>(value.dialog_type), - value.year, value.month, value.day, - value.hour, value.minute, value.second, value.year, value.week); + DateTimeFormatter formatter(static_cast<ui::TextInputType>(value.dialog_type), + value.year, + value.month, + value.day, + value.hour, + value.minute, + value.second, + value.milli, + value.year, + value.week); if (chooser_completion_) chooser_completion_->didChooseValue(WebString::fromUTF8( |