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