• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright (C) 2022 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.spa.app.appinfo
18 
19 import android.content.Context
20 import android.content.pm.ApplicationInfo
21 import android.util.Log
22 import androidx.compose.runtime.Composable
23 import androidx.compose.runtime.LaunchedEffect
24 import androidx.compose.runtime.derivedStateOf
25 import androidx.compose.runtime.getValue
26 import androidx.compose.runtime.mutableStateOf
27 import androidx.compose.runtime.remember
28 import androidx.compose.runtime.setValue
29 import androidx.compose.ui.platform.LocalContext
30 import androidx.compose.ui.platform.LocalLifecycleOwner
31 import androidx.compose.ui.res.stringResource
32 import androidx.core.os.bundleOf
33 import androidx.lifecycle.Lifecycle
34 import androidx.lifecycle.repeatOnLifecycle
35 import com.android.settings.R
36 import com.android.settings.Utils
37 import com.android.settings.core.SubSettingLauncher
38 import com.android.settings.fuelgauge.AdvancedPowerUsageDetail
39 import com.android.settings.fuelgauge.batteryusage.BatteryChartPreferenceController
40 import com.android.settings.fuelgauge.batteryusage.BatteryDiffEntry
41 import com.android.settingslib.spa.widget.preference.Preference
42 import com.android.settingslib.spa.widget.preference.PreferenceModel
43 import com.android.settingslib.spaprivileged.model.app.installed
44 import com.android.settingslib.spaprivileged.model.app.userHandle
45 import com.android.settingslib.spaprivileged.model.app.userId
46 import kotlinx.coroutines.Dispatchers
47 import kotlinx.coroutines.launch
48 import kotlinx.coroutines.withContext
49 
50 @Composable
51 fun AppBatteryPreference(app: ApplicationInfo) {
52     val context = LocalContext.current
53     val presenter = remember { AppBatteryPresenter(context, app) }
54     if (!presenter.isAvailable()) return
55 
56     Preference(object : PreferenceModel {
57         override val title = stringResource(R.string.battery_details_title)
58         override val summary = presenter.summary
59         override val enabled = presenter.enabled
60         override val onClick = presenter::startActivity
61     })
62 
63     presenter.Updater()
64 }
65 
66 private class AppBatteryPresenter(private val context: Context, private val app: ApplicationInfo) {
67     private var batteryDiffEntryState: LoadingState<BatteryDiffEntry?>
68         by mutableStateOf(LoadingState.Loading)
69 
70     @Composable
<lambda>null71     fun isAvailable() = remember {
72         context.resources.getBoolean(R.bool.config_show_app_info_settings_battery)
73     }
74 
75     @Composable
Updaternull76     fun Updater() {
77         if (!app.installed) return
78         val current = LocalLifecycleOwner.current
79         LaunchedEffect(app) {
80             current.repeatOnLifecycle(Lifecycle.State.STARTED) {
81                 launch { batteryDiffEntryState = LoadingState.Done(getBatteryDiffEntry()) }
82             }
83         }
84     }
85 
<lambda>null86     private suspend fun getBatteryDiffEntry(): BatteryDiffEntry? = withContext(Dispatchers.IO) {
87         BatteryChartPreferenceController.getAppBatteryUsageData(
88             context, app.packageName, app.userId
89         ).also {
90             Log.d(TAG, "loadBatteryDiffEntries():\n$it")
91         }
92     }
93 
<lambda>null94     val enabled = derivedStateOf { batteryDiffEntryState is LoadingState.Done }
95 
<lambda>null96     val summary = derivedStateOf<String> {
97         if (!app.installed) return@derivedStateOf ""
98         batteryDiffEntryState.let { batteryDiffEntryState ->
99             when (batteryDiffEntryState) {
100                 is LoadingState.Loading -> context.getString(R.string.summary_placeholder)
101                 is LoadingState.Done -> batteryDiffEntryState.result.getSummary()
102             }
103         }
104     }
105 
BatteryDiffEntrynull106     private fun BatteryDiffEntry?.getSummary(): String =
107         this?.takeIf { mConsumePower > 0 }?.let {
108             context.getString(
109                 R.string.battery_summary, Utils.formatPercentage(percentage, true)
110             )
111         } ?: context.getString(R.string.no_battery_summary)
112 
startActivitynull113     fun startActivity() {
114         batteryDiffEntryState.resultOrNull?.run {
115             startBatteryDetailPage()
116             return
117         }
118 
119         fallbackStartBatteryDetailPage()
120     }
121 
startBatteryDetailPagenull122     private fun BatteryDiffEntry.startBatteryDetailPage() {
123         Log.i(TAG, "handlePreferenceTreeClick():\n$this")
124         AdvancedPowerUsageDetail.startBatteryDetailPage(
125             context,
126             AppInfoSettingsProvider.METRICS_CATEGORY,
127             this,
128             Utils.formatPercentage(percentage, true),
129             null,
130             false,
131         )
132     }
133 
fallbackStartBatteryDetailPagenull134     private fun fallbackStartBatteryDetailPage() {
135         Log.i(TAG, "Launch : ${app.packageName} with package name")
136         val args = bundleOf(
137             AdvancedPowerUsageDetail.EXTRA_PACKAGE_NAME to app.packageName,
138             AdvancedPowerUsageDetail.EXTRA_POWER_USAGE_PERCENT to Utils.formatPercentage(0),
139             AdvancedPowerUsageDetail.EXTRA_UID to app.uid,
140         )
141         SubSettingLauncher(context)
142             .setDestination(AdvancedPowerUsageDetail::class.java.name)
143             .setTitleRes(R.string.battery_details_title)
144             .setArguments(args)
145             .setUserHandle(app.userHandle)
146             .setSourceMetricsCategory(AppInfoSettingsProvider.METRICS_CATEGORY)
147             .launch()
148     }
149 
150     companion object {
151         private const val TAG = "AppBatteryPresenter"
152     }
153 }
154 
155 private sealed class LoadingState<out T> {
156     object Loading : LoadingState<Nothing>()
157 
158     data class Done<T>(val result: T) : LoadingState<T>()
159 
160     val resultOrNull: T? get() = if (this is Done) result else null
161 }
162