Skip to content

Commit 94d2d05

Browse files
committed
ui: show device RAM usage in the chat screen
- add a new method getCurrentRAMUsage() in the ChatScreenViewModel which uses the ActivityManager API to get the current MemoryInfo - Add a new dropdown menu item which toggles the visibility of the RAM usage label
1 parent 1c6fbac commit 94d2d05

File tree

5 files changed

+93
-13
lines changed

5 files changed

+93
-13
lines changed

app/src/main/java/io/shubham0204/smollmandroid/ui/screens/chat/ChatActivity.kt

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ import io.shubham0204.smollmandroid.ui.screens.chat.ChatScreenViewModel.ModelLoa
110110
import io.shubham0204.smollmandroid.ui.screens.manage_tasks.ManageTasksActivity
111111
import io.shubham0204.smollmandroid.ui.screens.manage_tasks.TasksList
112112
import io.shubham0204.smollmandroid.ui.theme.SmolLMAndroidTheme
113+
import kotlinx.coroutines.delay
113114
import kotlinx.coroutines.launch
114115
import org.koin.android.ext.android.inject
115116

@@ -305,6 +306,7 @@ private fun ColumnScope.ScreenUI(
305306
currChat: Chat,
306307
) {
307308
val isGeneratingResponse by viewModel.isGeneratingResponse.collectAsStateWithLifecycle()
309+
RAMUsageLabel(viewModel)
308310
MessagesList(
309311
viewModel,
310312
isGeneratingResponse,
@@ -316,6 +318,30 @@ private fun ColumnScope.ScreenUI(
316318
)
317319
}
318320

321+
@Composable
322+
private fun RAMUsageLabel(viewModel: ChatScreenViewModel) {
323+
val showRAMUsageLabel by viewModel.showRAMUsageLabel.collectAsStateWithLifecycle()
324+
val context = LocalContext.current
325+
var labelText by remember { mutableStateOf("") }
326+
LaunchedEffect(showRAMUsageLabel) {
327+
if (showRAMUsageLabel) {
328+
while (true) {
329+
val (used, total) = viewModel.getCurrentMemoryUsage()
330+
labelText = context.getString(R.string.label_device_ram).format(used, total)
331+
delay(3000L)
332+
}
333+
}
334+
}
335+
if (showRAMUsageLabel) {
336+
Text(
337+
labelText,
338+
style = MaterialTheme.typography.labelSmall,
339+
modifier = Modifier.fillMaxWidth(),
340+
textAlign = TextAlign.Center,
341+
)
342+
}
343+
}
344+
319345
@Composable
320346
private fun ColumnScope.MessagesList(
321347
viewModel: ChatScreenViewModel,

app/src/main/java/io/shubham0204/smollmandroid/ui/screens/chat/ChatMoreOptionsPopup.kt

Lines changed: 38 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,21 +17,29 @@
1717
package io.shubham0204.smollmandroid.ui.screens.chat
1818

1919
import android.widget.Toast
20+
import androidx.compose.foundation.layout.Spacer
21+
import androidx.compose.foundation.layout.fillMaxWidth
22+
import androidx.compose.foundation.layout.height
2023
import androidx.compose.material.icons.Icons
2124
import androidx.compose.material.icons.automirrored.filled.ShortText
2225
import androidx.compose.material.icons.filled.Assistant
2326
import androidx.compose.material.icons.filled.Clear
2427
import androidx.compose.material.icons.filled.Delete
2528
import androidx.compose.material.icons.filled.Folder
29+
import androidx.compose.material.icons.filled.Memory
2630
import androidx.compose.material.icons.filled.Settings
2731
import androidx.compose.material3.DropdownMenu
2832
import androidx.compose.material3.DropdownMenuItem
33+
import androidx.compose.material3.HorizontalDivider
2934
import androidx.compose.material3.Icon
35+
import androidx.compose.material3.MaterialTheme
3036
import androidx.compose.material3.Text
3137
import androidx.compose.runtime.Composable
3238
import androidx.compose.runtime.getValue
39+
import androidx.compose.ui.Modifier
3340
import androidx.compose.ui.platform.LocalContext
3441
import androidx.compose.ui.res.stringResource
42+
import androidx.compose.ui.unit.dp
3543
import androidx.lifecycle.compose.collectAsStateWithLifecycle
3644
import io.shubham0204.smollmandroid.R
3745
import io.shubham0204.smollmandroid.ui.components.createAlertDialog
@@ -42,46 +50,39 @@ fun ChatMoreOptionsPopup(
4250
onEditChatSettingsClick: () -> Unit,
4351
) {
4452
val expanded by viewModel.showMoreOptionsPopupState.collectAsStateWithLifecycle()
53+
val showRAMUsageLabel by viewModel.showRAMUsageLabel.collectAsStateWithLifecycle()
4554
val context = LocalContext.current
4655
DropdownMenu(
4756
expanded = expanded,
4857
onDismissRequest = { viewModel.hideMoreOptionsPopup() },
4958
) {
5059
DropdownMenuItem(
5160
leadingIcon = { Icon(Icons.Default.Settings, contentDescription = "Edit Chat Name") },
52-
text = { Text(stringResource(R.string.chat_options_edit_settings)) },
61+
text = { Text(stringResource(R.string.chat_options_edit_settings), style = MaterialTheme.typography.labelMedium) },
5362
onClick = {
5463
onEditChatSettingsClick()
5564
viewModel.hideMoreOptionsPopup()
5665
},
5766
)
5867
DropdownMenuItem(
5968
leadingIcon = { Icon(Icons.Default.Folder, contentDescription = "Change Folder") },
60-
text = { Text(stringResource(R.string.chat_options_change_folder)) },
69+
text = { Text(stringResource(R.string.chat_options_change_folder), style = MaterialTheme.typography.labelMedium) },
6170
onClick = {
6271
viewModel.showChangeFolderDialog()
6372
viewModel.hideMoreOptionsPopup()
6473
},
6574
)
6675
DropdownMenuItem(
6776
leadingIcon = { Icon(Icons.Default.Assistant, contentDescription = "Change Model") },
68-
text = { Text(stringResource(R.string.chat_options_change_model)) },
77+
text = { Text(stringResource(R.string.chat_options_change_model), style = MaterialTheme.typography.labelMedium) },
6978
onClick = {
7079
viewModel.showSelectModelListDialog()
7180
viewModel.hideMoreOptionsPopup()
7281
},
7382
)
74-
DropdownMenuItem(
75-
leadingIcon = { Icon(Icons.AutoMirrored.Filled.ShortText, contentDescription = "Context Usage") },
76-
text = { Text(stringResource(R.string.chat_options_ctx_length_usage)) },
77-
onClick = {
78-
viewModel.showContextLengthUsageDialog()
79-
viewModel.hideMoreOptionsPopup()
80-
},
81-
)
8283
DropdownMenuItem(
8384
leadingIcon = { Icon(Icons.Default.Delete, contentDescription = "Delete Chat") },
84-
text = { Text(stringResource(R.string.dialog_title_delete_chat)) },
85+
text = { Text(stringResource(R.string.dialog_title_delete_chat), style = MaterialTheme.typography.labelMedium) },
8586
onClick = {
8687
viewModel.currChatState.value?.let { chat ->
8788
createAlertDialog(
@@ -106,7 +107,7 @@ fun ChatMoreOptionsPopup(
106107
)
107108
DropdownMenuItem(
108109
leadingIcon = { Icon(Icons.Default.Clear, contentDescription = "Clear Chat Messages") },
109-
text = { Text(stringResource(R.string.chat_options_clear_messages)) },
110+
text = { Text(stringResource(R.string.chat_options_clear_messages), style = MaterialTheme.typography.labelMedium) },
110111
onClick = {
111112
viewModel.currChatState.value?.let { chat ->
112113
createAlertDialog(
@@ -129,5 +130,29 @@ fun ChatMoreOptionsPopup(
129130
viewModel.hideMoreOptionsPopup()
130131
},
131132
)
133+
Spacer(modifier = Modifier.height(4.dp))
134+
HorizontalDivider(modifier = Modifier.fillMaxWidth())
135+
Spacer(modifier = Modifier.height(4.dp))
136+
DropdownMenuItem(
137+
leadingIcon = { Icon(Icons.AutoMirrored.Filled.ShortText, contentDescription = "Context Usage") },
138+
text = { Text(stringResource(R.string.chat_options_ctx_length_usage), style = MaterialTheme.typography.labelMedium) },
139+
onClick = {
140+
viewModel.showContextLengthUsageDialog()
141+
viewModel.hideMoreOptionsPopup()
142+
},
143+
)
144+
DropdownMenuItem(
145+
leadingIcon = { Icon(Icons.Default.Memory, contentDescription = "RAM Usage") },
146+
text = {
147+
Text(
148+
if (showRAMUsageLabel) "Hide RAM usage" else "Show RAM usage",
149+
style = MaterialTheme.typography.labelMedium,
150+
)
151+
},
152+
onClick = {
153+
viewModel.toggleRAMUsageLabelVisibility()
154+
viewModel.hideMoreOptionsPopup()
155+
},
156+
)
132157
}
133158
}

app/src/main/java/io/shubham0204/smollmandroid/ui/screens/chat/ChatScreenViewModel.kt

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
package io.shubham0204.smollmandroid.ui.screens.chat
1818

1919
import android.annotation.SuppressLint
20+
import android.app.ActivityManager
21+
import android.app.ActivityManager.MemoryInfo
2022
import android.content.Context
2123
import android.graphics.Color
2224
import android.text.util.Linkify
@@ -48,7 +50,9 @@ import kotlinx.coroutines.flow.Flow
4850
import kotlinx.coroutines.flow.MutableStateFlow
4951
import kotlinx.coroutines.flow.StateFlow
5052
import org.koin.android.annotation.KoinViewModel
53+
import java.lang.Math.pow
5154
import java.util.Date
55+
import kotlin.math.pow
5256

5357
private const val LOGTAG = "[SmolLMAndroid-Kt]"
5458
private val LOGD: (String) -> Unit = { Log.d(LOGTAG, it) }
@@ -92,6 +96,9 @@ class ChatScreenViewModel(
9296
private val _showTaskListBottomListState = MutableStateFlow(false)
9397
val showTaskListBottomListState: StateFlow<Boolean> = _showTaskListBottomListState
9498

99+
private val _showRAMUsageLabel = MutableStateFlow(false)
100+
val showRAMUsageLabel: StateFlow<Boolean> = _showRAMUsageLabel
101+
95102
// Used to pre-set a value in the query text-field of the chat screen
96103
// It is set when a query comes from a 'share-text' intent in ChatActivity
97104
var questionTextDefaultVal: String? = null
@@ -103,8 +110,11 @@ class ChatScreenViewModel(
103110
var responseGenerationTimeSecs: Int? = null
104111
val markwon: Markwon
105112

113+
private var activityManager: ActivityManager
114+
106115
init {
107116
_currChatState.value = appDB.loadDefaultChat()
117+
activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
108118
val prism4j = Prism4j(PrismGrammarLocator())
109119
markwon =
110120
Markwon
@@ -331,6 +341,19 @@ class ChatScreenViewModel(
331341
false
332342
}
333343

344+
/**
345+
* Get the current memory usage of the device.
346+
* This method returns the memory consumed (in GBs) and the total
347+
* memory available on the device (in GBs)
348+
*/
349+
fun getCurrentMemoryUsage(): Pair<Float, Float> {
350+
val memoryInfo = MemoryInfo()
351+
activityManager.getMemoryInfo(memoryInfo)
352+
val totalMemory = (memoryInfo.totalMem) / 1024.0.pow(3.0)
353+
val usedMemory = (memoryInfo.availMem) / 1024.0.pow(3.0)
354+
return Pair(usedMemory.toFloat(), totalMemory.toFloat())
355+
}
356+
334357
@SuppressLint("StringFormatMatches")
335358
fun showContextLengthUsageDialog() {
336359
_currChatState.value?.let { chat ->
@@ -381,4 +404,8 @@ class ChatScreenViewModel(
381404
fun hideTaskListBottomList() {
382405
_showTaskListBottomListState.value = false
383406
}
407+
408+
fun toggleRAMUsageLabelVisibility() {
409+
_showRAMUsageLabel.value = !_showRAMUsageLabel.value
410+
}
384411
}

app/src/main/res/values-zh-rCN/strings.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,4 +116,5 @@
116116
<string name="dialog_edit_folder_name_title">编辑文件夹名称</string>
117117
<string name="dialog_edit_folder_button_text">编辑</string>
118118
<string name="edit_chat_message_done">完成</string>
119+
<string name="label_device_ram">设备内存:%.2f GB 已用,总计 %.2f GB</string>
119120
</resources>

app/src/main/res/values/strings.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,4 +101,5 @@
101101
<string name="dialog_edit_folder_name_title">Edit Folder Name</string>
102102
<string name="dialog_edit_folder_button_text">Edit</string>
103103
<string name="edit_chat_message_done">Done</string>
104+
<string name="label_device_ram">Device RAM: %.2f GB used of %.2f GB total</string>
104105
</resources>

0 commit comments

Comments
 (0)