blob: a50ce4c3c2277ddf19579531ea95d226a59a190a [file] [log] [blame]
Neil Fuller4a6663c2020-09-10 15:06:39 +01001/*
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 */
Neil Fuller21840432020-11-09 20:44:27 +000016package com.android.settings.datetime;
Neil Fuller4a6663c2020-09-10 15:06:39 +010017
Almaz Mingaleeva29a6ce2021-03-11 15:26:31 +000018import static android.app.time.Capabilities.CAPABILITY_NOT_ALLOWED;
19import static android.app.time.Capabilities.CAPABILITY_NOT_APPLICABLE;
20import static android.app.time.Capabilities.CAPABILITY_NOT_SUPPORTED;
21import static android.app.time.Capabilities.CAPABILITY_POSSESSED;
Neil Fuller4a6663c2020-09-10 15:06:39 +010022
23import android.app.time.TimeManager;
24import android.app.time.TimeZoneCapabilities;
25import android.app.time.TimeZoneCapabilitiesAndConfig;
26import android.app.time.TimeZoneConfiguration;
27import android.content.Context;
Neil Fuller4a6663c2020-09-10 15:06:39 +010028
29import androidx.preference.Preference;
30import androidx.preference.PreferenceScreen;
31
32import com.android.settings.R;
Almaz Mingaleevfcf1fcf2021-03-18 10:30:04 +000033import com.android.settings.core.InstrumentedPreferenceFragment;
Almaz Mingaleevab059182021-03-01 12:24:54 +000034import com.android.settings.core.TogglePreferenceController;
Neil Fuller4a6663c2020-09-10 15:06:39 +010035import com.android.settingslib.core.lifecycle.LifecycleObserver;
36import com.android.settingslib.core.lifecycle.events.OnStart;
37import com.android.settingslib.core.lifecycle.events.OnStop;
38
39import java.util.concurrent.Executor;
40
41/**
42 * The controller for the "location time zone detection" entry in the Location settings
43 * screen.
44 */
45public class LocationTimeZoneDetectionPreferenceController
Almaz Mingaleevab059182021-03-01 12:24:54 +000046 extends TogglePreferenceController
Neil Fuller4a6663c2020-09-10 15:06:39 +010047 implements LifecycleObserver, OnStart, OnStop, TimeManager.TimeZoneDetectorListener {
48
Almaz Mingaleevfcf1fcf2021-03-18 10:30:04 +000049 private static final String TAG = "location_time_zone_detection";
50
Neil Fuller4a6663c2020-09-10 15:06:39 +010051 private final TimeManager mTimeManager;
Neil Fuller4a6663c2020-09-10 15:06:39 +010052 private TimeZoneCapabilitiesAndConfig mTimeZoneCapabilitiesAndConfig;
Almaz Mingaleevfcf1fcf2021-03-18 10:30:04 +000053 private InstrumentedPreferenceFragment mFragment;
Neil Fuller4a6663c2020-09-10 15:06:39 +010054 private Preference mPreference;
55
Almaz Mingaleevfcf1fcf2021-03-18 10:30:04 +000056 public LocationTimeZoneDetectionPreferenceController(Context context) {
57 super(context, TAG);
Neil Fuller4a6663c2020-09-10 15:06:39 +010058 mTimeManager = context.getSystemService(TimeManager.class);
Neil Fuller4a6663c2020-09-10 15:06:39 +010059 }
60
Almaz Mingaleevfcf1fcf2021-03-18 10:30:04 +000061 void setFragment(InstrumentedPreferenceFragment fragment) {
62 mFragment = fragment;
63 }
64
Neil Fuller4a6663c2020-09-10 15:06:39 +010065 @Override
Almaz Mingaleevab059182021-03-01 12:24:54 +000066 public boolean isChecked() {
67 TimeZoneCapabilitiesAndConfig capabilitiesAndConfig =
Neil Fuller85327e02023-01-17 17:36:59 +000068 getTimeZoneCapabilitiesAndConfig(/*forceRefresh=*/false);
Almaz Mingaleevab059182021-03-01 12:24:54 +000069 TimeZoneConfiguration configuration = capabilitiesAndConfig.getConfiguration();
70 return configuration.isGeoDetectionEnabled();
71 }
72
73 @Override
74 public boolean setChecked(boolean isChecked) {
Neil Fuller85327e02023-01-17 17:36:59 +000075 TimeZoneCapabilitiesAndConfig timeZoneCapabilitiesAndConfig =
76 getTimeZoneCapabilitiesAndConfig(/*forceRefresh=*/false);
77 boolean isLocationEnabled =
78 timeZoneCapabilitiesAndConfig.getCapabilities().isUseLocationEnabled();
79 if (isChecked && !isLocationEnabled) {
Neil Fuller7ddbef72023-03-10 13:44:47 +000080 new LocationToggleDisabledDialogFragment()
Almaz Mingaleevfcf1fcf2021-03-18 10:30:04 +000081 .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 }
Almaz Mingaleevab059182021-03-01 12:24:54 +000090 }
91
92 @Override
Neil Fuller4a6663c2020-09-10 15:06:39 +010093 public void displayPreference(PreferenceScreen screen) {
94 super.displayPreference(screen);
95 mPreference = screen.findPreference(getPreferenceKey());
96 }
97
98 @Override
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
109 public void onStop() {
110 mTimeManager.removeTimeZoneDetectorListener(this);
111 }
112
113 @Override
Almaz Mingaleev6be940f2021-04-23 08:53:18 +0000114 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
Jason Chiu2989c502021-11-01 00:38:49 +0800122 public int getSliceHighlightMenuRes() {
123 // not needed since it's not sliceable
Jason Chiu9fc0f182021-11-03 12:18:39 +0800124 return NO_RES;
Jason Chiu2989c502021-11-01 00:38:49 +0800125 }
126
127 @Override
Neil Fuller4a6663c2020-09-10 15:06:39 +0100128 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
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) {
Neil Fuller85327e02023-01-17 17:36:59 +0000161 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) {
Neil Fuller4a6663c2020-09-10 15:06:39 +0100168 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) {
Almaz Mingaleevab059182021-03-01 12:24:54 +0000177 // 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.
Sunny Shaod46dff82022-06-16 16:10:02 +0800180 summaryResId = R.string.location_time_zone_detection_auto_is_on;
Neil Fuller4a6663c2020-09-10 15:06:39 +0100181 } 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
Neil Fuller85327e02023-01-17 17:36:59 +0000190 /**
191 * Implementation of {@link TimeManager.TimeZoneDetectorListener#onChange()}. Called by the
192 * system server after a change that affects {@link TimeZoneCapabilitiesAndConfig}.
193 */
Neil Fuller4a6663c2020-09-10 15:06:39 +0100194 @Override
195 public void onChange() {
196 refreshUi();
197 }
198
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 */
209 private TimeZoneCapabilitiesAndConfig getTimeZoneCapabilitiesAndConfig(boolean forceRefresh) {
210 if (forceRefresh || mTimeZoneCapabilitiesAndConfig == null) {
211 mTimeZoneCapabilitiesAndConfig = mTimeManager.getTimeZoneCapabilitiesAndConfig();
212 }
213 return mTimeZoneCapabilitiesAndConfig;
214 }
215}