diff --git a/res/values/strings.xml b/res/values/strings.xml index 9ee2800f9aa..82256f90037 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -4002,6 +4002,32 @@ Location services for work + + Location time zone detection + + Location time zone detection + + Allows the device\u2019s location to be used to detect the current time zone. Other location settings such as Wi\u2011Fi scanning can affect the accuracy of time zone detection. + + + On + + Off + + Automatic time zone detection is disabled + + Location time zone detection is disabled + + Location time zone detection is not supported + + Location time zone detection changes are not allowed + Wi\u2011Fi & mobile network location diff --git a/res/xml/location_settings.xml b/res/xml/location_settings.xml index fb03f4c7f3d..80f4973539b 100644 --- a/res/xml/location_settings.xml +++ b/res/xml/location_settings.xml @@ -65,6 +65,13 @@ settings:forWork="true" settings:useAdminDisabledSummary="true"/> + + + + + + + + + + + + + diff --git a/src/com/android/settings/location/LocationPersonalSettings.java b/src/com/android/settings/location/LocationPersonalSettings.java index 92796a4bd66..3f90896fba2 100644 --- a/src/com/android/settings/location/LocationPersonalSettings.java +++ b/src/com/android/settings/location/LocationPersonalSettings.java @@ -58,6 +58,8 @@ public class LocationPersonalSettings extends DashboardFragment { RecentLocationRequestPreferenceController.class); controller.init(this); controller.setProfileType(profileType); + + use(LocationTimeZoneDetectionPreferenceController.class); } @Override diff --git a/src/com/android/settings/location/LocationSettings.java b/src/com/android/settings/location/LocationSettings.java index 43918111cca..248620b3fef 100644 --- a/src/com/android/settings/location/LocationSettings.java +++ b/src/com/android/settings/location/LocationSettings.java @@ -88,6 +88,7 @@ public class LocationSettings extends DashboardFragment { use(LocationFooterPreferenceController.class).init(this); use(LocationForWorkPreferenceController.class).init(this); use(LocationServiceForWorkPreferenceController.class).init(this); + use(LocationTimeZoneDetectionPreferenceController.class); } @Override diff --git a/src/com/android/settings/location/LocationTimeZoneDetectionPreferenceController.java b/src/com/android/settings/location/LocationTimeZoneDetectionPreferenceController.java new file mode 100644 index 00000000000..a16c9bbd792 --- /dev/null +++ b/src/com/android/settings/location/LocationTimeZoneDetectionPreferenceController.java @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.settings.location; + +import static android.app.time.TimeZoneCapabilities.CAPABILITY_NOT_ALLOWED; +import static android.app.time.TimeZoneCapabilities.CAPABILITY_NOT_APPLICABLE; +import static android.app.time.TimeZoneCapabilities.CAPABILITY_NOT_SUPPORTED; +import static android.app.time.TimeZoneCapabilities.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 android.location.LocationManager; + +import androidx.preference.Preference; +import androidx.preference.PreferenceScreen; + +import com.android.settings.R; +import com.android.settings.core.BasePreferenceController; +import com.android.settingslib.core.lifecycle.LifecycleObserver; +import com.android.settingslib.core.lifecycle.events.OnStart; +import com.android.settingslib.core.lifecycle.events.OnStop; + +import java.util.concurrent.Executor; + +/** + * The controller for the "location time zone detection" entry in the Location settings + * screen. + */ +public class LocationTimeZoneDetectionPreferenceController + extends BasePreferenceController + implements LifecycleObserver, OnStart, OnStop, TimeManager.TimeZoneDetectorListener { + + private final TimeManager mTimeManager; + private final LocationManager mLocationManager; + private TimeZoneCapabilitiesAndConfig mTimeZoneCapabilitiesAndConfig; + private Preference mPreference; + + public LocationTimeZoneDetectionPreferenceController(Context context, String key) { + super(context, key); + mTimeManager = context.getSystemService(TimeManager.class); + mLocationManager = context.getSystemService(LocationManager.class); + } + + @Override + public void displayPreference(PreferenceScreen screen) { + super.displayPreference(screen); + mPreference = screen.findPreference(getPreferenceKey()); + } + + @Override + public void onStart() { + // Register for updates to the user's time zone capabilities or configuration which could + // require UI changes. + Executor mainExecutor = mContext.getMainExecutor(); + mTimeManager.addTimeZoneDetectorListener(mainExecutor, this); + // Setup the initial state of the summary. + refreshUi(); + } + + @Override + public void onStop() { + mTimeManager.removeTimeZoneDetectorListener(this); + } + + @Override + public int getAvailabilityStatus() { + TimeZoneCapabilities timeZoneCapabilities = + getTimeZoneCapabilitiesAndConfig(/* forceRefresh= */ false).getCapabilities(); + int capability = timeZoneCapabilities.getConfigureGeoDetectionEnabledCapability(); + + // 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) { + return UNSUPPORTED_ON_DEVICE; + } else if (capability == CAPABILITY_NOT_APPLICABLE || capability == CAPABILITY_POSSESSED) { + return AVAILABLE; + } else { + throw new IllegalStateException("Unknown capability=" + capability); + } + } + + @Override + public CharSequence getSummary() { + TimeZoneCapabilitiesAndConfig timeZoneCapabilitiesAndConfig = + getTimeZoneCapabilitiesAndConfig(/* forceRefresh= */ false); + TimeZoneCapabilities capabilities = timeZoneCapabilitiesAndConfig.getCapabilities(); + int configureGeoDetectionEnabledCapability = + capabilities.getConfigureGeoDetectionEnabledCapability(); + TimeZoneConfiguration configuration = timeZoneCapabilitiesAndConfig.getConfiguration(); + + int summaryResId; + if (configureGeoDetectionEnabledCapability == CAPABILITY_NOT_SUPPORTED) { + // The preference should not be visible, but text is referenced in case this changes. + summaryResId = R.string.location_time_zone_detection_not_supported; + } else if (configureGeoDetectionEnabledCapability == CAPABILITY_NOT_ALLOWED) { + // The preference should not be visible, but text is referenced in case this changes. + summaryResId = R.string.location_time_zone_detection_not_allowed; + } else if (configureGeoDetectionEnabledCapability == CAPABILITY_NOT_APPLICABLE) { + // The TimeZoneCapabilities deliberately doesn't provide information about why the user + // doesn't have the capability, but the user's "location enabled" being off and the + // global automatic detection setting will always be considered overriding reasons why + // location time zone detection cannot be used. + if (!mLocationManager.isLocationEnabled()) { + summaryResId = R.string.location_app_permission_summary_location_off; + } else if (!configuration.isAutoDetectionEnabled()) { + summaryResId = R.string.location_time_zone_detection_auto_is_off; + } else { + // This is in case there are other reasons in future why location time zone + // detection is not applicable. + summaryResId = R.string.location_time_zone_detection_not_applicable; + } + } else if (configureGeoDetectionEnabledCapability == CAPABILITY_POSSESSED) { + boolean isGeoDetectionEnabled = configuration.isGeoDetectionEnabled(); + summaryResId = isGeoDetectionEnabled + ? R.string.location_time_zone_detection_on + : R.string.location_time_zone_detection_off; + } else { + // This is unexpected: getAvailabilityStatus() should ensure that the UI element isn't + // even shown for known cases, or the capability is unknown. + throw new IllegalStateException("Unexpected configureGeoDetectionEnabledCapability=" + + configureGeoDetectionEnabledCapability); + } + return mContext.getString(summaryResId); + } + + @Override + public void onChange() { + refreshUi(); + } + + private void refreshUi() { + // Force a refresh of cached user capabilities and config before refreshing the summary. + getTimeZoneCapabilitiesAndConfig(/* forceRefresh= */ true); + refreshSummary(mPreference); + } + + /** + * Returns the current user capabilities and configuration. {@code forceRefresh} can be {@code + * true} to discard any cached copy. + */ + private TimeZoneCapabilitiesAndConfig getTimeZoneCapabilitiesAndConfig(boolean forceRefresh) { + if (forceRefresh || mTimeZoneCapabilitiesAndConfig == null) { + mTimeZoneCapabilitiesAndConfig = mTimeManager.getTimeZoneCapabilitiesAndConfig(); + } + return mTimeZoneCapabilitiesAndConfig; + } +} diff --git a/src/com/android/settings/location/TimeZoneDetectionPreferenceController.java b/src/com/android/settings/location/TimeZoneDetectionPreferenceController.java new file mode 100644 index 00000000000..946376d77fa --- /dev/null +++ b/src/com/android/settings/location/TimeZoneDetectionPreferenceController.java @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.location; + +import android.app.time.TimeManager; +import android.app.time.TimeZoneCapabilitiesAndConfig; +import android.app.time.TimeZoneConfiguration; +import android.content.Context; +import android.text.TextUtils; + +import androidx.preference.Preference; +import androidx.preference.SwitchPreference; + +import com.android.settingslib.core.AbstractPreferenceController; + +/** + * The controller for the "location time zone detection" switch on the location time zone detection + * screen. + */ +public class TimeZoneDetectionPreferenceController extends AbstractPreferenceController { + + private static final String KEY_LOCATION_TIME_ZONE_DETECTION_ENABLED = + "location_time_zone_detection_enabled"; + + private final TimeManager mTimeManager; + + public TimeZoneDetectionPreferenceController(Context context) { + super(context); + mTimeManager = context.getSystemService(TimeManager.class); + } + + @Override + public boolean isAvailable() { + return true; + } + + @Override + public String getPreferenceKey() { + return KEY_LOCATION_TIME_ZONE_DETECTION_ENABLED; + } + + @Override + public void updateState(Preference preference) { + TimeZoneCapabilitiesAndConfig capabilitiesAndConfig = + mTimeManager.getTimeZoneCapabilitiesAndConfig(); + setPreferenceUiState((SwitchPreference) preference, capabilitiesAndConfig); + } + + @Override + public boolean handlePreferenceTreeClick(Preference preference) { + if (TextUtils.equals(preference.getKey(), KEY_LOCATION_TIME_ZONE_DETECTION_ENABLED)) { + SwitchPreference switchPreference = (SwitchPreference) preference; + final boolean switchState = switchPreference.isChecked(); + + // Update the settings to match the UI. + TimeZoneConfiguration configuration = new TimeZoneConfiguration.Builder() + .setGeoDetectionEnabled(switchState) + .build(); + + // The return value is ignored, but the current state is read back below ensuring it + // does not matter. + mTimeManager.updateTimeZoneConfiguration(configuration); + + // Configure the UI preference state from the configuration. This means that even in the + // unlikely event that the update failed, the UI should reflect current settings. + setPreferenceUiState(switchPreference, mTimeManager.getTimeZoneCapabilitiesAndConfig()); + + return true; + } + return false; + } + + /** + * Sets the switch's checked state from the supplied {@link TimeZoneCapabilitiesAndConfig}. + */ + @android.annotation.UiThread + private void setPreferenceUiState(SwitchPreference preference, + TimeZoneCapabilitiesAndConfig timeZoneCapabilitiesAndConfig) { + TimeZoneConfiguration configuration = timeZoneCapabilitiesAndConfig.getConfiguration(); + boolean checked = configuration.isGeoDetectionEnabled(); + preference.setChecked(checked); + } +} diff --git a/src/com/android/settings/location/TimeZoneDetectionSettings.java b/src/com/android/settings/location/TimeZoneDetectionSettings.java new file mode 100644 index 00000000000..555d9d47092 --- /dev/null +++ b/src/com/android/settings/location/TimeZoneDetectionSettings.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.settings.location; + +import android.app.settings.SettingsEnums; +import android.content.Context; + +import com.android.settings.R; +import com.android.settings.dashboard.DashboardFragment; +import com.android.settings.search.BaseSearchIndexProvider; +import com.android.settingslib.core.AbstractPreferenceController; +import com.android.settingslib.search.SearchIndexable; + +import java.util.ArrayList; +import java.util.List; + +/** + * The controller for the "location time zone detection" screen. + */ +@SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC) +public class TimeZoneDetectionSettings extends DashboardFragment { + private static final String TAG = "LTZDetectionSettings"; + + @Override + public int getMetricsCategory() { + return SettingsEnums.LOCATION_TIME_ZONE_DETECTION; + } + + @Override + protected int getPreferenceScreenResId() { + return R.xml.location_time_zone_detection; + } + + @Override + protected String getLogTag() { + return TAG; + } + + @Override + protected List createPreferenceControllers(Context context) { + return buildPreferenceControllers(context); + } + + private static List buildPreferenceControllers(Context context) { + final List controllers = new ArrayList<>(); + controllers.add(new TimeZoneDetectionPreferenceController(context)); + return controllers; + } + + /** + * For Search. + */ + public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = + new BaseSearchIndexProvider(R.xml.location_time_zone_detection) { + + @Override + public List createPreferenceControllers(Context + context) { + return buildPreferenceControllers(context); + } + }; +} diff --git a/tests/robotests/src/com/android/settings/location/LocationTimeZoneDetectionPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/location/LocationTimeZoneDetectionPreferenceControllerTest.java new file mode 100644 index 00000000000..03e684b6aa3 --- /dev/null +++ b/tests/robotests/src/com/android/settings/location/LocationTimeZoneDetectionPreferenceControllerTest.java @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.location; + +import static android.app.time.TimeZoneCapabilities.CAPABILITY_NOT_APPLICABLE; +import static android.app.time.TimeZoneCapabilities.CAPABILITY_NOT_SUPPORTED; +import static android.app.time.TimeZoneCapabilities.CAPABILITY_POSSESSED; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; + +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.location.LocationManager; +import android.os.UserHandle; + +import com.android.settings.R; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; + +@RunWith(RobolectricTestRunner.class) +public class LocationTimeZoneDetectionPreferenceControllerTest { + @Mock + private TimeManager mTimeManager; + @Mock + private LocationManager mLocationManager; + private Context mContext; + private LocationTimeZoneDetectionPreferenceController mController; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mContext = spy(RuntimeEnvironment.application); + when(mContext.getSystemService(TimeManager.class)).thenReturn(mTimeManager); + when(mContext.getSystemService(LocationManager.class)).thenReturn(mLocationManager); + mController = new LocationTimeZoneDetectionPreferenceController(mContext, "key"); + } + + @Test + public void testLocationTimeZoneDetection_supported_shouldBeShown() { + TimeZoneCapabilities capabilities = + createTimeZoneCapabilities(/* geoDetectionSupported= */ true); + TimeZoneConfiguration configuration = createTimeZoneConfig(/* geoDetectionEnabled= */ true); + TimeZoneCapabilitiesAndConfig capabilitiesAndConfig = + new TimeZoneCapabilitiesAndConfig(capabilities, configuration); + when(mTimeManager.getTimeZoneCapabilitiesAndConfig()).thenReturn(capabilitiesAndConfig); + + assertThat(mController.isAvailable()).isTrue(); + } + + @Test + public void testLocationTimeZoneDetection_unsupported_shouldNotBeShown() { + TimeZoneCapabilities capabilities = + createTimeZoneCapabilities(/* geoDetectionSupported= */ false); + TimeZoneConfiguration configuration = createTimeZoneConfig(/* geoDetectionEnabled= */ true); + TimeZoneCapabilitiesAndConfig capabilitiesAndConfig = + new TimeZoneCapabilitiesAndConfig(capabilities, configuration); + when(mTimeManager.getTimeZoneCapabilitiesAndConfig()).thenReturn(capabilitiesAndConfig); + + assertThat(mController.isAvailable()).isFalse(); + } + + /** + * Tests that the summary is set in just one of many cases. Exhaustive testing would be brittle. + */ + @Test + public void testLocationTimeZoneDetection_summary_geoDetectionEnabled() { + TimeZoneCapabilities capabilities = + createTimeZoneCapabilities(/* geoDetectionSupported= */ true); + TimeZoneConfiguration configuration = createTimeZoneConfig(/* geoDetectionEnabled= */ true); + TimeZoneCapabilitiesAndConfig capabilitiesAndConfig = + new TimeZoneCapabilitiesAndConfig(capabilities, configuration); + + when(mTimeManager.getTimeZoneCapabilitiesAndConfig()).thenReturn(capabilitiesAndConfig); + assertThat(mController.getSummary()).isEqualTo( + mContext.getString(R.string.location_time_zone_detection_on)); + } + + private static TimeZoneCapabilities createTimeZoneCapabilities(boolean geoDetectionSupported) { + UserHandle arbitraryUserHandle = UserHandle.of(123); + int geoDetectionCapability = + geoDetectionSupported ? CAPABILITY_POSSESSED : CAPABILITY_NOT_SUPPORTED; + return new TimeZoneCapabilities.Builder(arbitraryUserHandle) + .setConfigureAutoDetectionEnabledCapability(CAPABILITY_POSSESSED) + .setConfigureGeoDetectionEnabledCapability(geoDetectionCapability) + .setSuggestManualTimeZoneCapability(CAPABILITY_NOT_APPLICABLE) + .build(); + } + + private static TimeZoneConfiguration createTimeZoneConfig(boolean geoDetectionEnabled) { + return new TimeZoneConfiguration.Builder() + .setAutoDetectionEnabled(true) + .setGeoDetectionEnabled(geoDetectionEnabled) + .build(); + } +} diff --git a/tests/robotests/src/com/android/settings/location/TimeZoneDetectionPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/location/TimeZoneDetectionPreferenceControllerTest.java new file mode 100644 index 00000000000..de6be5618cc --- /dev/null +++ b/tests/robotests/src/com/android/settings/location/TimeZoneDetectionPreferenceControllerTest.java @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.settings.location; + +import static android.app.time.TimeZoneCapabilities.CAPABILITY_NOT_APPLICABLE; +import static android.app.time.TimeZoneCapabilities.CAPABILITY_POSSESSED; + +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.time.TimeManager; +import android.app.time.TimeZoneCapabilities; +import android.app.time.TimeZoneCapabilitiesAndConfig; +import android.app.time.TimeZoneConfiguration; +import android.content.ContentResolver; +import android.content.Context; +import android.os.UserHandle; + +import androidx.preference.SwitchPreference; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; + +@RunWith(RobolectricTestRunner.class) +public class TimeZoneDetectionPreferenceControllerTest { + + @Mock + private SwitchPreference mPreference; + @Mock + private TimeManager mTimeManager; + + private Context mContext; + private ContentResolver mContentResolver; + private TimeZoneDetectionPreferenceController mController; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mContentResolver = RuntimeEnvironment.application.getContentResolver(); + mContext = spy(RuntimeEnvironment.application); + when(mContext.getSystemService(TimeManager.class)).thenReturn(mTimeManager); + + mController = new TimeZoneDetectionPreferenceController(mContext); + when(mPreference.getKey()).thenReturn(mController.getPreferenceKey()); + } + + @Test + public void updateState_locationDetectionEnabled_shouldCheckPreference() { + TimeZoneCapabilitiesAndConfig capabilitiesAndConfig = + createTimeZoneCapabilitiesAndConfig(/* geoDetectionEnabled= */ true); + when(mTimeManager.getTimeZoneCapabilitiesAndConfig()).thenReturn(capabilitiesAndConfig); + + mController.updateState(mPreference); + + verify(mPreference).setChecked(true); + } + + @Test + public void updateState_locationDetectionDisabled_shouldUncheckPreference() { + TimeZoneCapabilitiesAndConfig capabilitiesAndConfig = + createTimeZoneCapabilitiesAndConfig(/* geoDetectionEnabled= */ false); + when(mTimeManager.getTimeZoneCapabilitiesAndConfig()).thenReturn(capabilitiesAndConfig); + + mController.updateState(mPreference); + + verify(mPreference).setChecked(false); + } + + @Test + public void handlePreferenceTreeClick_unchecked_shouldDisableGeoDetection() { + // getTimeZoneCapabilitiesAndConfig() is called after updateTimeZoneConfiguration() to + // obtain the new state. + boolean postUpdateResponseValue = false; + TimeZoneCapabilitiesAndConfig capabilitiesAndConfig = createTimeZoneCapabilitiesAndConfig( + /* geoDetectionEnabled= */postUpdateResponseValue); + when(mTimeManager.getTimeZoneCapabilitiesAndConfig()).thenReturn(capabilitiesAndConfig); + + // Simulate the UI being clicked. + boolean preferenceCheckedState = false; + when(mPreference.isChecked()).thenReturn(preferenceCheckedState); + mController.handlePreferenceTreeClick(mPreference); + + // Verify the TimeManager was updated with the UI value. + TimeZoneConfiguration expectedConfiguration = new TimeZoneConfiguration.Builder() + .setGeoDetectionEnabled(preferenceCheckedState) + .build(); + verify(mTimeManager).updateTimeZoneConfiguration(expectedConfiguration); + + // Confirm the UI state was reset using the getTimeZoneCapabilitiesAndConfig() response. + verify(mPreference).setChecked(postUpdateResponseValue); + } + + @Test + public void handlePreferenceTreeClick_checked_shouldEnableGeoDetection() { + // getTimeZoneCapabilitiesAndConfig() is called after updateTimeZoneConfiguration() to + // obtain the new state. + boolean postUpdateResponseValue = true; + TimeZoneCapabilitiesAndConfig capabilitiesAndConfig = createTimeZoneCapabilitiesAndConfig( + /* geoDetectionEnabled= */ postUpdateResponseValue); + when(mTimeManager.getTimeZoneCapabilitiesAndConfig()).thenReturn(capabilitiesAndConfig); + + // Simulate the UI being clicked. + boolean preferenceCheckedState = true; + when(mPreference.isChecked()).thenReturn(preferenceCheckedState); + mController.handlePreferenceTreeClick(mPreference); + + // Verify the TimeManager was updated with the UI value. + TimeZoneConfiguration expectedConfiguration = new TimeZoneConfiguration.Builder() + .setGeoDetectionEnabled(preferenceCheckedState) + .build(); + verify(mTimeManager).updateTimeZoneConfiguration(expectedConfiguration); + + // Confirm the UI state was reset using the getTimeZoneCapabilitiesAndConfig() response. + verify(mPreference).setChecked(postUpdateResponseValue); + } + + @Test + public void handlePreferenceTreeClick_checked_shouldEnableGeoDetection_updateRefused() { + // getTimeZoneCapabilitiesAndConfig() is called after updateTimeZoneConfiguration() to + // obtain the new state. + boolean postUpdateResponseValue = false; + TimeZoneCapabilitiesAndConfig capabilitiesAndConfig = createTimeZoneCapabilitiesAndConfig( + /* geoDetectionEnabled= */ postUpdateResponseValue); + when(mTimeManager.getTimeZoneCapabilitiesAndConfig()).thenReturn(capabilitiesAndConfig); + + // Simulate the UI being clicked. + boolean preferenceCheckedState = true; + when(mPreference.isChecked()).thenReturn(preferenceCheckedState); + mController.handlePreferenceTreeClick(mPreference); + + // Verify the TimeManager was updated with the UI value. + TimeZoneConfiguration expectedConfiguration = new TimeZoneConfiguration.Builder() + .setGeoDetectionEnabled(preferenceCheckedState) + .build(); + verify(mTimeManager).updateTimeZoneConfiguration(expectedConfiguration); + + // Confirm the UI state was reset using the getTimeZoneCapabilitiesAndConfig() response. + verify(mPreference).setChecked(postUpdateResponseValue); + } + + private static TimeZoneCapabilitiesAndConfig createTimeZoneCapabilitiesAndConfig( + boolean geoDetectionEnabled) { + UserHandle arbitraryUserHandle = UserHandle.of(123); + TimeZoneCapabilities capabilities = new TimeZoneCapabilities.Builder(arbitraryUserHandle) + .setConfigureAutoDetectionEnabledCapability(CAPABILITY_POSSESSED) + .setConfigureGeoDetectionEnabledCapability(CAPABILITY_POSSESSED) + .setSuggestManualTimeZoneCapability(CAPABILITY_NOT_APPLICABLE) + .build(); + TimeZoneConfiguration configuration = new TimeZoneConfiguration.Builder() + .setAutoDetectionEnabled(true) + .setGeoDetectionEnabled(geoDetectionEnabled) + .build(); + return new TimeZoneCapabilitiesAndConfig(capabilities, configuration); + } +} diff --git a/tests/robotests/src/com/android/settings/location/TimeZoneDetectionSettingsTest.java b/tests/robotests/src/com/android/settings/location/TimeZoneDetectionSettingsTest.java new file mode 100644 index 00000000000..a8104bd73b4 --- /dev/null +++ b/tests/robotests/src/com/android/settings/location/TimeZoneDetectionSettingsTest.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.location; + +import static com.google.common.truth.Truth.assertThat; + +import android.content.Context; +import android.provider.SearchIndexableResource; + +import com.android.settings.R; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; + +import java.util.List; + +@RunWith(RobolectricTestRunner.class) +public class TimeZoneDetectionSettingsTest { + + private Context mContext; + + @Before + public void setUp() { + mContext = RuntimeEnvironment.application; + } + + @Test + public void searchProvider_shouldIndexDefaultXml() { + final List sir = + TimeZoneDetectionSettings.SEARCH_INDEX_DATA_PROVIDER.getXmlResourcesToIndex( + mContext, /* enabled= */ true); + + assertThat(sir).hasSize(1); + assertThat(sir.get(0).xmlResId).isEqualTo(R.xml.location_time_zone_detection); + } +}