Skip to content

Commit 0798797

Browse files
feature: CPU
1 parent ca72e74 commit 0798797

File tree

8 files changed

+99
-60
lines changed

8 files changed

+99
-60
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
package io.github.openflocon.flocondesktop.device
22

3+
import io.github.openflocon.flocondesktop.device.pages.cpu.CpuViewModel
34
import io.github.openflocon.flocondesktop.device.pages.permission.PermissionViewModel
45
import org.koin.core.module.dsl.viewModelOf
56
import org.koin.dsl.module
67

78
internal val deviceModule = module {
89
viewModelOf(::DeviceViewModel)
910
viewModelOf(::PermissionViewModel)
11+
viewModelOf(::CpuViewModel)
1012
}

FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/device/DeviceScreen.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import io.github.openflocon.domain.device.models.DeviceId
2424
import io.github.openflocon.flocondesktop.device.models.DeviceUiState
2525
import io.github.openflocon.flocondesktop.device.models.previewDeviceUiState
2626
import io.github.openflocon.flocondesktop.device.pages.BatteryPage
27-
import io.github.openflocon.flocondesktop.device.pages.CpuPage
27+
import io.github.openflocon.flocondesktop.device.pages.cpu.CpuPage
2828
import io.github.openflocon.flocondesktop.device.pages.InfoPage
2929
import io.github.openflocon.flocondesktop.device.pages.MemoryPage
3030
import io.github.openflocon.flocondesktop.device.pages.permission.PermissionPage
@@ -110,7 +110,7 @@ private fun Content(
110110
when (tabs[index]) {
111111
DeviceTab.INFORMATION -> InfoPage(uiState.infoState)
112112
DeviceTab.BATTERY -> BatteryPage(uiState.batteryState)
113-
DeviceTab.CPU -> CpuPage(uiState.cpuState, onAction)
113+
DeviceTab.CPU -> CpuPage(uiState.deviceSerial)
114114
DeviceTab.MEMORY -> MemoryPage(uiState.memoryState)
115115
DeviceTab.PERMISSION -> PermissionPage(uiState.deviceSerial)
116116
}

FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/device/DeviceViewModel.kt

Lines changed: 3 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,8 @@ import androidx.lifecycle.viewModelScope
55
import io.github.openflocon.domain.adb.usecase.GetDeviceSerialUseCase
66
import io.github.openflocon.domain.adb.usecase.SendCommandUseCase
77
import io.github.openflocon.domain.common.getOrNull
8-
import io.github.openflocon.domain.device.usecase.GetCurrentDeviceIdAndPackageNameUseCase
98
import io.github.openflocon.flocondesktop.device.models.BatteryUiState
109
import io.github.openflocon.flocondesktop.device.models.ContentUiState
11-
import io.github.openflocon.flocondesktop.device.models.CpuItem
12-
import io.github.openflocon.flocondesktop.device.models.CpuUiState
1310
import io.github.openflocon.flocondesktop.device.models.DeviceUiState
1411
import io.github.openflocon.flocondesktop.device.models.InfoUiState
1512
import io.github.openflocon.flocondesktop.device.models.MemoryItem
@@ -25,8 +22,7 @@ import kotlinx.coroutines.launch
2522
internal class DeviceViewModel(
2623
val deviceId: String,
2724
val sendCommandUseCase: SendCommandUseCase,
28-
val deviceSerialUseCase: GetDeviceSerialUseCase,
29-
val currentDeviceAppsUseCase: GetCurrentDeviceIdAndPackageNameUseCase
25+
val deviceSerialUseCase: GetDeviceSerialUseCase
3026
) : ViewModel() {
3127

3228
private val contentState = MutableStateFlow(ContentUiState(selectedTab = DeviceTab.entries.first()))
@@ -63,24 +59,21 @@ internal class DeviceViewModel(
6359
)
6460
)
6561
private val memoryState = MutableStateFlow(MemoryUiState(emptyList()))
66-
private val cpuState = MutableStateFlow(CpuUiState(emptyList()))
6762
private val deviceSerial = MutableStateFlow("")
6863

6964
val uiState = combine(
7065
contentState,
7166
infoState,
7267
memoryState,
73-
cpuState,
7468
batteryState,
7569
deviceSerial
7670
) { states ->
7771
DeviceUiState(
7872
contentState = states[0] as ContentUiState,
7973
infoState = states[1] as InfoUiState,
8074
memoryState = states[2] as MemoryUiState,
81-
cpuState = states[3] as CpuUiState,
82-
batteryState = states[4] as BatteryUiState,
83-
deviceSerial = states[5] as String
75+
batteryState = states[3] as BatteryUiState,
76+
deviceSerial = states[4] as String
8477
)
8578
}
8679
.stateIn(
@@ -90,7 +83,6 @@ internal class DeviceViewModel(
9083
contentState = contentState.value,
9184
infoState = infoState.value,
9285
memoryState = memoryState.value,
93-
cpuState = cpuState.value,
9486
batteryState = batteryState.value,
9587
deviceSerial = deviceSerial.value
9688
)
@@ -116,7 +108,6 @@ internal class DeviceViewModel(
116108
}
117109

118110
private fun onRefresh() {
119-
refreshCpu()
120111
refreshMemory()
121112
refreshBattery()
122113
deviceInfo()
@@ -147,38 +138,6 @@ internal class DeviceViewModel(
147138
.removeSuffix("\n")
148139
}
149140

150-
private fun refreshCpu() {
151-
viewModelScope.launch(Dispatchers.IO) {
152-
val output = sendCommand("shell", "dumpsys", "cpuinfo")
153-
val regex = CPU_REGEX.toRegex()
154-
val items = output.lineSequence()
155-
.mapNotNull { regex.find(it) }
156-
.mapNotNull {
157-
try {
158-
val packageName = it.groupValues[2].split("/")
159-
160-
CpuItem(
161-
cpuUsage = it.groupValues[1].toDoubleOrNull() ?: return@mapNotNull null,
162-
packageName = packageName[1],
163-
pId = packageName[0].toIntOrNull() ?: return@mapNotNull null,
164-
userPercentage = it.groupValues[3].toDoubleOrNull() ?: return@mapNotNull null,
165-
kernelPercentage = it.groupValues[4].toDoubleOrNull() ?: return@mapNotNull null,
166-
minorFaults = it.groupValues[5].toIntOrNull(),
167-
majorFaults = it.groupValues[6].toIntOrNull()
168-
)
169-
} catch (e: NumberFormatException) {
170-
// Handle parsing errors gracefully (e.g., log the error)
171-
null
172-
}
173-
}
174-
.sortedByDescending(CpuItem::cpuUsage)
175-
.distinctBy(CpuItem::packageName)
176-
.toList()
177-
178-
cpuState.update { it.copy(list = items) }
179-
}
180-
}
181-
182141
private fun refreshMemory() {
183142
viewModelScope.launch(Dispatchers.IO) {
184143
val output = sendCommand("shell", "dumpsys", "meminfo")
@@ -253,10 +212,6 @@ internal class DeviceViewModel(
253212

254213
companion object {
255214

256-
// CPU
257-
private const val CPU_REGEX =
258-
"""(\d+(?:\.\d+)?)%\s+([^:]+):\s+(\d+(?:\.\d+)?)%\s+user\s+\+\s+(\d+(?:\.\d+)?)%\s+kernel\s+/ faults:\s+(\d+)\s+minor\s+(\d+)\s+major"""
259-
260215
// MEM
261216
private const val MEM_REGEX = """([\d,]+)K:\s+([a-zA-Z0-9._:-]+)\s+$${"pid"}\s+(\d+)(?:\s+/\s+([a-zA-Z\s]+))?$"""
262217

FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/device/models/DeviceUiState.kt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,13 @@ data class DeviceUiState(
77
val deviceSerial: String,
88
val contentState: ContentUiState,
99
val infoState: InfoUiState,
10-
val cpuState: CpuUiState,
1110
val memoryState: MemoryUiState,
1211
val batteryState: BatteryUiState
1312
)
1413

1514
internal fun previewDeviceUiState() = DeviceUiState(
1615
deviceSerial = "",
1716
contentState = previewContentUiState(),
18-
cpuState = previewCpuUiState(),
1917
memoryState = previewMemoryUiState(),
2018
infoState = previewInfoUiState(),
2119
batteryState = previewBatteryUiState()
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package io.github.openflocon.flocondesktop.device.pages.cpu
2+
3+
sealed interface CpuAction
Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package io.github.openflocon.flocondesktop.device.pages
1+
package io.github.openflocon.flocondesktop.device.pages.cpu
22

33
import androidx.compose.foundation.background
44
import androidx.compose.foundation.border
@@ -11,22 +11,37 @@ import androidx.compose.foundation.lazy.LazyColumn
1111
import androidx.compose.foundation.lazy.itemsIndexed
1212
import androidx.compose.material3.Text
1313
import androidx.compose.runtime.Composable
14+
import androidx.compose.runtime.getValue
1415
import androidx.compose.ui.Alignment
1516
import androidx.compose.ui.Modifier
1617
import androidx.compose.ui.draw.clip
1718
import androidx.compose.ui.text.TextStyle
1819
import androidx.compose.ui.text.font.FontWeight
1920
import androidx.compose.ui.text.style.TextAlign
2021
import androidx.compose.ui.unit.dp
21-
import io.github.openflocon.flocondesktop.device.DeviceAction
22-
import io.github.openflocon.flocondesktop.device.models.CpuUiState
22+
import androidx.lifecycle.compose.collectAsStateWithLifecycle
2323
import io.github.openflocon.library.designsystem.FloconTheme
2424
import io.github.openflocon.library.designsystem.components.FloconHorizontalDivider
25+
import org.koin.compose.viewmodel.koinViewModel
26+
import org.koin.core.parameter.parametersOf
2527

2628
@Composable
2729
internal fun CpuPage(
28-
state: CpuUiState,
29-
onAction: (DeviceAction) -> Unit
30+
deviceSerial: String
31+
) {
32+
val viewModel = koinViewModel<CpuViewModel> { parametersOf(deviceSerial) }
33+
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
34+
35+
Content(
36+
uiState = uiState,
37+
onAction = viewModel::onAction
38+
)
39+
}
40+
41+
@Composable
42+
private fun Content(
43+
uiState: CpuUiState,
44+
onAction: (CpuAction) -> Unit
3045
) {
3146
LazyColumn(
3247
modifier = Modifier
@@ -56,7 +71,7 @@ internal fun CpuPage(
5671
)
5772
}
5873
itemsIndexed(
59-
items = state.list,
74+
items = uiState.list,
6075
key = { _, item -> item.pId }
6176
) { index, item ->
6277
BasicItem(
@@ -69,7 +84,7 @@ internal fun CpuPage(
6984
minorFaults = item.minorFaults?.toString().orEmpty(),
7085
style = FloconTheme.typography.labelSmall
7186
)
72-
if (index != state.list.lastIndex) {
87+
if (index != uiState.list.lastIndex) {
7388
FloconHorizontalDivider(
7489
color = FloconTheme.colorPalette.secondary
7590
)
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package io.github.openflocon.flocondesktop.device.models
1+
package io.github.openflocon.flocondesktop.device.pages.cpu
22

33
import androidx.compose.runtime.Immutable
44

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package io.github.openflocon.flocondesktop.device.pages.cpu
2+
3+
import androidx.lifecycle.viewModelScope
4+
import io.github.openflocon.domain.adb.usecase.SendCommandUseCase
5+
import io.github.openflocon.flocondesktop.device.PageViewModel
6+
import kotlinx.coroutines.Dispatchers
7+
import kotlinx.coroutines.flow.MutableStateFlow
8+
import kotlinx.coroutines.flow.asStateFlow
9+
import kotlinx.coroutines.flow.update
10+
import kotlinx.coroutines.launch
11+
12+
class CpuViewModel(
13+
deviceSerial: String,
14+
sendCommandUseCase: SendCommandUseCase
15+
) : PageViewModel(deviceSerial, sendCommandUseCase) {
16+
17+
private val _uiState = MutableStateFlow(CpuUiState(emptyList()))
18+
val uiState = _uiState.asStateFlow()
19+
20+
init {
21+
refreshCpu()
22+
}
23+
24+
fun onAction(action: CpuAction) {
25+
26+
}
27+
28+
private fun refreshCpu() {
29+
viewModelScope.launch(Dispatchers.IO) {
30+
val output = sendCommand("shell", "dumpsys", "cpuinfo")
31+
val regex = CPU_REGEX.toRegex()
32+
val items = output.lineSequence()
33+
.mapNotNull { regex.find(it) }
34+
.mapNotNull {
35+
try {
36+
val packageName = it.groupValues[2].split("/")
37+
38+
CpuItem(
39+
cpuUsage = it.groupValues[1].toDoubleOrNull() ?: return@mapNotNull null,
40+
packageName = packageName[1],
41+
pId = packageName[0].toIntOrNull() ?: return@mapNotNull null,
42+
userPercentage = it.groupValues[3].toDoubleOrNull() ?: return@mapNotNull null,
43+
kernelPercentage = it.groupValues[4].toDoubleOrNull() ?: return@mapNotNull null,
44+
minorFaults = it.groupValues[5].toIntOrNull(),
45+
majorFaults = it.groupValues[6].toIntOrNull()
46+
)
47+
} catch (e: NumberFormatException) {
48+
// Handle parsing errors gracefully (e.g., log the error)
49+
null
50+
}
51+
}
52+
.sortedByDescending(CpuItem::cpuUsage)
53+
.distinctBy(CpuItem::packageName)
54+
.toList()
55+
56+
_uiState.update { it.copy(list = items) }
57+
}
58+
}
59+
60+
companion object {
61+
// CPU
62+
private const val CPU_REGEX =
63+
"""(\d+(?:\.\d+)?)%\s+([^:]+):\s+(\d+(?:\.\d+)?)%\s+user\s+\+\s+(\d+(?:\.\d+)?)%\s+kernel\s+/ faults:\s+(\d+)\s+minor\s+(\d+)\s+major"""
64+
}
65+
66+
}

0 commit comments

Comments
 (0)