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.content.Context; 27 import android.content.SharedPreferences; 28 import android.os.Bundle; 29 import android.os.UserManager; 30 import android.provider.Settings; 31 import android.text.format.DateUtils; 32 import android.util.Log; 33 34 import androidx.annotation.Keep; 35 import androidx.preference.ListPreference; 36 import androidx.preference.Preference; 37 38 import com.android.tv.settings.R; 39 import com.android.tv.settings.RestrictedPreferenceAdapter; 40 import com.android.tv.settings.SettingsPreferenceFragment; 41 import com.android.tv.twopanelsettings.TwoPanelSettingsFragment; 42 43 /** 44 * The energy saver screen in TV settings. 45 */ 46 @Keep 47 public class EnergySaverFragment extends SettingsPreferenceFragment implements 48 Preference.OnPreferenceChangeListener { 49 private static final String TAG = "EnergySaverFragment"; 50 private static final String KEY_SLEEP_TIME = "sleepTime"; 51 private static final String KEY_ATTENTIVE_TIME = "attentiveTime"; 52 53 private static final String SHARED_PREFS_NAME = "energy_saver"; 54 private static final String PREF_RESET_ATTENTIVE_TIMEOUT = "reset_attentive_timeout"; 55 56 private static final int DEFAULT_SLEEP_TIME_MS = (int) (20 * DateUtils.MINUTE_IN_MILLIS); 57 private static final int WARNING_THRESHOLD_SLEEP_TIME_MS = 58 (int) (20 * DateUtils.MINUTE_IN_MILLIS); 59 private static final int WARNING_THRESHOLD_ATTENTIVE_TIME_MS = 60 (int) (4 * DateUtils.HOUR_IN_MILLIS); 61 62 private ListPreference mSleepTimePref; 63 private ListPreference mAttentiveTimePref; 64 private RestrictedPreferenceAdapter<ListPreference> mRestrictedSleepTime; 65 private RestrictedPreferenceAdapter<ListPreference> mRestrictedAttentiveTime; 66 private int mDefaultAttentiveTimeoutConfig; 67 68 @Override onCreatePreferences(Bundle bundle, String s)69 public void onCreatePreferences(Bundle bundle, String s) { 70 setPreferencesFromResource(R.xml.energy_saver, null); 71 72 mDefaultAttentiveTimeoutConfig = getResources() 73 .getInteger(com.android.internal.R.integer.config_attentiveTimeout); 74 75 mSleepTimePref = findPreference(KEY_SLEEP_TIME); 76 int validatedSleepTime = getValidatedTimeout(getSleepTime(), true); 77 mSleepTimePref.setValue(String.valueOf(validatedSleepTime)); 78 if (getSleepTime() != validatedSleepTime) { 79 setSleepTime(validatedSleepTime); 80 } 81 mSleepTimePref.setOnPreferenceChangeListener(this); 82 mSleepTimePref.setOnPreferenceClickListener( 83 preference -> { 84 logEntrySelected(TvSettingsEnums.SYSTEM_ENERGYSAVER_START_DELAY); 85 return false; 86 }); 87 88 mAttentiveTimePref = findPreference(KEY_ATTENTIVE_TIME); 89 mAttentiveTimePref.setOnPreferenceChangeListener(this); 90 91 mRestrictedSleepTime = RestrictedPreferenceAdapter.adapt( 92 mSleepTimePref, UserManager.DISALLOW_CONFIG_SCREEN_TIMEOUT); 93 mRestrictedAttentiveTime = RestrictedPreferenceAdapter.adapt( 94 mAttentiveTimePref, UserManager.DISALLOW_CONFIG_SCREEN_TIMEOUT); 95 96 if (!showAttentiveSleepTimeoutSetting()) { 97 mAttentiveTimePref.setVisible(false); 98 mRestrictedAttentiveTime.updatePreference(); 99 } else { 100 int validatedAttentiveSleepTime = getValidatedTimeout(getAttentiveSleepTime(), false); 101 mAttentiveTimePref.setValue(String.valueOf(validatedAttentiveSleepTime)); 102 if (getAttentiveSleepTime() != validatedAttentiveSleepTime) { 103 setAttentiveSleepTime(validatedAttentiveSleepTime); 104 } 105 } 106 } 107 showAttentiveSleepTimeoutSetting()108 private boolean showAttentiveSleepTimeoutSetting() { 109 return getResources().getBoolean(R.bool.config_show_standby_timeout); 110 } 111 112 @Override onPreferenceChange(Preference preference, Object newValue)113 public boolean onPreferenceChange(Preference preference, Object newValue) { 114 switch (preference.getKey()) { 115 case KEY_SLEEP_TIME: 116 final int newSleepTime = Integer.parseInt((String) newValue); 117 if (getSleepTimeEntryId(newSleepTime) != -1) { 118 logEntrySelected(getSleepTimeEntryId(newSleepTime)); 119 } 120 if (showAttentiveSleepTimeoutSetting() && isTimeLargerThan( 121 newSleepTime, getAttentiveSleepTime())) { 122 new AlertDialog.Builder(getContext()) 123 .setMessage(R.string.device_energy_saver_validation_sleep) 124 .setPositiveButton( 125 R.string.settings_ok, (dialog, which) -> dialog.dismiss()) 126 .create() 127 .show(); 128 return false; 129 } else if (newSleepTime > WARNING_THRESHOLD_SLEEP_TIME_MS || newSleepTime == -1) { 130 // Some regions require a warning to be presented. 131 showConfirmChangeSettingDialog(false, newSleepTime); 132 return false; 133 } else { 134 confirmNewSleepTime(newSleepTime); 135 return true; 136 } 137 case KEY_ATTENTIVE_TIME: 138 final int attentiveTime = Integer.parseInt((String) newValue); 139 if (isTimeLargerThan(getSleepTime(), attentiveTime)) { 140 new AlertDialog.Builder(getContext()) 141 .setMessage(R.string.device_energy_saver_validation_attentive) 142 .setPositiveButton( 143 R.string.settings_ok, (dialog, which) -> dialog.dismiss()) 144 .create() 145 .show(); 146 return false; 147 } else if (attentiveTime > WARNING_THRESHOLD_ATTENTIVE_TIME_MS 148 || attentiveTime == -1) { 149 showConfirmChangeSettingDialog(true, attentiveTime); 150 return false; 151 } 152 confirmAttentiveSleepTime(attentiveTime); 153 return true; 154 default: 155 return false; 156 } 157 } 158 isTimeLargerThan(int x, int y)159 private boolean isTimeLargerThan(int x, int y) { 160 if (x == -1 && y == -1) { 161 return false; 162 } 163 if (x == -1) { 164 return true; 165 } 166 if (y == -1) { 167 return false; 168 } 169 return x > y; 170 } 171 showConfirmChangeSettingDialog(boolean isAttentiveTimer, int newTime)172 private void showConfirmChangeSettingDialog(boolean isAttentiveTimer, int newTime) { 173 new AlertDialog.Builder(getContext()) 174 .setTitle(R.string.device_energy_saver_confirmation_title) 175 .setMessage(R.string.device_energy_saver_confirmation_message) 176 .setPositiveButton(R.string.settings_confirm, 177 (dialog, which) -> { 178 if (isAttentiveTimer) { 179 confirmAttentiveSleepTime(newTime); 180 } else { 181 confirmNewSleepTime(newTime); 182 } 183 }) 184 .setNegativeButton(R.string.settings_cancel, 185 (dialog, which) -> dialog.dismiss()) 186 .create() 187 .show(); 188 } 189 confirmAttentiveSleepTime(int attentiveTime)190 private void confirmAttentiveSleepTime(int attentiveTime) { 191 if (mAttentiveTimePref != null) { 192 mAttentiveTimePref.setValue(String.valueOf(attentiveTime)); 193 setAttentiveSleepTime(attentiveTime); 194 mRestrictedAttentiveTime.updatePreference(); 195 if (getCallbackFragment() instanceof TwoPanelSettingsFragment) { 196 ((TwoPanelSettingsFragment) getCallbackFragment()).refocusPreference(this); 197 } 198 } 199 } 200 getSleepTime()201 private int getSleepTime() { 202 return Settings.Secure.getInt(getActivity().getContentResolver(), SLEEP_TIMEOUT, 203 DEFAULT_SLEEP_TIME_MS); 204 } 205 setSleepTime(int ms)206 private void setSleepTime(int ms) { 207 Settings.Secure.putInt(getActivity().getContentResolver(), SLEEP_TIMEOUT, ms); 208 } 209 getAttentiveSleepTime()210 private int getAttentiveSleepTime() { 211 return getAttentiveSleepTime(mDefaultAttentiveTimeoutConfig); 212 } 213 getAttentiveSleepTime(int def)214 private int getAttentiveSleepTime(int def) { 215 return Settings.Secure.getInt(getActivity().getContentResolver(), ATTENTIVE_TIMEOUT, def); 216 } 217 setAttentiveSleepTime(int ms)218 private void setAttentiveSleepTime(int ms) { 219 Settings.Secure.putInt(getActivity().getContentResolver(), ATTENTIVE_TIMEOUT, ms); 220 } 221 222 // The SLEEP_TIMEOUT and ATTENTIVE_TIMEOUT could be defined in overlay by OEMs. We validate the 223 // value to make sure that we select from the predefined options. If the value from overlay is 224 // not one of the predefined options, we round it to the closest predefined value, except -1. getValidatedTimeout(int purposedTimeout, boolean isSleepTimeout)225 private int getValidatedTimeout(int purposedTimeout, boolean isSleepTimeout) { 226 int validatedTimeout = 227 isSleepTimeout ? DEFAULT_SLEEP_TIME_MS : mDefaultAttentiveTimeoutConfig; 228 if (purposedTimeout < 0) { 229 return -1; 230 231 } 232 String[] optionsString = isSleepTimeout 233 ? getResources().getStringArray(R.array.device_energy_saver_sleep_timeout_values) 234 : getResources().getStringArray( 235 R.array.device_energy_saver_attentive_timeout_values); 236 // Find the value from the predefined values that is closest to the proposed value except -1 237 int diff = Integer.MAX_VALUE; 238 for (String option : optionsString) { 239 if (Integer.parseInt(option) != -1) { 240 int currentDiff = Math.abs(purposedTimeout - Integer.parseInt(option)); 241 if (currentDiff < diff) { 242 diff = currentDiff; 243 validatedTimeout = Integer.parseInt(option); 244 } 245 } 246 } 247 return validatedTimeout; 248 } 249 confirmNewSleepTime(int newSleepTime)250 private void confirmNewSleepTime(int newSleepTime) { 251 if (mSleepTimePref != null) { 252 setSleepTime(newSleepTime); 253 mSleepTimePref.setValue(String.valueOf(newSleepTime)); 254 mRestrictedSleepTime.updatePreference(); 255 if (getCallbackFragment() instanceof TwoPanelSettingsFragment) { 256 ((TwoPanelSettingsFragment) getCallbackFragment()).refocusPreference(this); 257 } 258 } 259 } 260 261 // TODO(b/158783050): update logging for new options 4H, 8H, 24H. 262 // Map @array/screen_off_timeout_entries to defined log enum getSleepTimeEntryId(int sleepTimeValue)263 private int getSleepTimeEntryId(int sleepTimeValue) { 264 switch (sleepTimeValue) { 265 case -1: 266 return TvSettingsEnums.SYSTEM_ENERGYSAVER_START_DELAY_NEVER; 267 case 900000: 268 return TvSettingsEnums.SYSTEM_ENERGYSAVER_START_DELAY_15M; 269 case 1800000: 270 return TvSettingsEnums.SYSTEM_ENERGYSAVER_START_DELAY_30M; 271 case 3600000: 272 return TvSettingsEnums.SYSTEM_ENERGYSAVER_START_DELAY_1H; 273 case 43200000: 274 return TvSettingsEnums.SYSTEM_ENERGYSAVER_START_DELAY_12H; 275 default: 276 return -1; 277 } 278 } 279 280 @Override getPageId()281 protected int getPageId() { 282 return TvSettingsEnums.SYSTEM_ENERGYSAVER; 283 } 284 285 /** 286 * Fix for b/286356445: 287 * The attentive timeout was previously set incorrectly when this Fragment was created. 288 * This method resets the attentive timeout setting to its default value if the setting 289 * is not supposed to be shown and this hasn't been run before. 290 */ resetAttentiveTimeoutIfHidden(Context context)291 public static void resetAttentiveTimeoutIfHidden(Context context) { 292 // 293 boolean showAttentiveSleepTimeoutSetting = context.getResources().getBoolean( 294 R.bool.config_show_standby_timeout); 295 if (showAttentiveSleepTimeoutSetting) { 296 // Keep current setting, as user can change it and may have changed it 297 return; 298 } 299 300 try { 301 final SharedPreferences sharedPreferences = context.getSharedPreferences( 302 SHARED_PREFS_NAME, Context.MODE_PRIVATE); 303 boolean hasResetAttentiveTimeout = sharedPreferences.getBoolean( 304 PREF_RESET_ATTENTIVE_TIMEOUT, false); 305 if (!hasResetAttentiveTimeout) { 306 Settings.Secure.putString(context.getContentResolver(), ATTENTIVE_TIMEOUT, ""); 307 sharedPreferences.edit() 308 .putBoolean(PREF_RESET_ATTENTIVE_TIMEOUT, true) 309 .apply(); 310 } 311 } catch (Exception e) { 312 Log.w(TAG, "Failed to reset attentive timeout", e); 313 } 314 } 315 } 316