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 package com.android.settings.datetime; 17 18 import static android.app.time.Capabilities.CAPABILITY_NOT_ALLOWED; 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 23 import android.app.time.TimeManager; 24 import android.app.time.TimeZoneCapabilities; 25 import android.app.time.TimeZoneCapabilitiesAndConfig; 26 import android.app.time.TimeZoneConfiguration; 27 import android.content.Context; 28 29 import androidx.preference.Preference; 30 import androidx.preference.PreferenceScreen; 31 32 import com.android.settings.R; 33 import com.android.settings.core.InstrumentedPreferenceFragment; 34 import com.android.settings.core.TogglePreferenceController; 35 import com.android.settingslib.core.lifecycle.LifecycleObserver; 36 import com.android.settingslib.core.lifecycle.events.OnStart; 37 import com.android.settingslib.core.lifecycle.events.OnStop; 38 39 import java.util.concurrent.Executor; 40 41 /** 42 * The controller for the "location time zone detection" entry in the Location settings 43 * screen. 44 */ 45 public class LocationTimeZoneDetectionPreferenceController 46 extends TogglePreferenceController 47 implements LifecycleObserver, OnStart, OnStop, TimeManager.TimeZoneDetectorListener { 48 49 private static final String TAG = "location_time_zone_detection"; 50 51 private final TimeManager mTimeManager; 52 private TimeZoneCapabilitiesAndConfig mTimeZoneCapabilitiesAndConfig; 53 private InstrumentedPreferenceFragment mFragment; 54 private Preference mPreference; 55 LocationTimeZoneDetectionPreferenceController(Context context)56 public LocationTimeZoneDetectionPreferenceController(Context context) { 57 super(context, TAG); 58 mTimeManager = context.getSystemService(TimeManager.class); 59 } 60 setFragment(InstrumentedPreferenceFragment fragment)61 void setFragment(InstrumentedPreferenceFragment fragment) { 62 mFragment = fragment; 63 } 64 65 @Override isChecked()66 public boolean isChecked() { 67 TimeZoneCapabilitiesAndConfig capabilitiesAndConfig = 68 getTimeZoneCapabilitiesAndConfig(/*forceRefresh=*/false); 69 TimeZoneConfiguration configuration = capabilitiesAndConfig.getConfiguration(); 70 return configuration.isGeoDetectionEnabled(); 71 } 72 73 @Override setChecked(boolean isChecked)74 public boolean setChecked(boolean isChecked) { 75 TimeZoneCapabilitiesAndConfig timeZoneCapabilitiesAndConfig = 76 getTimeZoneCapabilitiesAndConfig(/*forceRefresh=*/false); 77 boolean isLocationEnabled = 78 timeZoneCapabilitiesAndConfig.getCapabilities().isUseLocationEnabled(); 79 if (isChecked && !isLocationEnabled) { 80 new LocationToggleDisabledDialogFragment() 81 .show(mFragment.getFragmentManager(), TAG); 82 // Toggle status is not updated. 83 return false; 84 } else { 85 TimeZoneConfiguration configuration = new TimeZoneConfiguration.Builder() 86 .setGeoDetectionEnabled(isChecked) 87 .build(); 88 return mTimeManager.updateTimeZoneConfiguration(configuration); 89 } 90 } 91 92 @Override displayPreference(PreferenceScreen screen)93 public void displayPreference(PreferenceScreen screen) { 94 super.displayPreference(screen); 95 mPreference = screen.findPreference(getPreferenceKey()); 96 } 97 98 @Override onStart()99 public void onStart() { 100 // Register for updates to the user's time zone capabilities or configuration which could 101 // require UI changes. 102 Executor mainExecutor = mContext.getMainExecutor(); 103 mTimeManager.addTimeZoneDetectorListener(mainExecutor, this); 104 // Setup the initial state of the summary. 105 refreshUi(); 106 } 107 108 @Override onStop()109 public void onStop() { 110 mTimeManager.removeTimeZoneDetectorListener(this); 111 } 112 113 @Override isSliceable()114 public boolean isSliceable() { 115 // Prevent use in a slice, which would enable search to display a toggle in the search 116 // results: LocationToggleDisabledDialogFragment has to be shown under some circumstances 117 // which doesn't work when embedded in search. b/185906072 118 return false; 119 } 120 121 @Override getSliceHighlightMenuRes()122 public int getSliceHighlightMenuRes() { 123 // not needed since it's not sliceable 124 return NO_RES; 125 } 126 127 @Override getAvailabilityStatus()128 public int getAvailabilityStatus() { 129 TimeZoneCapabilities timeZoneCapabilities = 130 getTimeZoneCapabilitiesAndConfig(/* forceRefresh= */ false).getCapabilities(); 131 int capability = timeZoneCapabilities.getConfigureGeoDetectionEnabledCapability(); 132 133 // The preference only has two states: present and not present. The preference is never 134 // present but disabled. 135 if (capability == CAPABILITY_NOT_SUPPORTED || capability == CAPABILITY_NOT_ALLOWED) { 136 return UNSUPPORTED_ON_DEVICE; 137 } else if (capability == CAPABILITY_NOT_APPLICABLE || capability == CAPABILITY_POSSESSED) { 138 return AVAILABLE; 139 } else { 140 throw new IllegalStateException("Unknown capability=" + capability); 141 } 142 } 143 144 @Override getSummary()145 public CharSequence getSummary() { 146 TimeZoneCapabilitiesAndConfig timeZoneCapabilitiesAndConfig = 147 getTimeZoneCapabilitiesAndConfig(/* forceRefresh= */ false); 148 TimeZoneCapabilities capabilities = timeZoneCapabilitiesAndConfig.getCapabilities(); 149 int configureGeoDetectionEnabledCapability = 150 capabilities.getConfigureGeoDetectionEnabledCapability(); 151 TimeZoneConfiguration configuration = timeZoneCapabilitiesAndConfig.getConfiguration(); 152 153 int summaryResId; 154 if (configureGeoDetectionEnabledCapability == CAPABILITY_NOT_SUPPORTED) { 155 // The preference should not be visible, but text is referenced in case this changes. 156 summaryResId = R.string.location_time_zone_detection_not_supported; 157 } else if (configureGeoDetectionEnabledCapability == CAPABILITY_NOT_ALLOWED) { 158 // The preference should not be visible, but text is referenced in case this changes. 159 summaryResId = R.string.location_time_zone_detection_not_allowed; 160 } else if (configureGeoDetectionEnabledCapability == CAPABILITY_NOT_APPLICABLE) { 161 boolean isLocationEnabled = 162 timeZoneCapabilitiesAndConfig.getCapabilities().isUseLocationEnabled(); 163 // The TimeZoneCapabilities cannot provide implementation-specific information about why 164 // the user doesn't have the capability, but the user's "location enabled" being off and 165 // the global automatic detection setting will always be considered overriding reasons 166 // why location time zone detection cannot be used. 167 if (!isLocationEnabled) { 168 summaryResId = R.string.location_app_permission_summary_location_off; 169 } else if (!configuration.isAutoDetectionEnabled()) { 170 summaryResId = R.string.location_time_zone_detection_auto_is_off; 171 } else { 172 // This is in case there are other reasons in future why location time zone 173 // detection is not applicable. 174 summaryResId = R.string.location_time_zone_detection_not_applicable; 175 } 176 } else if (configureGeoDetectionEnabledCapability == CAPABILITY_POSSESSED) { 177 // If capability is possessed, toggle status already tells all the information needed. 178 // Returning null will make previous text stick on toggling. 179 // See AbstractPreferenceController#refreshSummary. 180 summaryResId = R.string.location_time_zone_detection_auto_is_on; 181 } else { 182 // This is unexpected: getAvailabilityStatus() should ensure that the UI element isn't 183 // even shown for known cases, or the capability is unknown. 184 throw new IllegalStateException("Unexpected configureGeoDetectionEnabledCapability=" 185 + configureGeoDetectionEnabledCapability); 186 } 187 return mContext.getString(summaryResId); 188 } 189 190 /** 191 * Implementation of {@link TimeManager.TimeZoneDetectorListener#onChange()}. Called by the 192 * system server after a change that affects {@link TimeZoneCapabilitiesAndConfig}. 193 */ 194 @Override onChange()195 public void onChange() { 196 refreshUi(); 197 } 198 refreshUi()199 private void refreshUi() { 200 // Force a refresh of cached user capabilities and config before refreshing the summary. 201 getTimeZoneCapabilitiesAndConfig(/* forceRefresh= */ true); 202 refreshSummary(mPreference); 203 } 204 205 /** 206 * Returns the current user capabilities and configuration. {@code forceRefresh} can be {@code 207 * true} to discard any cached copy. 208 */ getTimeZoneCapabilitiesAndConfig(boolean forceRefresh)209 private TimeZoneCapabilitiesAndConfig getTimeZoneCapabilitiesAndConfig(boolean forceRefresh) { 210 if (forceRefresh || mTimeZoneCapabilitiesAndConfig == null) { 211 mTimeZoneCapabilitiesAndConfig = mTimeManager.getTimeZoneCapabilitiesAndConfig(); 212 } 213 return mTimeZoneCapabilitiesAndConfig; 214 } 215 } 216