1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.settings.datetime; 18 19 import static android.app.time.Capabilities.CAPABILITY_NOT_APPLICABLE; 20 import static android.app.time.Capabilities.CAPABILITY_NOT_SUPPORTED; 21 import static android.app.time.Capabilities.CAPABILITY_POSSESSED; 22 import static android.app.time.DetectorStatusTypes.DETECTION_ALGORITHM_STATUS_NOT_SUPPORTED; 23 import static android.app.time.DetectorStatusTypes.DETECTION_ALGORITHM_STATUS_RUNNING; 24 import static android.app.time.DetectorStatusTypes.DETECTOR_STATUS_RUNNING; 25 import static android.app.time.LocationTimeZoneAlgorithmStatus.PROVIDER_STATUS_NOT_PRESENT; 26 import static android.app.time.LocationTimeZoneAlgorithmStatus.PROVIDER_STATUS_NOT_READY; 27 28 import static com.android.settings.core.BasePreferenceController.DISABLED_DEPENDENT_SETTING; 29 30 import static com.google.common.truth.Truth.assertThat; 31 32 import static org.mockito.ArgumentMatchers.any; 33 import static org.mockito.Mockito.never; 34 import static org.mockito.Mockito.spy; 35 import static org.mockito.Mockito.verify; 36 import static org.mockito.Mockito.when; 37 38 import android.app.time.Capabilities.CapabilityState; 39 import android.app.time.LocationTimeZoneAlgorithmStatus; 40 import android.app.time.TelephonyTimeZoneAlgorithmStatus; 41 import android.app.time.TimeManager; 42 import android.app.time.TimeZoneCapabilities; 43 import android.app.time.TimeZoneCapabilitiesAndConfig; 44 import android.app.time.TimeZoneConfiguration; 45 import android.app.time.TimeZoneDetectorStatus; 46 import android.content.Context; 47 import android.os.UserHandle; 48 49 import androidx.preference.SwitchPreference; 50 51 import com.android.settings.R; 52 import com.android.settings.core.InstrumentedPreferenceFragment; 53 54 import org.junit.Before; 55 import org.junit.Test; 56 import org.junit.runner.RunWith; 57 import org.mockito.Answers; 58 import org.mockito.Mock; 59 import org.mockito.MockitoAnnotations; 60 import org.robolectric.RobolectricTestRunner; 61 import org.robolectric.RuntimeEnvironment; 62 import org.robolectric.annotation.Config; 63 64 @RunWith(RobolectricTestRunner.class) 65 @Config(shadows = { 66 com.android.settings.testutils.shadow.ShadowFragment.class, 67 }) 68 public class LocationTimeZoneDetectionPreferenceControllerTest { 69 70 @Mock 71 private TimeManager mTimeManager; 72 private Context mContext; 73 private SwitchPreference mPreference; 74 private LocationTimeZoneDetectionPreferenceController mController; 75 @Mock(answer = Answers.RETURNS_DEEP_STUBS) 76 private InstrumentedPreferenceFragment mFragment; 77 78 @Before setUp()79 public void setUp() { 80 MockitoAnnotations.initMocks(this); 81 mContext = spy(RuntimeEnvironment.application); 82 when(mContext.getSystemService(TimeManager.class)).thenReturn(mTimeManager); 83 mController = new LocationTimeZoneDetectionPreferenceController(mContext); 84 mController.setFragment(mFragment); 85 86 mPreference = new SwitchPreference(mContext); 87 mPreference.setKey("location_time_zone_detection"); 88 } 89 90 @Test setChecked_withTrue_shouldUpdateSetting_whenLocationIsEnabled()91 public void setChecked_withTrue_shouldUpdateSetting_whenLocationIsEnabled() { 92 boolean useLocationEnabled = true; 93 TimeZoneCapabilitiesAndConfig capabilitiesAndConfig = 94 createTimeZoneCapabilitiesAndConfig(useLocationEnabled, CAPABILITY_POSSESSED); 95 when(mTimeManager.getTimeZoneCapabilitiesAndConfig()).thenReturn(capabilitiesAndConfig); 96 97 // Simulate the UI being clicked. 98 mController.setChecked(true); 99 100 // Verify the TimeManager was updated with the UI value. 101 TimeZoneConfiguration expectedConfiguration = new TimeZoneConfiguration.Builder() 102 .setGeoDetectionEnabled(true) 103 .build(); 104 verify(mTimeManager).updateTimeZoneConfiguration(expectedConfiguration); 105 } 106 107 @Test isNotSliceable()108 public void isNotSliceable() { 109 assertThat(mController.isSliceable()).isFalse(); 110 } 111 112 @Test setChecked_withTrue_shouldDoNothing_whenLocationIsDisabled()113 public void setChecked_withTrue_shouldDoNothing_whenLocationIsDisabled() { 114 boolean useLocationEnabled = false; 115 TimeZoneCapabilitiesAndConfig capabilitiesAndConfig = 116 createTimeZoneCapabilitiesAndConfig(useLocationEnabled, CAPABILITY_POSSESSED); 117 when(mTimeManager.getTimeZoneCapabilitiesAndConfig()).thenReturn(capabilitiesAndConfig); 118 119 // Simulate the UI being clicked. 120 mController.setChecked(true); 121 122 // Verify the TimeManager was not updated. 123 verify(mTimeManager, never()).updateTimeZoneConfiguration(any()); 124 } 125 126 @Test toggleOff_automaticTimeZone_disablesLocationToggle()127 public void toggleOff_automaticTimeZone_disablesLocationToggle() { 128 TimeZoneCapabilitiesAndConfig capabilitiesAndConfig = 129 createTimeZoneCapabilitiesAndConfig(/* useLocationEnabled= */ true, 130 CAPABILITY_POSSESSED, /* setAutoDetectionEnabled= */ false); 131 when(mTimeManager.getTimeZoneCapabilitiesAndConfig()).thenReturn(capabilitiesAndConfig); 132 133 assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_DEPENDENT_SETTING); 134 } 135 136 @Test setChecked_withFalse_shouldUpdateSetting()137 public void setChecked_withFalse_shouldUpdateSetting() { 138 boolean useLocationEnabled = false; 139 TimeZoneCapabilitiesAndConfig capabilitiesAndConfig = 140 createTimeZoneCapabilitiesAndConfig(useLocationEnabled, CAPABILITY_POSSESSED); 141 when(mTimeManager.getTimeZoneCapabilitiesAndConfig()).thenReturn(capabilitiesAndConfig); 142 143 // Simulate the UI being clicked. 144 mController.setChecked(false); 145 146 // Verify the TimeManager was updated with the UI value. 147 TimeZoneConfiguration expectedConfiguration = new TimeZoneConfiguration.Builder() 148 .setGeoDetectionEnabled(false) 149 .build(); 150 verify(mTimeManager).updateTimeZoneConfiguration(expectedConfiguration); 151 } 152 153 @Test testLocationTimeZoneDetection_supported_shouldBeShown()154 public void testLocationTimeZoneDetection_supported_shouldBeShown() { 155 boolean useLocationEnabled = false; 156 TimeZoneCapabilitiesAndConfig capabilitiesAndConfig = 157 createTimeZoneCapabilitiesAndConfig(useLocationEnabled, CAPABILITY_POSSESSED); 158 when(mTimeManager.getTimeZoneCapabilitiesAndConfig()).thenReturn(capabilitiesAndConfig); 159 160 assertThat(mController.isAvailable()).isTrue(); 161 } 162 163 @Test testLocationTimeZoneDetection_unsupported_shouldNotBeShown()164 public void testLocationTimeZoneDetection_unsupported_shouldNotBeShown() { 165 boolean useLocationEnabled = false; 166 TimeZoneCapabilitiesAndConfig capabilitiesAndConfig = createTimeZoneCapabilitiesAndConfig( 167 useLocationEnabled, CAPABILITY_NOT_SUPPORTED); 168 when(mTimeManager.getTimeZoneCapabilitiesAndConfig()).thenReturn(capabilitiesAndConfig); 169 170 assertThat(mController.isAvailable()).isFalse(); 171 } 172 173 /** 174 * Tests that the summary is set in just one of many cases. Exhaustive testing would be brittle. 175 */ 176 @Test testLocationTimeZoneDetection_summary_geoDetectionEnabled()177 public void testLocationTimeZoneDetection_summary_geoDetectionEnabled() { 178 boolean useLocationEnabled = false; 179 TimeZoneCapabilitiesAndConfig capabilitiesAndConfig = 180 createTimeZoneCapabilitiesAndConfig(useLocationEnabled, CAPABILITY_POSSESSED); 181 182 when(mTimeManager.getTimeZoneCapabilitiesAndConfig()).thenReturn(capabilitiesAndConfig); 183 assertThat(mController.getSummary()).isEqualTo( 184 mContext.getString(R.string.location_time_zone_detection_auto_is_on)); 185 } 186 187 @Test testLocationTimeZoneDetection_toggleIsOn_whenGeoDetectionEnabledAnsMlsIsOff()188 public void testLocationTimeZoneDetection_toggleIsOn_whenGeoDetectionEnabledAnsMlsIsOff() { 189 boolean useLocationEnabled = false; 190 TimeZoneCapabilitiesAndConfig capabilitiesAndConfig = createTimeZoneCapabilitiesAndConfig( 191 useLocationEnabled, CAPABILITY_NOT_APPLICABLE); 192 193 when(mTimeManager.getTimeZoneCapabilitiesAndConfig()).thenReturn(capabilitiesAndConfig); 194 195 assertThat(mController.isChecked()).isTrue(); 196 assertThat(mController.getSummary()).isEqualTo( 197 mContext.getString(R.string.location_app_permission_summary_location_off)); 198 } 199 createTimeZoneCapabilitiesAndConfig( boolean useLocationEnabled, @CapabilityState int configureGeoDetectionEnabledCapability)200 private static TimeZoneCapabilitiesAndConfig createTimeZoneCapabilitiesAndConfig( 201 boolean useLocationEnabled, 202 @CapabilityState int configureGeoDetectionEnabledCapability) { 203 return createTimeZoneCapabilitiesAndConfig(useLocationEnabled, 204 configureGeoDetectionEnabledCapability, /* setAutoDetectionEnabled= */ true); 205 } 206 createTimeZoneCapabilitiesAndConfig( boolean useLocationEnabled, @CapabilityState int configureGeoDetectionEnabledCapability, boolean setAutoDetectionEnabled)207 private static TimeZoneCapabilitiesAndConfig createTimeZoneCapabilitiesAndConfig( 208 boolean useLocationEnabled, 209 @CapabilityState int configureGeoDetectionEnabledCapability, 210 boolean setAutoDetectionEnabled) { 211 // Create a status that matches the user's capability state. 212 LocationTimeZoneAlgorithmStatus locationAlgorithmStatus; 213 switch (configureGeoDetectionEnabledCapability) { 214 case CAPABILITY_NOT_SUPPORTED: 215 locationAlgorithmStatus = new LocationTimeZoneAlgorithmStatus( 216 DETECTION_ALGORITHM_STATUS_NOT_SUPPORTED, 217 PROVIDER_STATUS_NOT_PRESENT, null, PROVIDER_STATUS_NOT_PRESENT, null); 218 break; 219 case CAPABILITY_NOT_APPLICABLE: 220 case CAPABILITY_POSSESSED: 221 locationAlgorithmStatus = new LocationTimeZoneAlgorithmStatus( 222 DETECTION_ALGORITHM_STATUS_RUNNING, 223 PROVIDER_STATUS_NOT_READY, null, PROVIDER_STATUS_NOT_READY, null); 224 break; 225 default: 226 throw new AssertionError( 227 "Unsupported capability state: " + configureGeoDetectionEnabledCapability); 228 } 229 TimeZoneDetectorStatus status = new TimeZoneDetectorStatus(DETECTOR_STATUS_RUNNING, 230 new TelephonyTimeZoneAlgorithmStatus(DETECTION_ALGORITHM_STATUS_RUNNING), 231 locationAlgorithmStatus); 232 233 UserHandle arbitraryUserHandle = UserHandle.of(123); 234 TimeZoneCapabilities capabilities = new TimeZoneCapabilities.Builder(arbitraryUserHandle) 235 .setConfigureAutoDetectionEnabledCapability(CAPABILITY_POSSESSED) 236 .setUseLocationEnabled(useLocationEnabled) 237 .setConfigureGeoDetectionEnabledCapability(configureGeoDetectionEnabledCapability) 238 .setSetManualTimeZoneCapability(CAPABILITY_NOT_APPLICABLE) 239 .setConfigureNotificationsEnabledCapability(CAPABILITY_POSSESSED) 240 .build(); 241 242 TimeZoneConfiguration configuration = new TimeZoneConfiguration.Builder() 243 .setAutoDetectionEnabled(setAutoDetectionEnabled) 244 .setGeoDetectionEnabled(true) 245 .setNotificationsEnabled(true) 246 .build(); 247 248 return new TimeZoneCapabilitiesAndConfig(status, capabilities, configuration); 249 } 250 } 251