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.getValue 25 import androidx.compose.runtime.mutableStateOf 26 import androidx.compose.runtime.remember 27 import androidx.compose.runtime.setValue 28 import androidx.compose.ui.platform.LocalContext 29 import androidx.compose.ui.platform.LocalLifecycleOwner 30 import androidx.compose.ui.res.stringResource 31 import androidx.core.os.bundleOf 32 import androidx.lifecycle.Lifecycle 33 import androidx.lifecycle.repeatOnLifecycle 34 import com.android.settings.R 35 import com.android.settings.Utils 36 import com.android.settings.core.SubSettingLauncher 37 import com.android.settings.fuelgauge.AdvancedPowerUsageDetail 38 import com.android.settings.fuelgauge.batteryusage.BatteryChartPreferenceController 39 import com.android.settings.fuelgauge.batteryusage.BatteryDiffEntry 40 import com.android.settingslib.spa.widget.preference.Preference 41 import com.android.settingslib.spa.widget.preference.PreferenceModel 42 import com.android.settingslib.spaprivileged.model.app.installed 43 import com.android.settingslib.spaprivileged.model.app.userHandle 44 import com.android.settingslib.spaprivileged.model.app.userId 45 import kotlinx.coroutines.Dispatchers 46 import kotlinx.coroutines.launch 47 import kotlinx.coroutines.withContext 48 49 @Composable 50 fun AppBatteryPreference(app: ApplicationInfo) { 51 val context = LocalContext.current 52 val presenter = remember(app) { AppBatteryPresenter(context, app) } 53 if (!presenter.isAvailable()) return 54 55 Preference(object : PreferenceModel { 56 override val title = stringResource(R.string.battery_details_title) 57 override val summary = presenter.summary 58 override val enabled = presenter.enabled 59 override val onClick = presenter::startActivity 60 }) 61 62 presenter.Updater() 63 } 64 65 private class AppBatteryPresenter(private val context: Context, private val app: ApplicationInfo) { 66 private var batteryDiffEntryState: LoadingState<BatteryDiffEntry?> 67 by mutableStateOf(LoadingState.Loading) 68 69 @Composable <lambda>null70 fun isAvailable() = remember { 71 context.resources.getBoolean(R.bool.config_show_app_info_settings_battery) 72 } 73 74 @Composable Updaternull75 fun Updater() { 76 if (!app.installed) return 77 val current = LocalLifecycleOwner.current 78 LaunchedEffect(app) { 79 current.repeatOnLifecycle(Lifecycle.State.STARTED) { 80 launch { batteryDiffEntryState = LoadingState.Done(getBatteryDiffEntry()) } 81 } 82 } 83 } 84 <lambda>null85 private suspend fun getBatteryDiffEntry(): BatteryDiffEntry? = withContext(Dispatchers.IO) { 86 BatteryChartPreferenceController.getAppBatteryUsageData( 87 context, app.packageName, app.userId 88 ).also { 89 Log.d(TAG, "loadBatteryDiffEntries():\n$it") 90 } 91 } 92 <lambda>null93 val enabled = { batteryDiffEntryState is LoadingState.Done } 94 <lambda>null95 val summary = { 96 if (app.installed) { 97 batteryDiffEntryState.let { batteryDiffEntryState -> 98 when (batteryDiffEntryState) { 99 is LoadingState.Loading -> context.getString(R.string.summary_placeholder) 100 is LoadingState.Done -> batteryDiffEntryState.result.getSummary() 101 } 102 } 103 } else "" 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 /*slotInformation=*/ null, 130 /*showTimeInformation=*/ false, 131 /*anomalyHintPrefKey=*/ null, 132 /*anomalyHintText=*/ null 133 ) 134 } 135 fallbackStartBatteryDetailPagenull136 private fun fallbackStartBatteryDetailPage() { 137 Log.i(TAG, "Launch : ${app.packageName} with package name") 138 val args = bundleOf( 139 AdvancedPowerUsageDetail.EXTRA_PACKAGE_NAME to app.packageName, 140 AdvancedPowerUsageDetail.EXTRA_POWER_USAGE_PERCENT to Utils.formatPercentage(0), 141 AdvancedPowerUsageDetail.EXTRA_UID to app.uid, 142 ) 143 SubSettingLauncher(context) 144 .setDestination(AdvancedPowerUsageDetail::class.java.name) 145 .setTitleRes(R.string.battery_details_title) 146 .setArguments(args) 147 .setUserHandle(app.userHandle) 148 .setSourceMetricsCategory(AppInfoSettingsProvider.METRICS_CATEGORY) 149 .launch() 150 } 151 152 companion object { 153 private const val TAG = "AppBatteryPresenter" 154 } 155 } 156 157 private sealed class LoadingState<out T> { 158 data object Loading : LoadingState<Nothing>() 159 160 data class Done<T>(val result: T) : LoadingState<T>() 161 162 val resultOrNull: T? get() = if (this is Done) result else null 163 } 164