1 /* 2 * Copyright (C) 2024 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.fuelgauge.batterysaver 17 18 import android.Manifest 19 import android.app.settings.SettingsEnums.ACTION_BATTERY_SAVER 20 import android.content.Context 21 import android.os.PowerManager 22 import com.android.settings.R 23 import com.android.settings.contract.KEY_BATTERY_SAVER 24 import com.android.settings.fuelgauge.BatterySaverReceiver 25 import com.android.settings.fuelgauge.BatterySaverReceiver.BatterySaverListener 26 import com.android.settings.metrics.PreferenceActionMetricsProvider 27 import com.android.settingslib.datastore.AbstractKeyedDataObservable 28 import com.android.settingslib.datastore.KeyValueStore 29 import com.android.settingslib.datastore.Permissions 30 import com.android.settingslib.fuelgauge.BatterySaverLogging.SAVER_ENABLED_SETTINGS 31 import com.android.settingslib.fuelgauge.BatterySaverUtils 32 import com.android.settingslib.fuelgauge.BatteryStatus 33 import com.android.settingslib.fuelgauge.BatteryUtils 34 import com.android.settingslib.metadata.MainSwitchPreference 35 import com.android.settingslib.metadata.PreferenceChangeReason 36 import com.android.settingslib.metadata.ReadWritePermit 37 import com.android.settingslib.metadata.SensitivityLevel 38 import kotlinx.coroutines.CoroutineScope 39 import kotlinx.coroutines.Dispatchers 40 import kotlinx.coroutines.cancel 41 import kotlinx.coroutines.delay 42 import kotlinx.coroutines.launch 43 44 // LINT.IfChange 45 class BatterySaverPreference : 46 MainSwitchPreference(KEY, R.string.battery_saver_master_switch_title), 47 PreferenceActionMetricsProvider { 48 49 override val preferenceActionMetrics: Int 50 get() = ACTION_BATTERY_SAVER 51 tagsnull52 override fun tags(context: Context) = arrayOf(KEY_BATTERY_SAVER) 53 54 override fun storage(context: Context) = BatterySaverStore(context) 55 56 override fun getReadPermissions(context: Context) = Permissions.EMPTY 57 58 override fun getWritePermissions(context: Context) = 59 Permissions.anyOf(Manifest.permission.DEVICE_POWER, Manifest.permission.POWER_SAVER) 60 61 override fun getReadPermit(context: Context, callingPid: Int, callingUid: Int) = 62 ReadWritePermit.ALLOW 63 64 override fun getWritePermit( 65 context: Context, 66 value: Boolean?, 67 callingPid: Int, 68 callingUid: Int, 69 ) = ReadWritePermit.ALLOW 70 71 override val sensitivityLevel 72 get() = SensitivityLevel.NO_SENSITIVITY 73 74 override fun isEnabled(context: Context) = 75 !BatteryStatus(BatteryUtils.getBatteryIntent(context)).isPluggedIn 76 77 @Suppress("UNCHECKED_CAST") 78 class BatterySaverStore(private val context: Context) : 79 AbstractKeyedDataObservable<String>(), KeyValueStore, BatterySaverListener { 80 private lateinit var batterySaverReceiver: BatterySaverReceiver 81 private lateinit var scope: CoroutineScope 82 83 override fun contains(key: String) = key == KEY 84 85 override fun <T : Any> getValue(key: String, valueType: Class<T>) = 86 context.isPowerSaveMode() as T 87 88 override fun <T : Any> setValue(key: String, valueType: Class<T>, value: T?) { 89 BatterySaverUtils.setPowerSaveMode( 90 context, 91 value as Boolean, 92 /* needFirstTimeWarning= */ false, 93 SAVER_ENABLED_SETTINGS, 94 ) 95 } 96 97 private fun Context.isPowerSaveMode() = 98 getSystemService(PowerManager::class.java)?.isPowerSaveMode == true 99 100 override fun onFirstObserverAdded() { 101 scope = CoroutineScope(Dispatchers.Main) 102 batterySaverReceiver = 103 BatterySaverReceiver(context).apply { 104 setBatterySaverListener(this@BatterySaverStore) 105 setListening(true) 106 } 107 } 108 109 override fun onLastObserverRemoved() { 110 scope.cancel() 111 batterySaverReceiver.setListening(false) 112 } 113 114 override fun onPowerSaveModeChanged() { 115 scope.launch { 116 delay(SWITCH_ANIMATION_DURATION) 117 notifyChange(KEY, PreferenceChangeReason.VALUE) 118 } 119 } 120 121 override fun onBatteryChanged(pluggedIn: Boolean) = 122 notifyChange(KEY, PreferenceChangeReason.STATE) 123 } 124 125 companion object { 126 private const val KEY = "battery_saver" 127 private const val SWITCH_ANIMATION_DURATION: Long = 350L 128 } 129 } 130 // LINT.ThenChange(BatterySaverButtonPreferenceController.java) 131