Change high/low bound logic for manual date/time
This commit moves knowledge of the upper / lower bounds for valid manual
date suggestions to (internal) API calls.
It adds a test to confirm the API calls are used to configure the date
dialog.
Further, it removes redundant filtering from the client that would
prevent suggestion calls being made if the settings UI considers them
invalid. Year bounds are already used to limit the UI entry, and the
system server will return false from the service call if the suggestion
is invalid, though the SettingsUI doesn't do anything (behaviorally)
with the false before or after this change. After this change it is
logged.
The goal of this change is to allow users with devices that don't have
the Y2038 issue to enter dates > 2037. This could have been done with
a smaller change, but the use of the new internal class
TimeDetectorHelper means that the bounds logic is in one place. The
lower bound (on mobile devices) can now be changed relatively easily by
touching one class.
Bug: 228967927
Test: m RunSettingsRoboTests ROBOTEST_FILTER="com.android.settings.datetime"
Change-Id: Iaf1ca8220e0e96773aa71b595da9c1ba1e50d59d
diff --git a/src/com/android/settings/datetime/DatePreferenceController.java b/src/com/android/settings/datetime/DatePreferenceController.java
index 66026d7..50feca1 100644
--- a/src/com/android/settings/datetime/DatePreferenceController.java
+++ b/src/com/android/settings/datetime/DatePreferenceController.java
@@ -18,15 +18,16 @@
import static android.app.time.Capabilities.CAPABILITY_POSSESSED;
-import android.app.Activity;
import android.app.DatePickerDialog;
import android.app.time.TimeCapabilities;
import android.app.time.TimeManager;
import android.app.timedetector.ManualTimeSuggestion;
import android.app.timedetector.TimeDetector;
+import android.app.timedetector.TimeDetectorHelper;
import android.content.Context;
import android.text.TextUtils;
import android.text.format.DateFormat;
+import android.util.Log;
import android.widget.DatePicker;
import androidx.annotation.VisibleForTesting;
@@ -47,6 +48,7 @@
public static final int DIALOG_DATEPICKER = 0;
+ private static final String TAG = "DatePreferenceController";
private static final String KEY_DATE = "date";
private final DatePreferenceHost mHost;
@@ -96,22 +98,32 @@
mHost.updateTimeAndDateDisplay(mContext);
}
- public DatePickerDialog buildDatePicker(Activity activity) {
+ /**
+ * Builds a {@link DatePickerDialog} that can be used to request the current date from the user.
+ */
+ public DatePickerDialog buildDatePicker(
+ Context parentContext, TimeDetectorHelper timeDetectorHelper) {
final Calendar calendar = Calendar.getInstance();
- final DatePickerDialog d = new DatePickerDialog(
- activity,
+ final DatePickerDialog dialog = new DatePickerDialog(
+ parentContext,
this,
calendar.get(Calendar.YEAR),
calendar.get(Calendar.MONTH),
calendar.get(Calendar.DAY_OF_MONTH));
- // The system clock can't represent dates outside this range.
+
+ // Limit the dates the user can pick to a sensible range.
+ DatePicker datePicker = dialog.getDatePicker();
+
calendar.clear();
- calendar.set(2007, Calendar.JANUARY, 1);
- d.getDatePicker().setMinDate(calendar.getTimeInMillis());
+ int minYear = timeDetectorHelper.getManualDateSelectionYearMin();
+ calendar.set(minYear, Calendar.JANUARY, 1);
+ datePicker.setMinDate(calendar.getTimeInMillis());
+
+ int maxYear = timeDetectorHelper.getManualDateSelectionYearMax();
calendar.clear();
- calendar.set(2037, Calendar.DECEMBER, 31);
- d.getDatePicker().setMaxDate(calendar.getTimeInMillis());
- return d;
+ calendar.set(maxYear, Calendar.DECEMBER, 31);
+ datePicker.setMaxDate(calendar.getTimeInMillis());
+ return dialog;
}
@VisibleForTesting
@@ -121,13 +133,16 @@
c.set(Calendar.YEAR, year);
c.set(Calendar.MONTH, month);
c.set(Calendar.DAY_OF_MONTH, day);
- long when = Math.max(c.getTimeInMillis(), DatePreferenceHost.MIN_DATE);
+ long when = c.getTimeInMillis();
- if (when / 1000 < Integer.MAX_VALUE) {
- TimeDetector timeDetector = mContext.getSystemService(TimeDetector.class);
- ManualTimeSuggestion manualTimeSuggestion =
- TimeDetector.createManualTimeSuggestion(when, "Settings: Set date");
- timeDetector.suggestManualTime(manualTimeSuggestion);
+ TimeDetector timeDetector = mContext.getSystemService(TimeDetector.class);
+ ManualTimeSuggestion manualTimeSuggestion =
+ TimeDetector.createManualTimeSuggestion(when, "Settings: Set date");
+ boolean success = timeDetector.suggestManualTime(manualTimeSuggestion);
+ if (!success) {
+ // This implies the system server is applying tighter bounds than the settings app or
+ // the date/time cannot be set for other reasons, e.g. perhaps "auto time" is turned on.
+ Log.w(TAG, "Unable to set date with suggestion=" + manualTimeSuggestion);
}
}
diff --git a/src/com/android/settings/datetime/DateTimeSettings.java b/src/com/android/settings/datetime/DateTimeSettings.java
index 3da4234..367146b 100644
--- a/src/com/android/settings/datetime/DateTimeSettings.java
+++ b/src/com/android/settings/datetime/DateTimeSettings.java
@@ -19,6 +19,7 @@
import android.app.Activity;
import android.app.Dialog;
import android.app.settings.SettingsEnums;
+import android.app.timedetector.TimeDetectorHelper;
import android.content.Context;
import android.content.Intent;
@@ -104,7 +105,7 @@
switch (id) {
case DatePreferenceController.DIALOG_DATEPICKER:
return use(DatePreferenceController.class)
- .buildDatePicker(getActivity());
+ .buildDatePicker(getActivity(), TimeDetectorHelper.INSTANCE);
case TimePreferenceController.DIALOG_TIMEPICKER:
return use(TimePreferenceController.class)
.buildTimePicker(getActivity());
diff --git a/src/com/android/settings/datetime/TimePreferenceController.java b/src/com/android/settings/datetime/TimePreferenceController.java
index 368f1f2..09950fc 100644
--- a/src/com/android/settings/datetime/TimePreferenceController.java
+++ b/src/com/android/settings/datetime/TimePreferenceController.java
@@ -16,13 +16,13 @@
package com.android.settings.datetime;
-import android.app.Activity;
import android.app.TimePickerDialog;
import android.app.timedetector.ManualTimeSuggestion;
import android.app.timedetector.TimeDetector;
import android.content.Context;
import android.text.TextUtils;
import android.text.format.DateFormat;
+import android.util.Log;
import android.widget.TimePicker;
import androidx.preference.Preference;
@@ -42,6 +42,7 @@
public static final int DIALOG_TIMEPICKER = 1;
+ private static final String TAG = "TimePreferenceController";
private static final String KEY_TIME = "time";
private final DatePreferenceController mDatePreferenceController;
@@ -99,14 +100,17 @@
// SystemClock time.
}
- public TimePickerDialog buildTimePicker(Activity activity) {
+ /**
+ * Builds a {@link TimePickerDialog} that can be used to request the current time from the user.
+ */
+ public TimePickerDialog buildTimePicker(Context parentContext) {
final Calendar calendar = Calendar.getInstance();
return new TimePickerDialog(
- activity,
+ parentContext,
this,
calendar.get(Calendar.HOUR_OF_DAY),
calendar.get(Calendar.MINUTE),
- DateFormat.is24HourFormat(activity));
+ DateFormat.is24HourFormat(parentContext));
}
void setTime(int hourOfDay, int minute) {
@@ -116,13 +120,16 @@
c.set(Calendar.MINUTE, minute);
c.set(Calendar.SECOND, 0);
c.set(Calendar.MILLISECOND, 0);
- long when = Math.max(c.getTimeInMillis(), TimePreferenceHost.MIN_DATE);
+ long when = c.getTimeInMillis();
- if (when / 1000 < Integer.MAX_VALUE) {
- TimeDetector timeDetector = mContext.getSystemService(TimeDetector.class);
- ManualTimeSuggestion manualTimeSuggestion =
- TimeDetector.createManualTimeSuggestion(when, "Settings: Set time");
- timeDetector.suggestManualTime(manualTimeSuggestion);
+ TimeDetector timeDetector = mContext.getSystemService(TimeDetector.class);
+ ManualTimeSuggestion manualTimeSuggestion =
+ TimeDetector.createManualTimeSuggestion(when, "Settings: Set time");
+ boolean success = timeDetector.suggestManualTime(manualTimeSuggestion);
+ if (!success) {
+ // This implies the system server is applying tighter bounds than the settings app or
+ // the date/time cannot be set for other reasons, e.g. perhaps "auto time" is turned on.
+ Log.w(TAG, "Unable to set time with suggestion=" + manualTimeSuggestion);
}
}
}
diff --git a/src/com/android/settings/datetime/UpdateTimeAndDateCallback.java b/src/com/android/settings/datetime/UpdateTimeAndDateCallback.java
index 333b9aa..aa1d64e 100644
--- a/src/com/android/settings/datetime/UpdateTimeAndDateCallback.java
+++ b/src/com/android/settings/datetime/UpdateTimeAndDateCallback.java
@@ -19,8 +19,5 @@
import android.content.Context;
public interface UpdateTimeAndDateCallback {
- // Minimum time is Nov 5, 2007, 0:00.
- long MIN_DATE = 1194220800000L;
-
void updateTimeAndDateDisplay(Context context);
}
diff --git a/tests/robotests/src/com/android/settings/datetime/DatePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/datetime/DatePreferenceControllerTest.java
index 1b8148b..c7357ad 100644
--- a/tests/robotests/src/com/android/settings/datetime/DatePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/datetime/DatePreferenceControllerTest.java
@@ -18,15 +18,19 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.DatePickerDialog;
import android.app.time.Capabilities;
import android.app.time.TimeCapabilities;
import android.app.time.TimeCapabilitiesAndConfig;
import android.app.time.TimeConfiguration;
import android.app.time.TimeManager;
import android.app.timedetector.TimeDetector;
+import android.app.timedetector.TimeDetectorHelper;
import android.content.Context;
import android.os.UserHandle;
@@ -40,17 +44,20 @@
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+
@RunWith(RobolectricTestRunner.class)
public class DatePreferenceControllerTest {
@Mock
private Context mContext;
@Mock
- private TimeDetector mTimeDetector;
+ private DatePreferenceController.DatePreferenceHost mHost;
@Mock
private TimeManager mTimeManager;
@Mock
- private DatePreferenceController.DatePreferenceHost mHost;
+ private TimeDetector mTimeDetector;
private RestrictedPreference mPreference;
private DatePreferenceController mController;
@@ -114,6 +121,26 @@
verify(mHost).showDatePicker();
}
+ @Test
+ public void testBuildDatePicker() {
+ TimeDetectorHelper timeDetectorHelper = mock(TimeDetectorHelper.class);
+ when(timeDetectorHelper.getManualDateSelectionYearMin()).thenReturn(2015);
+ when(timeDetectorHelper.getManualDateSelectionYearMax()).thenReturn(2020);
+
+ Context context = RuntimeEnvironment.application;
+ DatePickerDialog dialog = mController.buildDatePicker(context, timeDetectorHelper);
+
+ GregorianCalendar calendar = new GregorianCalendar();
+
+ long minDate = dialog.getDatePicker().getMinDate();
+ calendar.setTimeInMillis(minDate);
+ assertEquals(2015, calendar.get(Calendar.YEAR));
+
+ long maxDate = dialog.getDatePicker().getMaxDate();
+ calendar.setTimeInMillis(maxDate);
+ assertEquals(2020, calendar.get(Calendar.YEAR));
+ }
+
private static TimeCapabilitiesAndConfig createCapabilitiesAndConfig(
boolean suggestManualAllowed) {
int suggestManualCapability = suggestManualAllowed ? Capabilities.CAPABILITY_POSSESSED