@@ -6,11 +6,13 @@ import io.github.openflocon.domain.adb.usecase.GetDeviceSerialUseCase
66import io.github.openflocon.domain.adb.usecase.SendCommandUseCase
77import io.github.openflocon.domain.common.getOrNull
88import io.github.openflocon.domain.device.usecase.GetCurrentDeviceIdAndPackageNameUseCase
9+ import io.github.openflocon.flocondesktop.device.models.BatteryUiState
910import io.github.openflocon.flocondesktop.device.models.ContentUiState
1011import io.github.openflocon.flocondesktop.device.models.CpuItem
1112import io.github.openflocon.flocondesktop.device.models.CpuUiState
1213import io.github.openflocon.flocondesktop.device.models.DeviceUiState
1314import io.github.openflocon.flocondesktop.device.models.InfoUiState
15+ import io.github.openflocon.flocondesktop.device.models.MemoryItem
1416import io.github.openflocon.flocondesktop.device.models.MemoryUiState
1517import io.github.openflocon.flocondesktop.device.models.PermissionItem
1618import io.github.openflocon.flocondesktop.device.models.PermissionUiState
@@ -40,6 +42,28 @@ internal class DeviceViewModel(
4042 battery = " "
4143 )
4244 )
45+ private val batteryState = MutableStateFlow (
46+ BatteryUiState (
47+ acPowered = false ,
48+ usbPowered = false ,
49+ wirelessPowered = false ,
50+ dockPowered = false ,
51+ maxChargingCurrent = 0 ,
52+ maxChargingVoltage = 0 ,
53+ chargeCounter = 0 ,
54+ status = 0 ,
55+ health = 0 ,
56+ present = false ,
57+ level = 0 ,
58+ scale = 0 ,
59+ voltage = 0 ,
60+ temperature = 0 ,
61+ technology = " " ,
62+ chargingState = 0 ,
63+ chargingPolicy = 0 ,
64+ capacityLevel = 0
65+ )
66+ )
4367 private val memoryState = MutableStateFlow (MemoryUiState (emptyList()))
4468 private val cpuState = MutableStateFlow (CpuUiState (emptyList()))
4569 private val permissionState = MutableStateFlow (PermissionUiState (emptyList()))
@@ -49,14 +73,16 @@ internal class DeviceViewModel(
4973 infoState,
5074 memoryState,
5175 cpuState,
52- permissionState
53- ) { content, info, memory, cpu, permission ->
76+ permissionState,
77+ batteryState
78+ ) { states ->
5479 DeviceUiState (
55- contentState = content,
56- infoState = info,
57- memoryState = memory,
58- cpuState = cpu,
59- permissionState = permission
80+ contentState = states[0 ] as ContentUiState ,
81+ infoState = states[1 ] as InfoUiState ,
82+ memoryState = states[2 ] as MemoryUiState ,
83+ cpuState = states[3 ] as CpuUiState ,
84+ permissionState = states[4 ] as PermissionUiState ,
85+ batteryState = states[5 ] as BatteryUiState
6086 )
6187 }
6288 .stateIn(
@@ -67,7 +93,8 @@ internal class DeviceViewModel(
6793 infoState = infoState.value,
6894 memoryState = memoryState.value,
6995 cpuState = cpuState.value,
70- permissionState = permissionState.value
96+ permissionState = permissionState.value,
97+ batteryState = batteryState.value
7198 )
7299 )
73100
@@ -106,10 +133,12 @@ internal class DeviceViewModel(
106133
107134 private fun onRefresh () {
108135 refreshCpu()
136+ refreshMemory()
137+ refreshBattery()
109138 deviceInfo()
110139 fetchPermission()
111140 viewModelScope.launch {
112- // battery = sendCommand("shell", "dumpsys", "battery"),
141+ // battery = sendCommand("shell", "dumpsys", "battery"),
113142// mem = sendCommand("shell", "dumpsys", "meminfo")
114143 }
115144 }
@@ -203,9 +232,107 @@ internal class DeviceViewModel(
203232 }
204233 }
205234
235+ private fun refreshMemory () {
236+ viewModelScope.launch(Dispatchers .IO ) {
237+ val output = sendCommand(" shell" , " dumpsys" , " meminfo" )
238+ val regex = MEM_REGEX .toRegex()
239+ val items = output.lineSequence()
240+ .map { it.trim() }
241+ .mapNotNull { regex.find(it) }
242+ .mapNotNull {
243+ try {
244+ MemoryItem (
245+ memoryUsage = formatMemoryUsage(
246+ memoryUsageKB = it.groupValues[1 ].replace(" ," , " " ).toDoubleOrNull()
247+ ),
248+ processName = it.groupValues[2 ],
249+ pid = it.groupValues[3 ].toIntOrNull() ? : return @mapNotNull null
250+ )
251+ } catch (e: NumberFormatException ) {
252+ // Handle parsing errors gracefully (e.g., log the error)
253+ null
254+ }
255+ }
256+ .toList()
257+
258+ memoryState.update { it.copy(list = items) }
259+ }
260+ }
261+
262+ private fun formatMemoryUsage (memoryUsageKB : Double? ): String {
263+ if (memoryUsageKB == null ) {
264+ return " N/A" // Or handle null case as appropriate
265+ }
266+
267+ val memoryUsage = memoryUsageKB * 1024 // Convert KB to bytes
268+
269+ return when {
270+ memoryUsage < 1024 -> String .format(" %.2f B" , memoryUsage)
271+ memoryUsage < 1024 * 1024 -> String .format(" %.2f KB" , memoryUsage / 1024 )
272+ memoryUsage < 1024 * 1024 * 1024 -> String .format(" %.2f MB" , memoryUsage / (1024 * 1024 ))
273+ else -> String .format(" %.2f GB" , memoryUsage / (1024 * 1024 * 1024 ))
274+ }
275+ }
276+
277+ private fun refreshBattery () {
278+ viewModelScope.launch {
279+ val batteryInfo = sendCommand(" shell" , " dumpsys" , " battery" )
280+
281+ batteryState.update {
282+ it.copy(
283+ acPowered = AC_POWERED_REGEX .toRegex().find(batteryInfo)?.groupValues?.get(1 )?.toBoolean() ? : false ,
284+ usbPowered = USB_POWERED_REGEX .toRegex().find(batteryInfo)?.groupValues?.get(1 )?.toBoolean() ? : false ,
285+ wirelessPowered = WIRELESS_POWERED_REGEX .toRegex().find(batteryInfo)?.groupValues?.get(1 )?.toBoolean()
286+ ? : false ,
287+ dockPowered = DOCK_POWERED_REGEX .toRegex().find(batteryInfo)?.groupValues?.get(1 )?.toBoolean() ? : false ,
288+ maxChargingCurrent = MAX_CHARGING_CURRENT_REGEX .toRegex().find(batteryInfo)?.groupValues?.get(1 )?.toIntOrNull(),
289+ maxChargingVoltage = MAX_CHARGING_VOLTAGE_REGEX .toRegex().find(batteryInfo)?.groupValues?.get(1 )?.toIntOrNull(),
290+ chargeCounter = CHARGE_COUNTER_REGEX .toRegex().find(batteryInfo)?.groupValues?.get(1 )?.toIntOrNull(),
291+ status = STATUS_REGEX .toRegex().find(batteryInfo)?.groupValues?.get(1 )?.toIntOrNull(),
292+ health = HEALTH_REGEX .toRegex().find(batteryInfo)?.groupValues?.get(1 )?.toIntOrNull(),
293+ present = PRESENT_REGEX .toRegex().find(batteryInfo)?.groupValues?.get(1 )?.toBoolean() ? : false ,
294+ level = LEVEL_REGEX .toRegex().find(batteryInfo)?.groupValues?.get(1 )?.toIntOrNull(),
295+ scale = SCALE_REGEX .toRegex().find(batteryInfo)?.groupValues?.get(1 )?.toIntOrNull(),
296+ voltage = VOLTAGE_REGEX .toRegex().find(batteryInfo)?.groupValues?.get(1 )?.toIntOrNull(),
297+ temperature = TEMPERATURE_REGEX .toRegex().find(batteryInfo)?.groupValues?.get(1 )?.toIntOrNull(),
298+ technology = TECHNOLOGY_REGEX .toRegex().find(batteryInfo)?.groupValues?.get(1 ),
299+ chargingState = CHARGING_STATE_REGEX .toRegex().find(batteryInfo)?.groupValues?.get(1 )?.toIntOrNull(),
300+ chargingPolicy = CHARGING_POLICY_REGEX .toRegex().find(batteryInfo)?.groupValues?.get(1 )?.toIntOrNull(),
301+ capacityLevel = CAPACITY_LEVEL_REGEX .toRegex().find(batteryInfo)?.groupValues?.get(1 )?.toIntOrNull()
302+ )
303+ }
304+ }
305+ }
306+
206307 companion object {
207308 private const val PERMISSION_PREFIX = " android.permission."
208- private const val CPU_REGEX = """ (\d+(?:\.\d+)?)%\s+([^:]+):\s+(\d+(?:\.\d+)?)%\s+user\s+\+\s+(\d+(?:\.\d+)?)%\s+kernel\s+/ faults:\s+(\d+)\s+minor\s+(\d+)\s+major"""
309+
310+ // CPU
311+ private const val CPU_REGEX =
312+ """ (\d+(?:\.\d+)?)%\s+([^:]+):\s+(\d+(?:\.\d+)?)%\s+user\s+\+\s+(\d+(?:\.\d+)?)%\s+kernel\s+/ faults:\s+(\d+)\s+minor\s+(\d+)\s+major"""
313+
314+ // MEM
315+ private const val MEM_REGEX = """ ([\d,]+)K:\s+([a-zA-Z0-9._:-]+)\s+$${" pid" } \s+(\d+)(?:\s+/\s+([a-zA-Z\s]+))?$"""
316+
317+ // Battery
318+ private const val AC_POWERED_REGEX = """ AC powered:\s+(true|false)"""
319+ private const val USB_POWERED_REGEX = """ USB powered:\s+(true|false)"""
320+ private const val WIRELESS_POWERED_REGEX = """ Wireless powered:\s+(true|false)"""
321+ private const val DOCK_POWERED_REGEX = """ Dock powered:\s+(true|false)"""
322+ private const val MAX_CHARGING_CURRENT_REGEX = """ Max charging current:\s+(\d+)"""
323+ private const val MAX_CHARGING_VOLTAGE_REGEX = """ Max charging voltage:\s+(\d+)"""
324+ private const val CHARGE_COUNTER_REGEX = """ Charge counter:\s+(\d+)"""
325+ private const val STATUS_REGEX = """ status:\s+(\d+)"""
326+ private const val HEALTH_REGEX = """ health:\s+(\d+)"""
327+ private const val PRESENT_REGEX = """ present:\s+(true|false)"""
328+ private const val LEVEL_REGEX = """ level:\s+(\d+)"""
329+ private const val SCALE_REGEX = """ scale:\s+(\d+)"""
330+ private const val VOLTAGE_REGEX = """ voltage:\s+(\d+)"""
331+ private const val TEMPERATURE_REGEX = """ temperature:\s+(\d+)"""
332+ private const val TECHNOLOGY_REGEX = """ technology:\s+([a-zA-Z-]+)"""
333+ private const val CHARGING_STATE_REGEX = """ Charging state:\s+(\d+)"""
334+ private const val CHARGING_POLICY_REGEX = """ Charging policy:\s+(\d+)"""
335+ private const val CAPACITY_LEVEL_REGEX = """ Capacity level:\s+(\d+)"""
209336 }
210337
211338}
0 commit comments