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(
topBarconfig = scaffoldConfig.topBarConfig,
bottomBarConfig = scaffoldConfig.bottomBarConfig,
onScaffoldEvent = handleScaffoldEvent,
snackbarHostState = snackbarHostState,
) { paddingValues ->
// AnimatedNavHost inside the scaffold content

View File

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

View File

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

View File

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

View File

@ -1,5 +1,6 @@
package com.example.llama.ui.scaffold.topbar
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding
@ -20,10 +21,12 @@ import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.unit.dp
import com.example.llama.monitoring.MemoryMetrics
import com.example.llama.monitoring.TemperatureMetrics
import com.example.llama.monitoring.TemperatureWarningLevel
import com.example.llama.ui.scaffold.ScaffoldEvent
import java.util.Locale
@OptIn(ExperimentalMaterial3Api::class)
@ -32,6 +35,7 @@ fun PerformanceTopBar(
title: String,
memoryMetrics: MemoryMetrics?,
temperatureDisplay: Pair<TemperatureMetrics, Boolean>?,
onScaffoldEvent: (ScaffoldEvent) -> Unit,
onNavigateBack: (() -> Unit)? = null,
onMenuOpen: (() -> Unit)? = null,
) {
@ -63,7 +67,8 @@ fun PerformanceTopBar(
temperatureDisplay?.let { (temperatureMetrics, useFahrenheit) ->
TemperatureIndicator(
temperatureMetrics = temperatureMetrics,
useFahrenheit = useFahrenheit
useFahrenheit = useFahrenheit,
onScaffoldEvent = onScaffoldEvent,
)
Spacer(modifier = Modifier.width(8.dp))
@ -71,7 +76,7 @@ fun PerformanceTopBar(
// Memory indicator
memoryMetrics?.let {
MemoryIndicator(memoryUsage = it)
MemoryIndicator(memoryUsage = it, onScaffoldEvent = onScaffoldEvent)
}
},
colors = TopAppBarDefaults.topAppBarColors(
@ -82,17 +87,28 @@ fun PerformanceTopBar(
}
@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(
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,
) {
Icon(
imageVector = Icons.Default.Memory,
contentDescription = "RAM usage",
tint = when {
memoryUsage.availableGb < 1 -> MaterialTheme.colorScheme.error
memoryUsage.availableGb < 3 -> MaterialTheme.colorScheme.tertiary
memoryUsage.availableGB < 1 -> MaterialTheme.colorScheme.error
memoryUsage.availableGB < 3 -> MaterialTheme.colorScheme.tertiary
else -> MaterialTheme.colorScheme.onSurface
}
)
@ -100,17 +116,35 @@ private fun MemoryIndicator(memoryUsage: MemoryMetrics) {
Spacer(modifier = Modifier.width(4.dp))
Text(
text = String.format(
Locale.getDefault(), "%.1f / %.1f GB", memoryUsage.availableGb, memoryUsage.totalGb
),
text = "$availableGB / $totalGB GB",
style = MaterialTheme.typography.bodySmall,
)
}
}
@Composable
private fun TemperatureIndicator(temperatureMetrics: TemperatureMetrics, useFahrenheit: Boolean) {
Row(verticalAlignment = Alignment.CenterVertically) {
private fun TemperatureIndicator(
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(
imageVector = when (temperatureMetrics.warningLevel) {
TemperatureWarningLevel.HIGH -> Icons.Default.WarningAmber
@ -127,7 +161,7 @@ private fun TemperatureIndicator(temperatureMetrics: TemperatureMetrics, useFahr
Spacer(modifier = Modifier.width(2.dp))
Text(
text = temperatureMetrics.getDisplay(useFahrenheit),
text = temperatureDisplay,
style = MaterialTheme.typography.bodySmall,
color = when (temperatureMetrics.warningLevel) {
TemperatureWarningLevel.HIGH -> MaterialTheme.colorScheme.error

View File

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