UI: show corresponding system metrics detailed info upon tapping RAM / storage / temperature indicator

This commit is contained in:
Han Yin 2025-08-29 19:44:27 -07:00
parent 50cea70de3
commit 2e9de7c99c
6 changed files with 75 additions and 29 deletions

View File

@ -413,6 +413,7 @@ fun AppContent(
AppScaffold( AppScaffold(
topBarconfig = scaffoldConfig.topBarConfig, topBarconfig = scaffoldConfig.topBarConfig,
bottomBarConfig = scaffoldConfig.bottomBarConfig, bottomBarConfig = scaffoldConfig.bottomBarConfig,
onScaffoldEvent = handleScaffoldEvent,
snackbarHostState = snackbarHostState, snackbarHostState = snackbarHostState,
) { paddingValues -> ) { paddingValues ->
// AnimatedNavHost inside the scaffold content // AnimatedNavHost inside the scaffold content

View File

@ -71,8 +71,8 @@ class PerformanceMonitor(@ApplicationContext private val context: Context) {
availableMem = availableMem, availableMem = availableMem,
totalMem = totalMem, totalMem = totalMem,
percentUsed = percentUsed, percentUsed = percentUsed,
availableGb = availableGb, availableGB = availableGb,
totalGb = totalGb totalGB = totalGb
) )
} }

View File

@ -15,8 +15,8 @@ data class MemoryMetrics(
val availableMem: Long, val availableMem: Long,
val totalMem: Long, val totalMem: Long,
val percentUsed: Int, val percentUsed: Int,
val availableGb: Float, val availableGB: Float,
val totalGb: Float val totalGB: Float
) )
/** /**

View File

@ -51,6 +51,7 @@ sealed class ScaffoldEvent {
fun AppScaffold( fun AppScaffold(
topBarconfig: TopBarConfig, topBarconfig: TopBarConfig,
bottomBarConfig: BottomBarConfig = BottomBarConfig.None, bottomBarConfig: BottomBarConfig = BottomBarConfig.None,
onScaffoldEvent: (ScaffoldEvent) -> Unit,
snackbarHostState: SnackbarHostState = remember { SnackbarHostState() }, snackbarHostState: SnackbarHostState = remember { SnackbarHostState() },
content: @Composable (PaddingValues) -> Unit content: @Composable (PaddingValues) -> Unit
) { ) {
@ -68,6 +69,7 @@ fun AppScaffold(
title = topBarconfig.title, title = topBarconfig.title,
memoryMetrics = topBarconfig.memoryMetrics, memoryMetrics = topBarconfig.memoryMetrics,
temperatureDisplay = topBarconfig.temperatureInfo, temperatureDisplay = topBarconfig.temperatureInfo,
onScaffoldEvent = onScaffoldEvent,
onNavigateBack = topBarconfig.navigationIcon.backAction, onNavigateBack = topBarconfig.navigationIcon.backAction,
onMenuOpen = topBarconfig.navigationIcon.menuAction, onMenuOpen = topBarconfig.navigationIcon.menuAction,
) )
@ -75,6 +77,7 @@ fun AppScaffold(
is TopBarConfig.Storage -> StorageTopBar( is TopBarConfig.Storage -> StorageTopBar(
title = topBarconfig.title, title = topBarconfig.title,
storageMetrics = topBarconfig.storageMetrics, storageMetrics = topBarconfig.storageMetrics,
onScaffoldEvent = onScaffoldEvent,
onNavigateBack = topBarconfig.navigationIcon.backAction, onNavigateBack = topBarconfig.navigationIcon.backAction,
) )
} }

View File

@ -1,5 +1,6 @@
package com.example.llama.ui.scaffold.topbar package com.example.llama.ui.scaffold.topbar
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
@ -20,10 +21,12 @@ import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.example.llama.monitoring.MemoryMetrics import com.example.llama.monitoring.MemoryMetrics
import com.example.llama.monitoring.TemperatureMetrics import com.example.llama.monitoring.TemperatureMetrics
import com.example.llama.monitoring.TemperatureWarningLevel import com.example.llama.monitoring.TemperatureWarningLevel
import com.example.llama.ui.scaffold.ScaffoldEvent
import java.util.Locale import java.util.Locale
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@ -32,6 +35,7 @@ fun PerformanceTopBar(
title: String, title: String,
memoryMetrics: MemoryMetrics?, memoryMetrics: MemoryMetrics?,
temperatureDisplay: Pair<TemperatureMetrics, Boolean>?, temperatureDisplay: Pair<TemperatureMetrics, Boolean>?,
onScaffoldEvent: (ScaffoldEvent) -> Unit,
onNavigateBack: (() -> Unit)? = null, onNavigateBack: (() -> Unit)? = null,
onMenuOpen: (() -> Unit)? = null, onMenuOpen: (() -> Unit)? = null,
) { ) {
@ -63,7 +67,8 @@ fun PerformanceTopBar(
temperatureDisplay?.let { (temperatureMetrics, useFahrenheit) -> temperatureDisplay?.let { (temperatureMetrics, useFahrenheit) ->
TemperatureIndicator( TemperatureIndicator(
temperatureMetrics = temperatureMetrics, temperatureMetrics = temperatureMetrics,
useFahrenheit = useFahrenheit useFahrenheit = useFahrenheit,
onScaffoldEvent = onScaffoldEvent,
) )
Spacer(modifier = Modifier.width(8.dp)) Spacer(modifier = Modifier.width(8.dp))
@ -71,7 +76,7 @@ fun PerformanceTopBar(
// Memory indicator // Memory indicator
memoryMetrics?.let { memoryMetrics?.let {
MemoryIndicator(memoryUsage = it) MemoryIndicator(memoryUsage = it, onScaffoldEvent = onScaffoldEvent)
} }
}, },
colors = TopAppBarDefaults.topAppBarColors( colors = TopAppBarDefaults.topAppBarColors(
@ -82,17 +87,28 @@ fun PerformanceTopBar(
} }
@Composable @Composable
private fun MemoryIndicator(memoryUsage: MemoryMetrics) { private fun MemoryIndicator(
memoryUsage: MemoryMetrics,
onScaffoldEvent: (ScaffoldEvent) -> Unit,
) {
val availableGB = String.format(Locale.getDefault(), "%.1f", memoryUsage.availableGB)
val totalGB = String.format(Locale.getDefault(), "%.1f", memoryUsage.totalGB)
Row( Row(
modifier = Modifier.padding(end = 8.dp), modifier = Modifier.padding(end = 8.dp).clickable(role = Role.Button) {
onScaffoldEvent(ScaffoldEvent.ShowSnackbar(
message = "Free RAM available: $availableGB GB\nTotal RAM on your device: $totalGB GB",
withDismissAction = true,
))
},
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
) { ) {
Icon( Icon(
imageVector = Icons.Default.Memory, imageVector = Icons.Default.Memory,
contentDescription = "RAM usage", contentDescription = "RAM usage",
tint = when { tint = when {
memoryUsage.availableGb < 1 -> MaterialTheme.colorScheme.error memoryUsage.availableGB < 1 -> MaterialTheme.colorScheme.error
memoryUsage.availableGb < 3 -> MaterialTheme.colorScheme.tertiary memoryUsage.availableGB < 3 -> MaterialTheme.colorScheme.tertiary
else -> MaterialTheme.colorScheme.onSurface else -> MaterialTheme.colorScheme.onSurface
} }
) )
@ -100,17 +116,35 @@ private fun MemoryIndicator(memoryUsage: MemoryMetrics) {
Spacer(modifier = Modifier.width(4.dp)) Spacer(modifier = Modifier.width(4.dp))
Text( Text(
text = String.format( text = "$availableGB / $totalGB GB",
Locale.getDefault(), "%.1f / %.1f GB", memoryUsage.availableGb, memoryUsage.totalGb
),
style = MaterialTheme.typography.bodySmall, style = MaterialTheme.typography.bodySmall,
) )
} }
} }
@Composable @Composable
private fun TemperatureIndicator(temperatureMetrics: TemperatureMetrics, useFahrenheit: Boolean) { private fun TemperatureIndicator(
Row(verticalAlignment = Alignment.CenterVertically) { temperatureMetrics: TemperatureMetrics,
useFahrenheit: Boolean,
onScaffoldEvent: (ScaffoldEvent) -> Unit,
) {
val temperatureDisplay = temperatureMetrics.getDisplay(useFahrenheit)
val temperatureWarning = when (temperatureMetrics.warningLevel) {
TemperatureWarningLevel.HIGH -> "Your device is HEATED UP to $temperatureDisplay, please cool it down before continue using the app."
TemperatureWarningLevel.MEDIUM -> "Your device is warming up to $temperatureDisplay."
else -> "Your device's temperature is $temperatureDisplay."
}
val warningDismissible = temperatureMetrics.warningLevel == TemperatureWarningLevel.HIGH
Row(
modifier = Modifier.clickable(role = Role.Button) {
onScaffoldEvent(ScaffoldEvent.ShowSnackbar(
message = temperatureWarning,
withDismissAction = warningDismissible,
))
},
verticalAlignment = Alignment.CenterVertically) {
Icon( Icon(
imageVector = when (temperatureMetrics.warningLevel) { imageVector = when (temperatureMetrics.warningLevel) {
TemperatureWarningLevel.HIGH -> Icons.Default.WarningAmber TemperatureWarningLevel.HIGH -> Icons.Default.WarningAmber
@ -127,7 +161,7 @@ private fun TemperatureIndicator(temperatureMetrics: TemperatureMetrics, useFahr
Spacer(modifier = Modifier.width(2.dp)) Spacer(modifier = Modifier.width(2.dp))
Text( Text(
text = temperatureMetrics.getDisplay(useFahrenheit), text = temperatureDisplay,
style = MaterialTheme.typography.bodySmall, style = MaterialTheme.typography.bodySmall,
color = when (temperatureMetrics.warningLevel) { color = when (temperatureMetrics.warningLevel) {
TemperatureWarningLevel.HIGH -> MaterialTheme.colorScheme.error TemperatureWarningLevel.HIGH -> MaterialTheme.colorScheme.error

View File

@ -1,5 +1,6 @@
package com.example.llama.ui.scaffold.topbar package com.example.llama.ui.scaffold.topbar
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
@ -17,8 +18,10 @@ import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.example.llama.monitoring.StorageMetrics import com.example.llama.monitoring.StorageMetrics
import com.example.llama.ui.scaffold.ScaffoldEvent
import java.util.Locale import java.util.Locale
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@ -26,6 +29,7 @@ import java.util.Locale
fun StorageTopBar( fun StorageTopBar(
title: String, title: String,
storageMetrics: StorageMetrics?, storageMetrics: StorageMetrics?,
onScaffoldEvent: (ScaffoldEvent) -> Unit,
onNavigateBack: (() -> Unit)? = null, onNavigateBack: (() -> Unit)? = null,
) { ) {
TopAppBar( TopAppBar(
@ -42,7 +46,7 @@ fun StorageTopBar(
}, },
actions = { actions = {
storageMetrics?.let { storageMetrics?.let {
StorageIndicator(storageMetrics = it) StorageIndicator(storageMetrics = it, onScaffoldEvent = onScaffoldEvent)
} }
}, },
colors = TopAppBarDefaults.topAppBarColors( colors = TopAppBarDefaults.topAppBarColors(
@ -53,20 +57,29 @@ fun StorageTopBar(
} }
@Composable @Composable
private fun StorageIndicator(storageMetrics: StorageMetrics) { private fun StorageIndicator(
val usedGb = storageMetrics.usedGB storageMetrics: StorageMetrics,
val availableGb = storageMetrics.availableGB onScaffoldEvent: (ScaffoldEvent) -> Unit,
) {
val usedGb = String.format(Locale.getDefault(), "%.1f", storageMetrics.usedGB)
val availableGb = String.format(Locale.getDefault(), "%.1f", storageMetrics.availableGB)
Row( Row(
modifier = Modifier.padding(end = 8.dp), modifier = Modifier.padding(end = 8.dp).clickable(role = Role.Button) {
onScaffoldEvent(ScaffoldEvent.ShowSnackbar(
message = "Your models occupy $usedGb GB storage\nRemaining free space available: $availableGb GB",
withDismissAction = true,
))
},
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
Icon( Icon(
imageVector = Icons.Default.SdStorage, imageVector = Icons.Default.SdStorage,
contentDescription = "Storage", contentDescription = "Storage",
tint = when { tint = when {
availableGb < 5.0f -> MaterialTheme.colorScheme.error storageMetrics.availableGB < 5.0f -> MaterialTheme.colorScheme.error
availableGb < 10.0f -> MaterialTheme.colorScheme.tertiary storageMetrics.availableGB < 10.0f -> MaterialTheme.colorScheme.tertiary
else -> MaterialTheme.colorScheme.onSurface else -> MaterialTheme.colorScheme.onSurface
} }
) )
@ -74,12 +87,7 @@ private fun StorageIndicator(storageMetrics: StorageMetrics) {
Spacer(modifier = Modifier.width(2.dp)) Spacer(modifier = Modifier.width(2.dp))
Text( Text(
text = String.format( text = "$usedGb / $availableGb GB",
Locale.getDefault(),
"%.1f / %.1f GB",
usedGb,
availableGb
),
style = MaterialTheme.typography.bodySmall style = MaterialTheme.typography.bodySmall
) )
} }