diff --git a/src/com/android/settings/datetime/AutoTimeZonePreferenceController.java b/src/com/android/settings/datetime/AutoTimeZonePreferenceController.java index 4426bde99f4..98629b422a0 100644 --- a/src/com/android/settings/datetime/AutoTimeZonePreferenceController.java +++ b/src/com/android/settings/datetime/AutoTimeZonePreferenceController.java @@ -16,14 +16,22 @@ package com.android.settings.datetime; -import android.content.Context; -import android.provider.Settings; +import static android.app.time.Capabilities.CAPABILITY_NOT_ALLOWED; +import static android.app.time.Capabilities.CAPABILITY_NOT_APPLICABLE; +import static android.app.time.Capabilities.CAPABILITY_NOT_SUPPORTED; +import static android.app.time.Capabilities.CAPABILITY_POSSESSED; +import android.app.time.TimeManager; +import android.app.time.TimeZoneCapabilities; +import android.app.time.TimeZoneCapabilitiesAndConfig; +import android.app.time.TimeZoneConfiguration; +import android.content.Context; + +import androidx.annotation.VisibleForTesting; import androidx.preference.Preference; import androidx.preference.SwitchPreference; import com.android.settings.core.PreferenceControllerMixin; -import com.android.settingslib.Utils; import com.android.settingslib.core.AbstractPreferenceController; public class AutoTimeZonePreferenceController extends AbstractPreferenceController @@ -33,17 +41,37 @@ public class AutoTimeZonePreferenceController extends AbstractPreferenceControll private final boolean mIsFromSUW; private final UpdateTimeAndDateCallback mCallback; + private final TimeManager mTimeManager; public AutoTimeZonePreferenceController(Context context, UpdateTimeAndDateCallback callback, boolean isFromSUW) { super(context); + mTimeManager = context.getSystemService(TimeManager.class); mCallback = callback; mIsFromSUW = isFromSUW; } @Override public boolean isAvailable() { - return !(Utils.isWifiOnly(mContext) || mIsFromSUW); + if (mIsFromSUW) { + return false; + } + + TimeZoneCapabilities timeZoneCapabilities = + getTimeZoneCapabilitiesAndConfig().getCapabilities(); + int capability = timeZoneCapabilities.getConfigureAutoDetectionEnabledCapability(); + + // The preference only has two states: present and not present. The preference is never + // present but disabled. + if (capability == CAPABILITY_NOT_SUPPORTED + || capability == CAPABILITY_NOT_ALLOWED + || capability == CAPABILITY_NOT_APPLICABLE) { + return false; + } else if (capability == CAPABILITY_POSSESSED) { + return true; + } else { + throw new IllegalStateException("Unknown capability=" + capability); + } } @Override @@ -62,14 +90,22 @@ public class AutoTimeZonePreferenceController extends AbstractPreferenceControll @Override public boolean onPreferenceChange(Preference preference, Object newValue) { boolean autoZoneEnabled = (Boolean) newValue; - Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AUTO_TIME_ZONE, - autoZoneEnabled ? 1 : 0); + TimeZoneConfiguration configuration = new TimeZoneConfiguration.Builder() + .setAutoDetectionEnabled(autoZoneEnabled) + .build(); + boolean result = mTimeManager.updateTimeZoneConfiguration(configuration); + mCallback.updateTimeAndDateDisplay(mContext); - return true; + return result; } - public boolean isEnabled() { - return isAvailable() && Settings.Global.getInt(mContext.getContentResolver(), - Settings.Global.AUTO_TIME_ZONE, 0) > 0; + @VisibleForTesting + boolean isEnabled() { + TimeZoneConfiguration config = getTimeZoneCapabilitiesAndConfig().getConfiguration(); + return config.isAutoDetectionEnabled(); + } + + private TimeZoneCapabilitiesAndConfig getTimeZoneCapabilitiesAndConfig() { + return mTimeManager.getTimeZoneCapabilitiesAndConfig(); } } diff --git a/src/com/android/settings/datetime/DateTimeSettings.java b/src/com/android/settings/datetime/DateTimeSettings.java index d74847f1411..169455a7296 100644 --- a/src/com/android/settings/datetime/DateTimeSettings.java +++ b/src/com/android/settings/datetime/DateTimeSettings.java @@ -85,8 +85,7 @@ public class DateTimeSettings extends DashboardFragment implements controllers.add(new TimeFormatPreferenceController( activity, this /* UpdateTimeAndDateCallback */, isFromSUW)); - controllers.add(new TimeZonePreferenceController( - activity, autoTimeZonePreferenceController)); + controllers.add(new TimeZonePreferenceController(activity)); controllers.add(new TimePreferenceController( activity, this /* UpdateTimeAndDateCallback */, autoTimePreferenceController)); controllers.add(new DatePreferenceController( diff --git a/src/com/android/settings/datetime/TimeZonePreferenceController.java b/src/com/android/settings/datetime/TimeZonePreferenceController.java index a19f0559216..a26c7bf2b36 100644 --- a/src/com/android/settings/datetime/TimeZonePreferenceController.java +++ b/src/com/android/settings/datetime/TimeZonePreferenceController.java @@ -16,6 +16,10 @@ package com.android.settings.datetime; +import static android.app.time.Capabilities.CAPABILITY_POSSESSED; + +import android.app.time.TimeManager; +import android.app.time.TimeZoneCapabilities; import android.content.Context; import androidx.annotation.VisibleForTesting; @@ -33,12 +37,11 @@ public class TimeZonePreferenceController extends AbstractPreferenceController private static final String KEY_TIMEZONE = "timezone"; - private final AutoTimeZonePreferenceController mAutoTimeZonePreferenceController; + private final TimeManager mTimeManager; - public TimeZonePreferenceController(Context context, - AutoTimeZonePreferenceController autoTimeZonePreferenceController) { + public TimeZonePreferenceController(Context context) { super(context); - mAutoTimeZonePreferenceController = autoTimeZonePreferenceController; + mTimeManager = context.getSystemService(TimeManager.class); } @Override @@ -47,8 +50,9 @@ public class TimeZonePreferenceController extends AbstractPreferenceController return; } preference.setSummary(getTimeZoneOffsetAndName()); - if( !((RestrictedPreference) preference).isDisabledByAdmin()) { - preference.setEnabled(!mAutoTimeZonePreferenceController.isEnabled()); + if (!((RestrictedPreference) preference).isDisabledByAdmin()) { + boolean enableManualTimeZoneSelection = shouldEnableManualTimeZoneSelection(); + preference.setEnabled(enableManualTimeZoneSelection); } } @@ -68,4 +72,12 @@ public class TimeZonePreferenceController extends AbstractPreferenceController return ZoneGetter.getTimeZoneOffsetAndName(mContext, now.getTimeZone(), now.getTime()); } + + private boolean shouldEnableManualTimeZoneSelection() { + TimeZoneCapabilities timeZoneCapabilities = + mTimeManager.getTimeZoneCapabilitiesAndConfig().getCapabilities(); + int suggestManualTimeZoneCapability = + timeZoneCapabilities.getSuggestManualTimeZoneCapability(); + return suggestManualTimeZoneCapability == CAPABILITY_POSSESSED; + } } diff --git a/tests/robotests/src/com/android/settings/datetime/AutoTimeZonePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/datetime/AutoTimeZonePreferenceControllerTest.java index eb29b7ca76e..f2ba580f3fe 100644 --- a/tests/robotests/src/com/android/settings/datetime/AutoTimeZonePreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/datetime/AutoTimeZonePreferenceControllerTest.java @@ -22,9 +22,13 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.app.time.Capabilities; +import android.app.time.TimeManager; +import android.app.time.TimeZoneCapabilities; +import android.app.time.TimeZoneCapabilitiesAndConfig; +import android.app.time.TimeZoneConfiguration; import android.content.Context; -import android.provider.Settings; -import android.telephony.TelephonyManager; +import android.os.UserHandle; import androidx.preference.Preference; @@ -32,6 +36,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; @@ -46,7 +51,7 @@ public class AutoTimeZonePreferenceControllerTest { private AutoTimeZonePreferenceController mController; private Preference mPreference; @Mock - private TelephonyManager mTelephonyManager; + private TimeManager mTimeManager; @Before public void setUp() { @@ -55,12 +60,15 @@ public class AutoTimeZonePreferenceControllerTest { mPreference = new Preference(mContext); - when(mContext.getSystemService(TelephonyManager.class)).thenReturn(mTelephonyManager); - when(mTelephonyManager.isDataCapable()).thenReturn(true); + when(mContext.getSystemService(TimeManager.class)).thenReturn(mTimeManager); } @Test public void isFromSUW_notAvailable() { + TimeZoneCapabilitiesAndConfig capabilitiesAndConfig = createCapabilitiesAndConfig( + /* autoSupported= */true, /* autoEnabled= */false); + when(mTimeManager.getTimeZoneCapabilitiesAndConfig()).thenReturn(capabilitiesAndConfig); + mController = new AutoTimeZonePreferenceController( mContext, null /* callback */, true /* isFromSUW */); @@ -69,6 +77,10 @@ public class AutoTimeZonePreferenceControllerTest { @Test public void notFromSUW_isAvailable() { + TimeZoneCapabilitiesAndConfig capabilitiesAndConfig = createCapabilitiesAndConfig( + /* autoSupported= */true, /* autoEnabled= */false); + when(mTimeManager.getTimeZoneCapabilitiesAndConfig()).thenReturn(capabilitiesAndConfig); + mController = new AutoTimeZonePreferenceController( mContext, null /* callback */, false /* isFromSUW */); @@ -76,8 +88,11 @@ public class AutoTimeZonePreferenceControllerTest { } @Test - public void isWifiOnly_notAvailable() { - when(mTelephonyManager.isDataCapable()).thenReturn(false); + public void autoTimeZoneNotSupported_notAvailable() { + TimeZoneCapabilitiesAndConfig capabilitiesAndConfig = createCapabilitiesAndConfig( + /* autoSupported= */false, /* autoEnabled= */false); + when(mTimeManager.getTimeZoneCapabilitiesAndConfig()).thenReturn(capabilitiesAndConfig); + mController = new AutoTimeZonePreferenceController( mContext, null /* callback */, false /* fromSUW */); @@ -86,54 +101,134 @@ public class AutoTimeZonePreferenceControllerTest { @Test public void isFromSUW_notEnable() { - mController = - new AutoTimeZonePreferenceController(mContext, null /* callback */, true /* fromSUW */); + TimeZoneCapabilitiesAndConfig capabilitiesAndConfig = createCapabilitiesAndConfig( + /* autoSupported= */false, /* autoEnabled= */false); + when(mTimeManager.getTimeZoneCapabilitiesAndConfig()).thenReturn(capabilitiesAndConfig); + + mController = new AutoTimeZonePreferenceController( + mContext, null /* callback */, true /* fromSUW */); assertThat(mController.isEnabled()).isFalse(); } @Test - public void isWifiOnly_notEnable() { - when(mTelephonyManager.isDataCapable()).thenReturn(false); + public void isFromSUW_isEnable() { + TimeZoneCapabilitiesAndConfig capabilitiesAndConfig = createCapabilitiesAndConfig( + /* autoSupported= */false, /* autoEnabled= */true); + when(mTimeManager.getTimeZoneCapabilitiesAndConfig()).thenReturn(capabilitiesAndConfig); + mController = new AutoTimeZonePreferenceController( - mContext, null /* callback */, false /* fromSUW */); + mContext, null /* callback */, true /* fromSUW */); - assertThat(mController.isEnabled()).isFalse(); - } - - @Test - public void testIsEnabled_shouldReadFromSettingsProvider() { - mController = new AutoTimeZonePreferenceController( - mContext, null /* callback */, false /* fromSUW */); - - // Disabled - Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AUTO_TIME_ZONE, 0); - assertThat(mController.isEnabled()).isFalse(); - - // Enabled - Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AUTO_TIME_ZONE, 1); assertThat(mController.isEnabled()).isTrue(); } + @Test + public void autoTimeZoneNotSupported_notEnable() { + TimeZoneCapabilitiesAndConfig capabilitiesAndConfig = createCapabilitiesAndConfig( + /* autoSupported= */false, /* autoEnabled= */false); + when(mTimeManager.getTimeZoneCapabilitiesAndConfig()).thenReturn(capabilitiesAndConfig); + + mController = new AutoTimeZonePreferenceController( + mContext, null /* callback */, false /* fromSUW */); + + assertThat(mController.isEnabled()).isFalse(); + } + + @Test + public void testIsEnabled_shouldReadFromTimeManagerConfig() { + mController = new AutoTimeZonePreferenceController( + mContext, null /* callback */, false /* fromSUW */); + + { + // Disabled + TimeZoneCapabilitiesAndConfig capabilitiesAndConfig = createCapabilitiesAndConfig( + /* autoSupported= */true, /* autoEnabled= */false); + when(mTimeManager.getTimeZoneCapabilitiesAndConfig()).thenReturn(capabilitiesAndConfig); + + assertThat(mController.isEnabled()).isFalse(); + } + + { + // Enabled + TimeZoneCapabilitiesAndConfig capabilitiesAndConfig = createCapabilitiesAndConfig( + /* autoSupported= */true, /* autoEnabled= */true); + when(mTimeManager.getTimeZoneCapabilitiesAndConfig()).thenReturn(capabilitiesAndConfig); + + assertThat(mController.isEnabled()).isTrue(); + } + } + @Test public void updatePreferenceChange_prefIsChecked_shouldUpdatePreferenceAndNotifyCallback() { - mController = - new AutoTimeZonePreferenceController(mContext, mCallback, false /* fromSUW */); + TimeZoneCapabilitiesAndConfig capabilitiesAndConfig = createCapabilitiesAndConfig( + /* autoSupported= */true, /* autoEnabled= */false); + when(mTimeManager.getTimeZoneCapabilitiesAndConfig()).thenReturn(capabilitiesAndConfig); + when(mTimeManager.updateTimeZoneConfiguration(Mockito.any())).thenReturn(true); - mController.onPreferenceChange(mPreference, true); + mController = new AutoTimeZonePreferenceController( + mContext, mCallback, false /* fromSUW */); + + assertThat(mController.onPreferenceChange(mPreference, true)).isTrue(); + verify(mCallback).updateTimeAndDateDisplay(mContext); + + // Check the service was asked to change the configuration correctly. + TimeZoneConfiguration timeZoneConfiguration = new TimeZoneConfiguration.Builder() + .setAutoDetectionEnabled(true) + .build(); + verify(mTimeManager).updateTimeZoneConfiguration(timeZoneConfiguration); + + // Update the mTimeManager mock so that it now returns the expected updated config. + TimeZoneCapabilitiesAndConfig capabilitiesAndConfigAfterUpdate = + createCapabilitiesAndConfig(/* autoSupported= */true, /* autoEnabled= */true); + when(mTimeManager.getTimeZoneCapabilitiesAndConfig()) + .thenReturn(capabilitiesAndConfigAfterUpdate); assertThat(mController.isEnabled()).isTrue(); - verify(mCallback).updateTimeAndDateDisplay(mContext); } @Test public void updatePreferenceChange_prefIsUnchecked_shouldUpdatePreferenceAndNotifyCallback() { - mController = - new AutoTimeZonePreferenceController(mContext, mCallback, false /* fromSUW */); + TimeZoneCapabilitiesAndConfig capabilitiesAndConfig = createCapabilitiesAndConfig( + /* autoSupported= */true, /* autoEnabled= */true); + when(mTimeManager.getTimeZoneCapabilitiesAndConfig()).thenReturn(capabilitiesAndConfig); + when(mTimeManager.updateTimeZoneConfiguration(Mockito.any())).thenReturn(true); - mController.onPreferenceChange(mPreference, false); + mController = new AutoTimeZonePreferenceController( + mContext, mCallback, false /* fromSUW */); + + assertThat(mController.onPreferenceChange(mPreference, false)).isTrue(); + verify(mCallback).updateTimeAndDateDisplay(mContext); + + // Check the service was asked to change the configuration correctly. + TimeZoneConfiguration timeZoneConfiguration = new TimeZoneConfiguration.Builder() + .setAutoDetectionEnabled(false) + .build(); + verify(mTimeManager).updateTimeZoneConfiguration(timeZoneConfiguration); + + // Update the mTimeManager mock so that it now returns the expected updated config. + TimeZoneCapabilitiesAndConfig capabilitiesAndConfigAfterUpdate = + createCapabilitiesAndConfig(/* autoSupported= */true, /* autoEnabled= */false); + when(mTimeManager.getTimeZoneCapabilitiesAndConfig()) + .thenReturn(capabilitiesAndConfigAfterUpdate); assertThat(mController.isEnabled()).isFalse(); - verify(mCallback).updateTimeAndDateDisplay(mContext); + } + + private static TimeZoneCapabilitiesAndConfig createCapabilitiesAndConfig( + boolean autoSupported, boolean autoEnabled) { + int configureAutoDetectionEnabledCapability = + autoSupported ? Capabilities.CAPABILITY_POSSESSED + : Capabilities.CAPABILITY_NOT_SUPPORTED; + TimeZoneCapabilities capabilities = new TimeZoneCapabilities.Builder(UserHandle.SYSTEM) + .setConfigureAutoDetectionEnabledCapability(configureAutoDetectionEnabledCapability) + .setConfigureGeoDetectionEnabledCapability(Capabilities.CAPABILITY_NOT_SUPPORTED) + .setSuggestManualTimeZoneCapability(Capabilities.CAPABILITY_POSSESSED) + .build(); + TimeZoneConfiguration config = new TimeZoneConfiguration.Builder() + .setAutoDetectionEnabled(autoEnabled) + .setGeoDetectionEnabled(false) + .build(); + return new TimeZoneCapabilitiesAndConfig(capabilities, config); } } diff --git a/tests/robotests/src/com/android/settings/datetime/TimeZonePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/datetime/TimeZonePreferenceControllerTest.java index f94e8d1aa54..6767b4e2f72 100644 --- a/tests/robotests/src/com/android/settings/datetime/TimeZonePreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/datetime/TimeZonePreferenceControllerTest.java @@ -22,7 +22,13 @@ import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; +import android.app.time.Capabilities; +import android.app.time.TimeManager; +import android.app.time.TimeZoneCapabilities; +import android.app.time.TimeZoneCapabilitiesAndConfig; +import android.app.time.TimeZoneConfiguration; import android.content.Context; +import android.os.UserHandle; import com.android.settingslib.RestrictedPreference; @@ -38,8 +44,7 @@ import org.robolectric.RuntimeEnvironment; public class TimeZonePreferenceControllerTest { @Mock - private AutoTimeZonePreferenceController mAutoTimeZonePreferenceController; - + private TimeManager mTimeManager; private Context mContext; private TimeZonePreferenceController mController; private RestrictedPreference mPreference; @@ -47,10 +52,14 @@ public class TimeZonePreferenceControllerTest { @Before public void setUp() { MockitoAnnotations.initMocks(this); - mContext = RuntimeEnvironment.application; + + mContext = spy(RuntimeEnvironment.application); + doReturn(mTimeManager).when(mContext).getSystemService(TimeManager.class); + mPreference = new RestrictedPreference(mContext); - mController = spy(new TimeZonePreferenceController(mContext, - mAutoTimeZonePreferenceController)); + + mController = spy(new TimeZonePreferenceController(mContext)); + doReturn("test timezone").when(mController).getTimeZoneOffsetAndName(); } @Test @@ -59,26 +68,46 @@ public class TimeZonePreferenceControllerTest { } @Test - public void updateState_autoTimeZoneEnabled_shouldDisablePref() { + public void updateState_suggestManualNotAllowed_shouldDisablePref() { // Make sure not disabled by admin. mPreference.setDisabledByAdmin(null); - doReturn("test timezone").when(mController).getTimeZoneOffsetAndName(); - when(mAutoTimeZonePreferenceController.isEnabled()).thenReturn(true); + TimeZoneCapabilitiesAndConfig capabilitiesAndConfig = createCapabilitiesAndConfig( + /* suggestManualAllowed= */false); + when(mTimeManager.getTimeZoneCapabilitiesAndConfig()).thenReturn(capabilitiesAndConfig); + mController.updateState(mPreference); assertThat(mPreference.isEnabled()).isFalse(); } @Test - public void updateState_autoTimeZoneDisabled_shouldEnablePref() { + public void updateState_suggestManualAllowed_shouldEnablePref() { // Make sure not disabled by admin. mPreference.setDisabledByAdmin(null); - doReturn("test timezone").when(mController).getTimeZoneOffsetAndName(); - when(mAutoTimeZonePreferenceController.isEnabled()).thenReturn(false); + TimeZoneCapabilitiesAndConfig capabilitiesAndConfig = createCapabilitiesAndConfig( + /* suggestManualAllowed= */true); + when(mTimeManager.getTimeZoneCapabilitiesAndConfig()).thenReturn(capabilitiesAndConfig); + mController.updateState(mPreference); assertThat(mPreference.isEnabled()).isTrue(); } + + private static TimeZoneCapabilitiesAndConfig createCapabilitiesAndConfig( + boolean suggestManualAllowed) { + int suggestManualCapability = suggestManualAllowed ? Capabilities.CAPABILITY_POSSESSED + : Capabilities.CAPABILITY_NOT_SUPPORTED; + TimeZoneCapabilities capabilities = new TimeZoneCapabilities.Builder(UserHandle.SYSTEM) + .setConfigureAutoDetectionEnabledCapability(Capabilities.CAPABILITY_POSSESSED) + .setConfigureGeoDetectionEnabledCapability(Capabilities.CAPABILITY_NOT_SUPPORTED) + .setSuggestManualTimeZoneCapability(suggestManualCapability) + .build(); + TimeZoneConfiguration config = new TimeZoneConfiguration.Builder() + .setAutoDetectionEnabled(!suggestManualAllowed) + .setGeoDetectionEnabled(false) + .build(); + return new TimeZoneCapabilitiesAndConfig(capabilities, config); + } }