• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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