| Index: ui/android/java/src/org/chromium/ui/picker/DateDialogNormalizer.java
|
| diff --git a/ui/android/java/src/org/chromium/ui/picker/DateDialogNormalizer.java b/ui/android/java/src/org/chromium/ui/picker/DateDialogNormalizer.java
|
| index 5f0012475c4b6882b54d96cb650b08e197ea15e0..2faa6d67d9a2764488ddbc3c897ed87f9a5f9a34 100644
|
| --- a/ui/android/java/src/org/chromium/ui/picker/DateDialogNormalizer.java
|
| +++ b/ui/android/java/src/org/chromium/ui/picker/DateDialogNormalizer.java
|
| @@ -4,92 +4,125 @@
|
|
|
| package org.chromium.ui.picker;
|
|
|
| +import android.os.Build;
|
| import android.widget.DatePicker;
|
| import android.widget.DatePicker.OnDateChangedListener;
|
|
|
| -import org.chromium.base.VisibleForTesting;
|
| -
|
| import java.util.Calendar;
|
| +import java.util.Date;
|
| +import java.util.GregorianCalendar;
|
| import java.util.TimeZone;
|
|
|
| /**
|
| - * Normalize a date dialog so that it respect min and max.
|
| + * Sets the current, min, and max values on the given DatePicker.
|
| */
|
| public class DateDialogNormalizer {
|
| - @VisibleForTesting
|
| - 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;
|
| +
|
| + /**
|
| + * Stores a date (year-month-day) and the number of milliseconds corresponding to that date
|
| + * according to the DatePicker's calendar.
|
| + */
|
| + private static class DateAndMillis {
|
| + /**
|
| + * Number of milliseconds from the epoch (1970-01-01) to the beginning of year-month-day
|
| + * in the default time zone (TimeZone.getDefault()) using the Julian/Gregorian split
|
| + * calendar. This value is interopable with {@link DatePicker#getMinDate} and
|
| + * {@link DatePicker#setMinDate}.
|
| + */
|
| + public final long millisForPicker;
|
| +
|
| + public final int year;
|
| + public final int month; // 0-based
|
| + public final int day;
|
| +
|
| + DateAndMillis(long millisForPicker, int year, int month, int day) {
|
| + this.millisForPicker = millisForPicker;
|
| + this.year = year;
|
| + this.month = month;
|
| + this.day = day;
|
| }
|
| - Calendar minCal = trimToDate(minMillis);
|
| - Calendar maxCal = trimToDate(maxMillis);
|
| - int currentYear = picker.getYear();
|
| - int currentMonth = picker.getMonth();
|
| - int currentDayOfMonth = picker.getDayOfMonth();
|
| - TimeZone timeZone = TimeZone.getDefault();
|
|
|
| - picker.updateDate(maxCal.get(Calendar.YEAR),
|
| - maxCal.get(Calendar.MONTH),
|
| - maxCal.get(Calendar.DAY_OF_MONTH));
|
| - // setMinDate() requires milliseconds since 1970-01-01 00:00 in the
|
| - // default time zone, and minCal.getTimeInMillis() represents
|
| - // millisecnods since 1970-01-01 00:00 UTC. We need to adjust it.
|
| - picker.setMinDate(minCal.getTimeInMillis() - timeZone.getOffset(minCal.getTimeInMillis()));
|
| - picker.updateDate(minCal.get(Calendar.YEAR),
|
| - minCal.get(Calendar.MONTH),
|
| - minCal.get(Calendar.DAY_OF_MONTH));
|
| - // setMaxDate() requires milliseconds since 1970-01-01 00:00 in the
|
| - // default time zone, and maxCal.getTimeInMillis() represents
|
| - // millisecnods since 1970-01-01 00:00 UTC. We need to adjust it.
|
| - picker.setMaxDate(maxCal.getTimeInMillis() - timeZone.getOffset(maxCal.getTimeInMillis()));
|
| + static DateAndMillis create(long millisUtc) {
|
| + // millisUtc uses the Gregorian calendar only, so disable the Julian changeover date.
|
| + GregorianCalendar utcCal = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
|
| + utcCal.setGregorianChange(new Date(Long.MIN_VALUE));
|
| + utcCal.setTimeInMillis(millisUtc);
|
| + int year = utcCal.get(Calendar.YEAR);
|
| + int month = utcCal.get(Calendar.MONTH);
|
| + int day = utcCal.get(Calendar.DAY_OF_MONTH);
|
| + return create(year, month, day);
|
| + }
|
|
|
| - // Restore the current date, only if within the accepted range
|
| - // This will keep the min/max settings
|
| - // previously set into account.
|
| - Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
|
| - cal.clear();
|
| - cal.set(currentYear, currentMonth, currentDayOfMonth);
|
| - if (cal.getTimeInMillis() > minCal.getTimeInMillis()
|
| - && cal.getTimeInMillis() < maxCal.getTimeInMillis()) {
|
| - picker.updateDate(currentYear, currentMonth, currentDayOfMonth);
|
| + static DateAndMillis create(int year, int month, int day) {
|
| + // By contrast, millisForPicker uses the default Gregorian/Julian changeover date.
|
| + Calendar defaultTimeZoneCal = Calendar.getInstance(TimeZone.getDefault());
|
| + defaultTimeZoneCal.clear();
|
| + defaultTimeZoneCal.set(year, month, day);
|
| + long millisForPicker = defaultTimeZoneCal.getTimeInMillis();
|
| + return new DateAndMillis(millisForPicker, year, month, day);
|
| }
|
| }
|
|
|
| - @VisibleForTesting
|
| - 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;
|
| + private static void setLimits(DatePicker picker, long currentMillisForPicker,
|
| + long minMillisForPicker, long maxMillisForPicker) {
|
| + // On Lollipop only (not KitKat or Marshmallow), DatePicker has terrible performance for
|
| + // large date ranges. This causes problems when the min or max date isn't set in HTML, in
|
| + // which case these values default to the min and max possible values for the JavaScript
|
| + // Date object (1CE and 275760CE). As a workaround, limit the date range to 5000 years
|
| + // before and after the current date. In practice, this doesn't limit users since scrolling
|
| + // through 5000 years in the DatePicker is highly impractical anyway. See
|
| + // http://crbug.com/441060
|
| + if (Build.VERSION.SDK_INT == Build.VERSION_CODES.LOLLIPOP
|
| + || Build.VERSION.SDK_INT == Build.VERSION_CODES.LOLLIPOP_MR1) {
|
| + final long maxRangeMillis = 5000L * 365 * 24 * 60 * 60 * 1000;
|
| + minMillisForPicker = Math.max(minMillisForPicker,
|
| + currentMillisForPicker - maxRangeMillis);
|
| + maxMillisForPicker = Math.min(maxMillisForPicker,
|
| + currentMillisForPicker + maxRangeMillis);
|
| + }
|
| +
|
| + // On KitKat and earlier, DatePicker requires the minDate is always less than maxDate, even
|
| + // during the process of setting those values (eek), so set them in an order that preserves
|
| + // this invariant throughout.
|
| + if (minMillisForPicker > picker.getMaxDate()) {
|
| + picker.setMaxDate(maxMillisForPicker);
|
| + picker.setMinDate(minMillisForPicker);
|
| + } else {
|
| + picker.setMinDate(minMillisForPicker);
|
| + picker.setMaxDate(maxMillisForPicker);
|
| + }
|
| }
|
|
|
| /**
|
| - * Normalizes an existing DateDialogPicker changing the default date if
|
| - * needed to comply with the {@code min} and {@code max} attributes.
|
| + * Sets the current, min, and max values on the given DatePicker and ensures that
|
| + * min <= current <= max, adjusting current and max if needed.
|
| + *
|
| + * @param year The current year to set.
|
| + * @param month The current month to set. 0-based.
|
| + * @param day The current day to set.
|
| + * @param minMillisUtc The minimum allowed date, in milliseconds from the epoch according to a
|
| + * proleptic Gregorian calendar (no Julian switch).
|
| + * @param maxMillisUtc The maximum allowed date, in milliseconds from the epoch according to a
|
| + * proleptic Gregorian calendar (no Julian switch).
|
| */
|
| - 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);
|
| + public static void normalize(DatePicker picker, final OnDateChangedListener listener,
|
| + int year, int month, int day, long minMillisUtc, long maxMillisUtc) {
|
| + DateAndMillis currentDate = DateAndMillis.create(year, month, day);
|
| + DateAndMillis minDate = DateAndMillis.create(minMillisUtc);
|
| + DateAndMillis maxDate = DateAndMillis.create(maxMillisUtc);
|
| +
|
| + // Ensure min <= current <= max, adjusting current and max if needed.
|
| + if (maxDate.millisForPicker < minDate.millisForPicker) {
|
| + maxDate = minDate;
|
| + }
|
| + if (currentDate.millisForPicker < minDate.millisForPicker) {
|
| + currentDate = minDate;
|
| + } else if (currentDate.millisForPicker > maxDate.millisForPicker) {
|
| + currentDate = maxDate;
|
| }
|
| - picker.init(
|
| - calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH),
|
| - calendar.get(Calendar.DAY_OF_MONTH), listener);
|
|
|
| - setLimits(picker, minMillis, maxMillis);
|
| + setLimits(picker, currentDate.millisForPicker, minDate.millisForPicker,
|
| + maxDate.millisForPicker);
|
| + picker.init(currentDate.year, currentDate.month, currentDate.day, listener);
|
| }
|
| }
|
|
|