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