Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(538)

Unified Diff: ui/android/java/src/org/chromium/ui/picker/DateDialogNormalizer.java

Issue 1622773002: Fix date picker performance issues, crash, and incorrect pre-1582 dates. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « ui/android/BUILD.gn ('k') | ui/android/java/src/org/chromium/ui/picker/DateTimePickerDialog.java » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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);
}
}
« no previous file with comments | « ui/android/BUILD.gn ('k') | ui/android/java/src/org/chromium/ui/picker/DateTimePickerDialog.java » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698