• 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 
17 package com.android.settings.display;
18 
19 import static android.app.admin.DevicePolicyResources.Strings.Settings.OTHER_OPTIONS_DISABLED_BY_ADMIN;
20 import static android.hardware.SensorPrivacyManager.Sensors.CAMERA;
21 import static android.provider.Settings.System.SCREEN_OFF_TIMEOUT;
22 
23 import android.app.admin.DevicePolicyManager;
24 import android.app.settings.SettingsEnums;
25 import android.content.BroadcastReceiver;
26 import android.content.Context;
27 import android.content.Intent;
28 import android.content.IntentFilter;
29 import android.content.res.Resources;
30 import android.graphics.drawable.Drawable;
31 import android.hardware.SensorPrivacyManager;
32 import android.os.PowerManager;
33 import android.os.UserHandle;
34 import android.provider.Settings;
35 import android.util.Log;
36 
37 import androidx.preference.PreferenceScreen;
38 
39 import com.android.settings.R;
40 import com.android.settings.overlay.FeatureFactory;
41 import com.android.settings.search.BaseSearchIndexProvider;
42 import com.android.settings.support.actionbar.HelpResourceProvider;
43 import com.android.settings.widget.RadioButtonPickerFragment;
44 import com.android.settingslib.RestrictedLockUtils;
45 import com.android.settingslib.RestrictedLockUtilsInternal;
46 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
47 import com.android.settingslib.search.SearchIndexable;
48 import com.android.settingslib.search.SearchIndexableRaw;
49 import com.android.settingslib.widget.CandidateInfo;
50 import com.android.settingslib.widget.FooterPreference;
51 import com.android.settingslib.widget.SelectorWithWidgetPreference;
52 
53 import com.google.common.annotations.VisibleForTesting;
54 
55 import java.util.ArrayList;
56 import java.util.List;
57 
58 /**
59  * Fragment that is used to control screen timeout.
60  */
61 @SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC)
62 public class ScreenTimeoutSettings extends RadioButtonPickerFragment implements
63         HelpResourceProvider {
64     private static final String TAG = "ScreenTimeout";
65     /** If there is no setting in the provider, use this. */
66     public static final int FALLBACK_SCREEN_TIMEOUT_VALUE = 30000;
67 
68     private static final int DEFAULT_ORDER_OF_LOWEST_PREFERENCE = Integer.MAX_VALUE - 1;
69 
70     private CharSequence[] mInitialEntries;
71     private CharSequence[] mInitialValues;
72     private FooterPreference mPrivacyPreference;
73     private final MetricsFeatureProvider mMetricsFeatureProvider;
74     private SensorPrivacyManager mPrivacyManager;
75     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
76         @Override
77         public void onReceive(Context context, Intent intent) {
78             mAdaptiveSleepBatterySaverPreferenceController.updateVisibility();
79             mAdaptiveSleepController.updatePreference();
80         }
81     };
82 
83     private DevicePolicyManager mDevicePolicyManager;
84 
85     @VisibleForTesting
86     Context mContext;
87 
88     @VisibleForTesting
89     RestrictedLockUtils.EnforcedAdmin mAdmin;
90 
91     @VisibleForTesting
92     FooterPreference mDisableOptionsPreference;
93 
94     @VisibleForTesting
95     AdaptiveSleepPermissionPreferenceController mAdaptiveSleepPermissionController;
96 
97     @VisibleForTesting
98     AdaptiveSleepCameraStatePreferenceController mAdaptiveSleepCameraStatePreferenceController;
99 
100     @VisibleForTesting
101     AdaptiveSleepPreferenceController mAdaptiveSleepController;
102 
103     @VisibleForTesting
104     AdaptiveSleepBatterySaverPreferenceController mAdaptiveSleepBatterySaverPreferenceController;
105 
ScreenTimeoutSettings()106     public ScreenTimeoutSettings() {
107         super();
108         mMetricsFeatureProvider = FeatureFactory.getFactory(getContext())
109                 .getMetricsFeatureProvider();
110     }
111 
112     @Override
onAttach(Context context)113     public void onAttach(Context context) {
114         super.onAttach(context);
115         mContext = context;
116         mDevicePolicyManager = mContext.getSystemService(DevicePolicyManager.class);
117         mInitialEntries = getResources().getStringArray(R.array.screen_timeout_entries);
118         mInitialValues = getResources().getStringArray(R.array.screen_timeout_values);
119         mAdaptiveSleepController = new AdaptiveSleepPreferenceController(context);
120         mAdaptiveSleepPermissionController = new AdaptiveSleepPermissionPreferenceController(
121                 context);
122         mAdaptiveSleepCameraStatePreferenceController =
123                 new AdaptiveSleepCameraStatePreferenceController(context);
124         mAdaptiveSleepBatterySaverPreferenceController =
125                 new AdaptiveSleepBatterySaverPreferenceController(context);
126         mPrivacyPreference = new FooterPreference(context);
127         mPrivacyPreference.setIcon(R.drawable.ic_privacy_shield_24dp);
128         mPrivacyPreference.setTitle(R.string.adaptive_sleep_privacy);
129         mPrivacyPreference.setSelectable(false);
130         mPrivacyPreference.setLayoutResource(R.layout.preference_footer);
131         mPrivacyManager = SensorPrivacyManager.getInstance(context);
132         mPrivacyManager.addSensorPrivacyListener(CAMERA,
133                 (sensor, enabled) -> mAdaptiveSleepController.updatePreference());
134     }
135 
136     @Override
getCandidates()137     protected List<? extends CandidateInfo> getCandidates() {
138         final List<CandidateInfo> candidates = new ArrayList<>();
139         final long maxTimeout = getMaxScreenTimeout(getContext());
140         if (mInitialValues != null) {
141             for (int i = 0; i < mInitialValues.length; ++i) {
142                 if (Long.parseLong(mInitialValues[i].toString()) <= maxTimeout) {
143                     candidates.add(new TimeoutCandidateInfo(mInitialEntries[i],
144                             mInitialValues[i].toString(), true));
145                 }
146             }
147         } else {
148             Log.e(TAG, "Screen timeout options do not exist.");
149         }
150         return candidates;
151     }
152 
153     @Override
onStart()154     public void onStart() {
155         super.onStart();
156         mAdaptiveSleepPermissionController.updateVisibility();
157         mAdaptiveSleepCameraStatePreferenceController.updateVisibility();
158         mAdaptiveSleepBatterySaverPreferenceController.updateVisibility();
159         mAdaptiveSleepController.updatePreference();
160         mContext.registerReceiver(mReceiver,
161                 new IntentFilter(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED));
162     }
163 
164     @Override
onStop()165     public void onStop() {
166         super.onStop();
167         mContext.unregisterReceiver(mReceiver);
168     }
169 
170     @Override
updateCandidates()171     public void updateCandidates() {
172         final String defaultKey = getDefaultKey();
173         final PreferenceScreen screen = getPreferenceScreen();
174         screen.removeAll();
175 
176         final List<? extends CandidateInfo> candidateList = getCandidates();
177         if (candidateList == null) {
178             return;
179         }
180 
181         for (CandidateInfo info : candidateList) {
182             SelectorWithWidgetPreference pref =
183                     new SelectorWithWidgetPreference(getPrefContext());
184             bindPreference(pref, info.getKey(), info, defaultKey);
185             screen.addPreference(pref);
186         }
187 
188         final long selectedTimeout = Long.parseLong(defaultKey);
189         final long maxTimeout = getMaxScreenTimeout(getContext());
190         if (!candidateList.isEmpty() && (selectedTimeout > maxTimeout)) {
191             // The selected time out value is longer than the max timeout allowed by the admin.
192             // Select the largest value from the list by default.
193             final SelectorWithWidgetPreference preferenceWithLargestTimeout =
194                     (SelectorWithWidgetPreference) screen.getPreference(candidateList.size() - 1);
195             preferenceWithLargestTimeout.setChecked(true);
196         }
197 
198         mPrivacyPreference = new FooterPreference(mContext);
199         mPrivacyPreference.setIcon(R.drawable.ic_privacy_shield_24dp);
200         mPrivacyPreference.setTitle(R.string.adaptive_sleep_privacy);
201         mPrivacyPreference.setSelectable(false);
202         mPrivacyPreference.setLayoutResource(R.layout.preference_footer);
203 
204         if (isScreenAttentionAvailable(getContext())) {
205             mAdaptiveSleepPermissionController.addToScreen(screen);
206             mAdaptiveSleepCameraStatePreferenceController.addToScreen(screen);
207             mAdaptiveSleepController.addToScreen(screen);
208             mAdaptiveSleepBatterySaverPreferenceController.addToScreen(screen);
209             screen.addPreference(mPrivacyPreference);
210         }
211 
212         if (mAdmin != null) {
213             setupDisabledFooterPreference();
214             screen.addPreference(mDisableOptionsPreference);
215         }
216     }
217 
218     @VisibleForTesting
setupDisabledFooterPreference()219     void setupDisabledFooterPreference() {
220         final String textDisabledByAdmin = mDevicePolicyManager.getResources().getString(
221                 OTHER_OPTIONS_DISABLED_BY_ADMIN, () -> getResources().getString(
222                         R.string.admin_disabled_other_options));
223         final String textMoreDetails = getResources().getString(R.string.admin_more_details);
224 
225         mDisableOptionsPreference = new FooterPreference(getContext());
226         mDisableOptionsPreference.setTitle(textDisabledByAdmin);
227         mDisableOptionsPreference.setSelectable(false);
228         mDisableOptionsPreference.setLearnMoreText(textMoreDetails);
229         mDisableOptionsPreference.setLearnMoreAction(v -> {
230             RestrictedLockUtils.sendShowAdminSupportDetailsIntent(getContext(), mAdmin);
231         });
232         mDisableOptionsPreference.setIcon(R.drawable.ic_info_outline_24dp);
233 
234         // The 'disabled by admin' preference should always be at the end of the setting page.
235         mDisableOptionsPreference.setOrder(DEFAULT_ORDER_OF_LOWEST_PREFERENCE);
236         mPrivacyPreference.setOrder(DEFAULT_ORDER_OF_LOWEST_PREFERENCE - 1);
237     }
238 
239     @Override
getDefaultKey()240     protected String getDefaultKey() {
241         return getCurrentSystemScreenTimeout(getContext());
242     }
243 
244     @Override
setDefaultKey(String key)245     protected boolean setDefaultKey(String key) {
246         setCurrentSystemScreenTimeout(getContext(), key);
247         return true;
248     }
249 
250     @Override
getMetricsCategory()251     public int getMetricsCategory() {
252         return SettingsEnums.SCREEN_TIMEOUT;
253     }
254 
255     @Override
getPreferenceScreenResId()256     protected int getPreferenceScreenResId() {
257         return R.xml.screen_timeout_settings;
258     }
259 
260     @Override
getHelpResource()261     public int getHelpResource() {
262         return R.string.help_url_adaptive_sleep;
263     }
264 
getMaxScreenTimeout(Context context)265     private Long getMaxScreenTimeout(Context context) {
266         if (context == null) {
267             return Long.MAX_VALUE;
268         }
269         final DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class);
270         if (dpm == null) {
271             return Long.MAX_VALUE;
272         }
273         mAdmin = RestrictedLockUtilsInternal.checkIfMaximumTimeToLockIsSet(context);
274         if (mAdmin != null) {
275             return dpm.getMaximumTimeToLock(null /* admin */, UserHandle.myUserId());
276         }
277         return Long.MAX_VALUE;
278     }
279 
getCurrentSystemScreenTimeout(Context context)280     private String getCurrentSystemScreenTimeout(Context context) {
281         if (context == null) {
282             return Long.toString(FALLBACK_SCREEN_TIMEOUT_VALUE);
283         } else {
284             return Long.toString(Settings.System.getLong(context.getContentResolver(),
285                     SCREEN_OFF_TIMEOUT, FALLBACK_SCREEN_TIMEOUT_VALUE));
286         }
287     }
288 
setCurrentSystemScreenTimeout(Context context, String key)289     private void setCurrentSystemScreenTimeout(Context context, String key) {
290         try {
291             if (context != null) {
292                 final long value = Long.parseLong(key);
293                 mMetricsFeatureProvider.action(context, SettingsEnums.ACTION_SCREEN_TIMEOUT_CHANGED,
294                         (int) value);
295                 Settings.System.putLong(context.getContentResolver(), SCREEN_OFF_TIMEOUT, value);
296             }
297         } catch (NumberFormatException e) {
298             Log.e(TAG, "could not persist screen timeout setting", e);
299         }
300     }
301 
isScreenAttentionAvailable(Context context)302     private static boolean isScreenAttentionAvailable(Context context) {
303         return AdaptiveSleepPreferenceController.isAdaptiveSleepSupported(context);
304     }
305 
306     private static class TimeoutCandidateInfo extends CandidateInfo {
307         private final CharSequence mLabel;
308         private final String mKey;
309 
TimeoutCandidateInfo(CharSequence label, String key, boolean enabled)310         TimeoutCandidateInfo(CharSequence label, String key, boolean enabled) {
311             super(enabled);
312             mLabel = label;
313             mKey = key;
314         }
315 
316         @Override
loadLabel()317         public CharSequence loadLabel() {
318             return mLabel;
319         }
320 
321         @Override
loadIcon()322         public Drawable loadIcon() {
323             return null;
324         }
325 
326         @Override
getKey()327         public String getKey() {
328             return mKey;
329         }
330     }
331 
332     public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
333             new BaseSearchIndexProvider(R.xml.screen_timeout_settings) {
334                 public List<SearchIndexableRaw> getRawDataToIndex(Context context,
335                         boolean enabled) {
336                     if (!isScreenAttentionAvailable(context)) {
337                         return null;
338                     }
339                     final Resources res = context.getResources();
340                     final SearchIndexableRaw data = new SearchIndexableRaw(context);
341                     data.title = res.getString(R.string.adaptive_sleep_title);
342                     data.key = AdaptiveSleepPreferenceController.PREFERENCE_KEY;
343                     data.keywords = res.getString(R.string.adaptive_sleep_title);
344 
345                     final List<SearchIndexableRaw> result = new ArrayList<>(1);
346                     result.add(data);
347                     return result;
348                 }
349             };
350 }
351