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.google.common.truth.Truth.assertThat; 29 30 import static org.mockito.ArgumentMatchers.any; 31 import static org.mockito.Mockito.never; 32 import static org.mockito.Mockito.spy; 33 import static org.mockito.Mockito.verify; 34 import static org.mockito.Mockito.when; 35 36 import android.app.time.Capabilities.CapabilityState; 37 import android.app.time.LocationTimeZoneAlgorithmStatus; 38 import android.app.time.TelephonyTimeZoneAlgorithmStatus; 39 import android.app.time.TimeManager; 40 import android.app.time.TimeZoneCapabilities; 41 import android.app.time.TimeZoneCapabilitiesAndConfig; 42 import android.app.time.TimeZoneConfiguration; 43 import android.app.time.TimeZoneDetectorStatus; 44 import android.content.Context; 45 import android.os.UserHandle; 46 47 import com.android.settings.R; 48 import com.android.settings.core.InstrumentedPreferenceFragment; 49 50 import org.junit.Before; 51 import org.junit.Test; 52 import org.junit.runner.RunWith; 53 import org.mockito.Answers; 54 import org.mockito.Mock; 55 import org.mockito.MockitoAnnotations; 56 import org.robolectric.RobolectricTestRunner; 57 import org.robolectric.RuntimeEnvironment; 58 import org.robolectric.annotation.Config; 59 60 @RunWith(RobolectricTestRunner.class) 61 @Config(shadows = { 62 com.android.settings.testutils.shadow.ShadowFragment.class, 63 }) 64 public class LocationTimeZoneDetectionPreferenceControllerTest { 65 @Mock 66 private TimeManager mTimeManager; 67 private Context mContext; 68 private LocationTimeZoneDetectionPreferenceController mController; 69 @Mock(answer = Answers.RETURNS_DEEP_STUBS) 70 private InstrumentedPreferenceFragment mFragment; 71 72 @Before setUp()73 public void setUp() { 74 MockitoAnnotations.initMocks(this); 75 mContext = spy(RuntimeEnvironment.application); 76 when(mContext.getSystemService(TimeManager.class)).thenReturn(mTimeManager); 77 mController = new LocationTimeZoneDetectionPreferenceController(mContext); 78 mController.setFragment(mFragment); 79 } 80 81 @Test setChecked_withTrue_shouldUpdateSetting_whenLocationIsEnabled()82 public void setChecked_withTrue_shouldUpdateSetting_whenLocationIsEnabled() { 83 boolean useLocationEnabled = true; 84 TimeZoneCapabilitiesAndConfig capabilitiesAndConfig = 85 createTimeZoneCapabilitiesAndConfig(useLocationEnabled, CAPABILITY_POSSESSED); 86 when(mTimeManager.getTimeZoneCapabilitiesAndConfig()).thenReturn(capabilitiesAndConfig); 87 88 // Simulate the UI being clicked. 89 mController.setChecked(true); 90 91 // Verify the TimeManager was updated with the UI value. 92 TimeZoneConfiguration expectedConfiguration = new TimeZoneConfiguration.Builder() 93 .setGeoDetectionEnabled(true) 94 .build(); 95 verify(mTimeManager).updateTimeZoneConfiguration(expectedConfiguration); 96 } 97 98 @Test isNotSliceable()99 public void isNotSliceable() { 100 assertThat(mController.isSliceable()).isFalse(); 101 } 102 103 @Test setChecked_withTrue_shouldDoNothing_whenLocationIsDisabled()104 public void setChecked_withTrue_shouldDoNothing_whenLocationIsDisabled() { 105 boolean useLocationEnabled = false; 106 TimeZoneCapabilitiesAndConfig capabilitiesAndConfig = 107 createTimeZoneCapabilitiesAndConfig(useLocationEnabled, CAPABILITY_POSSESSED); 108 when(mTimeManager.getTimeZoneCapabilitiesAndConfig()).thenReturn(capabilitiesAndConfig); 109 110 // Simulate the UI being clicked. 111 mController.setChecked(true); 112 113 // Verify the TimeManager was not updated. 114 verify(mTimeManager, never()).updateTimeZoneConfiguration(any()); 115 } 116 117 @Test setChecked_withFalse_shouldUpdateSetting()118 public void setChecked_withFalse_shouldUpdateSetting() { 119 boolean useLocationEnabled = false; 120 TimeZoneCapabilitiesAndConfig capabilitiesAndConfig = 121 createTimeZoneCapabilitiesAndConfig(useLocationEnabled, CAPABILITY_POSSESSED); 122 when(mTimeManager.getTimeZoneCapabilitiesAndConfig()).thenReturn(capabilitiesAndConfig); 123 124 // Simulate the UI being clicked. 125 mController.setChecked(false); 126 127 // Verify the TimeManager was updated with the UI value. 128 TimeZoneConfiguration expectedConfiguration = new TimeZoneConfiguration.Builder() 129 .setGeoDetectionEnabled(false) 130 .build(); 131 verify(mTimeManager).updateTimeZoneConfiguration(expectedConfiguration); 132 } 133 134 @Test testLocationTimeZoneDetection_supported_shouldBeShown()135 public void testLocationTimeZoneDetection_supported_shouldBeShown() { 136 boolean useLocationEnabled = false; 137 TimeZoneCapabilitiesAndConfig capabilitiesAndConfig = 138 createTimeZoneCapabilitiesAndConfig(useLocationEnabled, CAPABILITY_POSSESSED); 139 when(mTimeManager.getTimeZoneCapabilitiesAndConfig()).thenReturn(capabilitiesAndConfig); 140 141 assertThat(mController.isAvailable()).isTrue(); 142 } 143 144 @Test testLocationTimeZoneDetection_unsupported_shouldNotBeShown()145 public void testLocationTimeZoneDetection_unsupported_shouldNotBeShown() { 146 boolean useLocationEnabled = false; 147 TimeZoneCapabilitiesAndConfig capabilitiesAndConfig = createTimeZoneCapabilitiesAndConfig( 148 useLocationEnabled, CAPABILITY_NOT_SUPPORTED); 149 when(mTimeManager.getTimeZoneCapabilitiesAndConfig()).thenReturn(capabilitiesAndConfig); 150 151 assertThat(mController.isAvailable()).isFalse(); 152 } 153 154 /** 155 * Tests that the summary is set in just one of many cases. Exhaustive testing would be brittle. 156 */ 157 @Test testLocationTimeZoneDetection_summary_geoDetectionEnabled()158 public void testLocationTimeZoneDetection_summary_geoDetectionEnabled() { 159 boolean useLocationEnabled = false; 160 TimeZoneCapabilitiesAndConfig capabilitiesAndConfig = 161 createTimeZoneCapabilitiesAndConfig(useLocationEnabled, CAPABILITY_POSSESSED); 162 163 when(mTimeManager.getTimeZoneCapabilitiesAndConfig()).thenReturn(capabilitiesAndConfig); 164 assertThat(mController.getSummary()).isEqualTo( 165 mContext.getString(R.string.location_time_zone_detection_auto_is_on)); 166 } 167 168 @Test testLocationTimeZoneDetection_toggleIsOn_whenGeoDetectionEnabledAnsMlsIsOff()169 public void testLocationTimeZoneDetection_toggleIsOn_whenGeoDetectionEnabledAnsMlsIsOff() { 170 boolean useLocationEnabled = false; 171 TimeZoneCapabilitiesAndConfig capabilitiesAndConfig = createTimeZoneCapabilitiesAndConfig( 172 useLocationEnabled, CAPABILITY_NOT_APPLICABLE); 173 174 when(mTimeManager.getTimeZoneCapabilitiesAndConfig()).thenReturn(capabilitiesAndConfig); 175 176 assertThat(mController.isChecked()).isTrue(); 177 assertThat(mController.getSummary()).isEqualTo( 178 mContext.getString(R.string.location_app_permission_summary_location_off)); 179 } 180 createTimeZoneCapabilitiesAndConfig( boolean useLocationEnabled, @CapabilityState int configureGeoDetectionEnabledCapability)181 private static TimeZoneCapabilitiesAndConfig createTimeZoneCapabilitiesAndConfig( 182 boolean useLocationEnabled, 183 @CapabilityState int configureGeoDetectionEnabledCapability) { 184 185 // Create a status that matches the user's capability state. 186 LocationTimeZoneAlgorithmStatus locationAlgorithmStatus; 187 switch (configureGeoDetectionEnabledCapability) { 188 case CAPABILITY_NOT_SUPPORTED: 189 locationAlgorithmStatus = new LocationTimeZoneAlgorithmStatus( 190 DETECTION_ALGORITHM_STATUS_NOT_SUPPORTED, 191 PROVIDER_STATUS_NOT_PRESENT, null, PROVIDER_STATUS_NOT_PRESENT, null); 192 break; 193 case CAPABILITY_NOT_APPLICABLE: 194 case CAPABILITY_POSSESSED: 195 locationAlgorithmStatus = new LocationTimeZoneAlgorithmStatus( 196 DETECTION_ALGORITHM_STATUS_RUNNING, 197 PROVIDER_STATUS_NOT_READY, null, PROVIDER_STATUS_NOT_READY, null); 198 break; 199 default: 200 throw new AssertionError( 201 "Unsupported capability state: " + configureGeoDetectionEnabledCapability); 202 } 203 TimeZoneDetectorStatus status = new TimeZoneDetectorStatus(DETECTOR_STATUS_RUNNING, 204 new TelephonyTimeZoneAlgorithmStatus(DETECTION_ALGORITHM_STATUS_RUNNING), 205 locationAlgorithmStatus); 206 207 UserHandle arbitraryUserHandle = UserHandle.of(123); 208 TimeZoneCapabilities capabilities = new TimeZoneCapabilities.Builder(arbitraryUserHandle) 209 .setConfigureAutoDetectionEnabledCapability(CAPABILITY_POSSESSED) 210 .setUseLocationEnabled(useLocationEnabled) 211 .setConfigureGeoDetectionEnabledCapability(configureGeoDetectionEnabledCapability) 212 .setSetManualTimeZoneCapability(CAPABILITY_NOT_APPLICABLE) 213 .build(); 214 215 TimeZoneConfiguration configuration = new TimeZoneConfiguration.Builder() 216 .setAutoDetectionEnabled(true) 217 .setGeoDetectionEnabled(true) 218 .build(); 219 220 return new TimeZoneCapabilitiesAndConfig(status, capabilities, configuration); 221 } 222 } 223