1 /*
2  * Copyright (C) 2019 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.tv.settings.device.display.daydream;
18 
19 import static android.provider.Settings.Secure.ATTENTIVE_TIMEOUT;
20 import static android.provider.Settings.Secure.SLEEP_TIMEOUT;
21 
22 import static com.android.tv.settings.util.InstrumentationUtils.logEntrySelected;
23 
24 import android.app.AlertDialog;
25 import android.app.tvsettings.TvSettingsEnums;
26 import android.os.Bundle;
27 import android.os.UserManager;
28 import android.provider.Settings;
29 import android.text.format.DateUtils;
30 
31 import androidx.annotation.Keep;
32 import androidx.preference.ListPreference;
33 import androidx.preference.Preference;
34 
35 import com.android.settingslib.RestrictedSwitchPreference;
36 import com.android.tv.settings.R;
37 import com.android.tv.settings.RestrictedPreferenceAdapter;
38 import com.android.tv.settings.SettingsPreferenceFragment;
39 import com.android.tv.twopanelsettings.TwoPanelSettingsFragment;
40 
41 /**
42  * The energy saver screen in TV settings.
43  */
44 @Keep
45 public class EnergySaverFragment extends SettingsPreferenceFragment implements
46         Preference.OnPreferenceChangeListener {
47     private static final String TAG = "EnergySaverFragment";
48     private static final String KEY_SLEEP_TIME = "sleepTime";
49     private static final String KEY_ALLOW_TURN_SCREEN_OFF = "allowTurnScreenOff";
50     private static final int DEFAULT_SLEEP_TIME_MS = (int) (24 * DateUtils.HOUR_IN_MILLIS);
51     private static final int WARNING_THRESHOLD_SLEEP_TIME_MS = (int) (4 * DateUtils.HOUR_IN_MILLIS);
52     private RestrictedSwitchPreference mAllowTurnScreenOffWithWakeLockPref;
53     private ListPreference mSleepTimePref;
54     private RestrictedPreferenceAdapter<ListPreference> mRestrictedSleepTime;
55 
56     @Override
onCreatePreferences(Bundle bundle, String s)57     public void onCreatePreferences(Bundle bundle, String s) {
58         setPreferencesFromResource(R.xml.energy_saver, null);
59         mAllowTurnScreenOffWithWakeLockPref = findPreference(KEY_ALLOW_TURN_SCREEN_OFF);
60         mAllowTurnScreenOffWithWakeLockPref.setOnPreferenceChangeListener(this);
61         mAllowTurnScreenOffWithWakeLockPref.setVisible(showStandbyTimeout());
62         UserManager userManager = UserManager.get(getContext());
63         if (userManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_SCREEN_TIMEOUT)
64                 && !mAllowTurnScreenOffWithWakeLockPref.isDisabledByAdmin()) {
65             mAllowTurnScreenOffWithWakeLockPref.setEnabled(false);
66         }
67 
68         updateAllowTurnScreenOffWithWakeLockPref();
69         mSleepTimePref = findPreference(KEY_SLEEP_TIME);
70         if (allowTurnOffWithWakeLock()) {
71             int validatedAttentiveSleepTime = getValidatedTimeout(getAttentiveSleepTime());
72             mSleepTimePref.setValue(String.valueOf(validatedAttentiveSleepTime));
73             if (getAttentiveSleepTime() != validatedAttentiveSleepTime) {
74                 setAttentiveSleepTime(validatedAttentiveSleepTime);
75             }
76         } else {
77             int validatedSleepTime = getValidatedTimeout(getSleepTime());
78             mSleepTimePref.setValue(String.valueOf(validatedSleepTime));
79             if (getSleepTime() != validatedSleepTime) {
80                 setSleepTime(validatedSleepTime);
81             }
82         }
83         mSleepTimePref.setOnPreferenceChangeListener(this);
84         mSleepTimePref.setOnPreferenceClickListener(
85                 preference -> {
86                     logEntrySelected(TvSettingsEnums.SYSTEM_ENERGYSAVER_START_DELAY);
87                     return false;
88                 });
89 
90         mRestrictedSleepTime = RestrictedPreferenceAdapter.adapt(
91                 mSleepTimePref, UserManager.DISALLOW_CONFIG_SCREEN_TIMEOUT);
92     }
93 
showStandbyTimeout()94     private boolean showStandbyTimeout() {
95         return getResources().getBoolean(R.bool.config_show_standby_timeout);
96     }
97 
allowTurnOffWithWakeLock()98     private boolean allowTurnOffWithWakeLock() {
99         return showStandbyTimeout() && mAllowTurnScreenOffWithWakeLockPref.isChecked();
100     }
101 
updateAllowTurnScreenOffWithWakeLockPref()102     private void updateAllowTurnScreenOffWithWakeLockPref() {
103         if (!mAllowTurnScreenOffWithWakeLockPref.isVisible()) {
104             return;
105         }
106 
107         UserManager userManager = UserManager.get(getContext());
108         boolean canChangeEnabled = !userManager
109                 .hasUserRestriction(UserManager.DISALLOW_CONFIG_SCREEN_TIMEOUT);
110 
111         if (getSleepTime() == -1) {
112             mAllowTurnScreenOffWithWakeLockPref.setChecked(false);
113             if (canChangeEnabled) {
114                 mAllowTurnScreenOffWithWakeLockPref.setEnabled(false);
115             }
116         } else if (getAttentiveSleepTime() == -1) {
117             mAllowTurnScreenOffWithWakeLockPref.setChecked(false);
118             if (canChangeEnabled) {
119                 mAllowTurnScreenOffWithWakeLockPref.setEnabled(true);
120             }
121         } else {
122             mAllowTurnScreenOffWithWakeLockPref.setChecked(true);
123             if (canChangeEnabled) {
124                 mAllowTurnScreenOffWithWakeLockPref.setEnabled(true);
125             }
126         }
127     }
128 
129     @Override
onPreferenceChange(Preference preference, Object newValue)130     public boolean onPreferenceChange(Preference preference, Object newValue) {
131         switch (preference.getKey()) {
132             case KEY_SLEEP_TIME:
133                 final int newSleepTime = Integer.parseInt((String) newValue);
134                 if (getSleepTimeEntryId(newSleepTime) != -1) {
135                     logEntrySelected(getSleepTimeEntryId(newSleepTime));
136                 }
137                 if (showStandbyTimeout()
138                         && (newSleepTime > WARNING_THRESHOLD_SLEEP_TIME_MS || newSleepTime == -1)) {
139                     // Some regions require a warning to be presented.
140                     new AlertDialog.Builder(getContext())
141                             .setTitle(R.string.device_energy_saver_confirmation_title)
142                             .setMessage(getConfirmationDialogDescription(newSleepTime))
143                             .setPositiveButton(R.string.settings_confirm,
144                                     (dialog, which) -> confirmNewSleepTime(newSleepTime))
145                             .setNegativeButton(R.string.settings_cancel,
146                                     (dialog, which) -> dialog.dismiss())
147                             .create()
148                             .show();
149                     return false;
150                 } else {
151                     updateTimeOut(allowTurnOffWithWakeLock(), newSleepTime);
152                     return true;
153                 }
154             case KEY_ALLOW_TURN_SCREEN_OFF:
155                 updateTimeOut((boolean) newValue, Integer.parseInt(mSleepTimePref.getValue()));
156                 return true;
157             default:
158                 return false;
159         }
160     }
161 
updateTimeOut(boolean allowTurnScreenOffWithWakeLock, int value)162     private void updateTimeOut(boolean allowTurnScreenOffWithWakeLock, int value) {
163         if (allowTurnScreenOffWithWakeLock) {
164             setSleepTime(value);
165             if (showStandbyTimeout()) {
166                 setAttentiveSleepTime(value);
167             }
168         } else {
169             setSleepTime(value);
170             if (showStandbyTimeout()) {
171                 setAttentiveSleepTime(-1);
172             }
173         }
174         updateAllowTurnScreenOffWithWakeLockPref();
175     }
176 
getSleepTime()177     private int getSleepTime() {
178         return Settings.Secure.getInt(getActivity().getContentResolver(), SLEEP_TIMEOUT,
179                 DEFAULT_SLEEP_TIME_MS);
180     }
181 
getAttentiveSleepTime()182     private int getAttentiveSleepTime() {
183         return Settings.Secure.getInt(getActivity().getContentResolver(), ATTENTIVE_TIMEOUT,
184                 DEFAULT_SLEEP_TIME_MS);
185     }
186 
setSleepTime(int ms)187     private void setSleepTime(int ms) {
188         Settings.Secure.putInt(getActivity().getContentResolver(), SLEEP_TIMEOUT, ms);
189     }
190 
setAttentiveSleepTime(int ms)191     private void setAttentiveSleepTime(int ms) {
192         Settings.Secure.putInt(getActivity().getContentResolver(), ATTENTIVE_TIMEOUT, ms);
193     }
194 
195     // The SLEEP_TIMEOUT and ATTENTIVE_TIMEOUT could be defined in overlay by OEMs. We validate the
196     // value to make sure that we select from the predefined options. If the value from overlay is
197     // not one of the predefined options, we round it to the closest predefined value, except -1.
getValidatedTimeout(int purposedTimeout)198     private int getValidatedTimeout(int purposedTimeout) {
199         int validatedTimeout = DEFAULT_SLEEP_TIME_MS;
200         if (purposedTimeout < 0) {
201             return -1;
202         }
203         String[] optionsString = getResources().getStringArray(R.array.screen_off_timeout_values);
204         // Find the value from the predefined values that is closest to the proposed value except -1
205         int diff = Integer.MAX_VALUE;
206         for (String option : optionsString) {
207             if (Integer.parseInt(option) != -1) {
208                 int currentDiff = Math.abs(purposedTimeout - Integer.parseInt(option));
209                 if (currentDiff < diff) {
210                     diff = currentDiff;
211                     validatedTimeout = Integer.parseInt(option);
212                 }
213             }
214         }
215         return validatedTimeout;
216     }
217 
getConfirmationDialogDescription(int newSleepTime)218     private String getConfirmationDialogDescription(int newSleepTime) {
219         String sleepTimeText = null;
220         String[] optionsValues = getResources().getStringArray(R.array.screen_off_timeout_values);
221         String[] optionsStrings = getResources().getStringArray(R.array.screen_off_timeout_entries);
222         for (int i = 0; i < optionsValues.length; i++) {
223             if (newSleepTime == Integer.parseInt(optionsValues[i])) {
224                 sleepTimeText = optionsStrings[i];
225             }
226         }
227         return getString(R.string.device_energy_saver_confirmation_text, sleepTimeText);
228     }
229 
confirmNewSleepTime(int newSleepTime)230     private void confirmNewSleepTime(int newSleepTime) {
231         if (mSleepTimePref != null) {
232             updateTimeOut(allowTurnOffWithWakeLock(), newSleepTime);
233             mSleepTimePref.setValue(String.valueOf(newSleepTime));
234             mRestrictedSleepTime.updatePreference();
235             if (getCallbackFragment() instanceof TwoPanelSettingsFragment) {
236                 ((TwoPanelSettingsFragment) getCallbackFragment()).refocusPreference(this);
237             }
238         }
239     }
240 
241     // TODO(b/158783050): update logging for new options 4H, 8H, 24H.
242     // Map @array/screen_off_timeout_entries to defined log enum
getSleepTimeEntryId(int sleepTimeValue)243     private int getSleepTimeEntryId(int sleepTimeValue) {
244         switch(sleepTimeValue) {
245             case -1:
246                 return TvSettingsEnums.SYSTEM_ENERGYSAVER_START_DELAY_NEVER;
247             case 900000:
248                 return TvSettingsEnums.SYSTEM_ENERGYSAVER_START_DELAY_15M;
249             case 1800000:
250                 return TvSettingsEnums.SYSTEM_ENERGYSAVER_START_DELAY_30M;
251             case 3600000:
252                 return TvSettingsEnums.SYSTEM_ENERGYSAVER_START_DELAY_1H;
253             case 43200000:
254                 return TvSettingsEnums.SYSTEM_ENERGYSAVER_START_DELAY_12H;
255             default:
256                 return -1;
257         }
258     }
259 
260     @Override
getPageId()261     protected int getPageId() {
262         return TvSettingsEnums.SYSTEM_ENERGYSAVER;
263     }
264 }
265