util: implement performance monitor; wrap it with a viewmodel
This commit is contained in:
parent
4dd755e25b
commit
46bd638c5f
|
|
@ -0,0 +1,161 @@
|
|||
package com.example.llama.revamp.monitoring
|
||||
|
||||
import android.app.ActivityManager
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.os.BatteryManager
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
/**
|
||||
* Service that monitors device performance metrics such as memory usage,
|
||||
* battery level, and temperature.
|
||||
*/
|
||||
class PerformanceMonitor(private val context: Context) {
|
||||
|
||||
/**
|
||||
* Provides a flow of memory usage information that updates at the specified interval.
|
||||
*/
|
||||
fun monitorMemoryUsage(intervalMs: Long = 5000): Flow<MemoryMetrics> = flow {
|
||||
while(true) {
|
||||
emit(getMemoryInfo())
|
||||
delay(intervalMs)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a flow of battery information that updates at the specified interval.
|
||||
*/
|
||||
fun monitorBattery(intervalMs: Long = 10000): Flow<BatteryMetrics> = flow {
|
||||
while(true) {
|
||||
emit(getBatteryInfo())
|
||||
delay(intervalMs)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a flow of temperature information that updates at the specified interval.
|
||||
*/
|
||||
fun monitorTemperature(intervalMs: Long = 10000): Flow<TemperatureMetrics> = flow {
|
||||
while(true) {
|
||||
emit(getTemperatureInfo())
|
||||
delay(intervalMs)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current memory usage information.
|
||||
*/
|
||||
private suspend fun getMemoryInfo(): MemoryMetrics = withContext(Dispatchers.IO) {
|
||||
val mi = ActivityManager.MemoryInfo()
|
||||
val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
|
||||
activityManager.getMemoryInfo(mi)
|
||||
|
||||
val availableMem = mi.availMem
|
||||
val totalMem = mi.totalMem
|
||||
val percentUsed = ((totalMem - availableMem) / totalMem.toFloat() * 100).roundToInt()
|
||||
|
||||
// Convert to more readable units (GB)
|
||||
val availableGb = (availableMem / (1024.0 * 1024.0 * 1024.0)).toFloat().round(1)
|
||||
val totalGb = (totalMem / (1024.0 * 1024.0 * 1024.0)).toFloat().round(1)
|
||||
|
||||
MemoryMetrics(
|
||||
availableMem = availableMem,
|
||||
totalMem = totalMem,
|
||||
percentUsed = percentUsed,
|
||||
availableGb = availableGb,
|
||||
totalGb = totalGb
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current battery information.
|
||||
*/
|
||||
private fun getBatteryInfo(): BatteryMetrics {
|
||||
val intent = context.registerReceiver(null,
|
||||
IntentFilter(Intent.ACTION_BATTERY_CHANGED))
|
||||
|
||||
val level = intent?.getIntExtra(BatteryManager.EXTRA_LEVEL, 0) ?: 0
|
||||
val scale = intent?.getIntExtra(BatteryManager.EXTRA_SCALE, 100) ?: 100
|
||||
val batteryPct = level * 100 / scale
|
||||
|
||||
val status = intent?.getIntExtra(BatteryManager.EXTRA_STATUS, -1) ?: -1
|
||||
val isCharging = status == BatteryManager.BATTERY_STATUS_CHARGING ||
|
||||
status == BatteryManager.BATTERY_STATUS_FULL
|
||||
|
||||
return BatteryMetrics(
|
||||
level = batteryPct,
|
||||
isCharging = isCharging
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current temperature information.
|
||||
*/
|
||||
private fun getTemperatureInfo(): TemperatureMetrics {
|
||||
val intent = context.registerReceiver(null,
|
||||
IntentFilter(Intent.ACTION_BATTERY_CHANGED))
|
||||
|
||||
// Battery temperature is reported in tenths of a degree Celsius
|
||||
val tempTenthsC = intent?.getIntExtra(BatteryManager.EXTRA_TEMPERATURE, 0) ?: 0
|
||||
val tempC = tempTenthsC / 10.0f
|
||||
|
||||
val warningLevel = when {
|
||||
tempC >= 45.0f -> TemperatureWarningLevel.HIGH
|
||||
tempC >= 40.0f -> TemperatureWarningLevel.MEDIUM
|
||||
else -> TemperatureWarningLevel.NORMAL
|
||||
}
|
||||
|
||||
return TemperatureMetrics(
|
||||
temperature = tempC,
|
||||
warningLevel = warningLevel
|
||||
)
|
||||
}
|
||||
|
||||
private fun Float.round(decimals: Int): Float {
|
||||
var multiplier = 1.0f
|
||||
repeat(decimals) { multiplier *= 10 }
|
||||
return (this * multiplier).roundToInt() / multiplier
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Data class containing memory usage metrics.
|
||||
*/
|
||||
data class MemoryMetrics(
|
||||
val availableMem: Long,
|
||||
val totalMem: Long,
|
||||
val percentUsed: Int,
|
||||
val availableGb: Float,
|
||||
val totalGb: Float
|
||||
)
|
||||
|
||||
/**
|
||||
* Data class containing battery information.
|
||||
*/
|
||||
data class BatteryMetrics(
|
||||
val level: Int,
|
||||
val isCharging: Boolean
|
||||
)
|
||||
|
||||
/**
|
||||
* Warning levels for temperature.
|
||||
*/
|
||||
enum class TemperatureWarningLevel {
|
||||
NORMAL,
|
||||
MEDIUM,
|
||||
HIGH
|
||||
}
|
||||
|
||||
/**
|
||||
* Data class containing temperature information.
|
||||
*/
|
||||
data class TemperatureMetrics(
|
||||
val temperature: Float,
|
||||
val warningLevel: TemperatureWarningLevel
|
||||
)
|
||||
|
|
@ -0,0 +1,148 @@
|
|||
package com.example.llama.revamp.viewmodel
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.example.llama.revamp.data.preferences.UserPreferences
|
||||
import com.example.llama.revamp.monitoring.BatteryMetrics
|
||||
import com.example.llama.revamp.monitoring.MemoryMetrics
|
||||
import com.example.llama.revamp.monitoring.PerformanceMonitor
|
||||
import com.example.llama.revamp.monitoring.TemperatureMetrics
|
||||
import com.example.llama.revamp.monitoring.TemperatureWarningLevel
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
/**
|
||||
* ViewModel that manages performance monitoring for the app.
|
||||
*/
|
||||
class PerformanceViewModel(
|
||||
private val performanceMonitor: PerformanceMonitor,
|
||||
private val userPreferences: UserPreferences
|
||||
) : ViewModel() {
|
||||
|
||||
// Memory usage metrics
|
||||
private val _memoryUsage = MutableStateFlow(MemoryMetrics(0, 0, 0, 0f, 0f))
|
||||
val memoryUsage: StateFlow<MemoryMetrics> = _memoryUsage.asStateFlow()
|
||||
|
||||
// Battery information
|
||||
private val _batteryInfo = MutableStateFlow(BatteryMetrics(0, false))
|
||||
val batteryInfo: StateFlow<BatteryMetrics> = _batteryInfo.asStateFlow()
|
||||
|
||||
// Temperature information
|
||||
private val _temperatureInfo = MutableStateFlow(TemperatureMetrics(0f, TemperatureWarningLevel.NORMAL))
|
||||
val temperatureInfo: StateFlow<TemperatureMetrics> = _temperatureInfo.asStateFlow()
|
||||
|
||||
// User preferences
|
||||
private val _isMonitoringEnabled = MutableStateFlow(true)
|
||||
val isMonitoringEnabled: StateFlow<Boolean> = _isMonitoringEnabled.asStateFlow()
|
||||
|
||||
private val _useFahrenheitUnit = MutableStateFlow(false)
|
||||
val useFahrenheitUnit: StateFlow<Boolean> = _useFahrenheitUnit.asStateFlow()
|
||||
|
||||
private val _monitoringInterval = MutableStateFlow(5000L)
|
||||
val monitoringInterval: StateFlow<Long> = _monitoringInterval.asStateFlow()
|
||||
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
// Load user preferences
|
||||
_isMonitoringEnabled.value = userPreferences.isPerformanceMonitoringEnabled().first()
|
||||
_useFahrenheitUnit.value = userPreferences.usesFahrenheitTemperature().first()
|
||||
_monitoringInterval.value = userPreferences.getMonitoringInterval().first()
|
||||
|
||||
// Start monitoring if enabled
|
||||
if (_isMonitoringEnabled.value) {
|
||||
startMonitoring()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts monitoring device performance.
|
||||
*/
|
||||
private fun startMonitoring() {
|
||||
val interval = _monitoringInterval.value
|
||||
|
||||
viewModelScope.launch {
|
||||
performanceMonitor.monitorMemoryUsage(interval).collect { metrics ->
|
||||
_memoryUsage.value = metrics
|
||||
}
|
||||
}
|
||||
|
||||
viewModelScope.launch {
|
||||
performanceMonitor.monitorBattery(interval * 2).collect { metrics ->
|
||||
_batteryInfo.value = metrics
|
||||
}
|
||||
}
|
||||
|
||||
viewModelScope.launch {
|
||||
performanceMonitor.monitorTemperature(interval * 2).collect { metrics ->
|
||||
_temperatureInfo.value = metrics
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether performance monitoring is enabled.
|
||||
*/
|
||||
fun setMonitoringEnabled(enabled: Boolean) {
|
||||
viewModelScope.launch {
|
||||
userPreferences.setPerformanceMonitoringEnabled(enabled)
|
||||
_isMonitoringEnabled.value = enabled
|
||||
|
||||
if (enabled && !isMonitoringActive()) {
|
||||
startMonitoring()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the temperature unit preference.
|
||||
*/
|
||||
fun setUseFahrenheitUnit(useFahrenheit: Boolean) {
|
||||
viewModelScope.launch {
|
||||
userPreferences.setUseFahrenheitTemperature(useFahrenheit)
|
||||
_useFahrenheitUnit.value = useFahrenheit
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the monitoring interval.
|
||||
*/
|
||||
fun setMonitoringInterval(intervalMs: Long) {
|
||||
viewModelScope.launch {
|
||||
userPreferences.setMonitoringInterval(intervalMs)
|
||||
_monitoringInterval.value = intervalMs
|
||||
|
||||
// Restart monitoring with new interval if active
|
||||
if (isMonitoringActive()) {
|
||||
startMonitoring()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if monitoring is currently active.
|
||||
*/
|
||||
private fun isMonitoringActive(): Boolean {
|
||||
return _isMonitoringEnabled.value
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory for creating PerformanceViewModel instances.
|
||||
*/
|
||||
class Factory(
|
||||
private val performanceMonitor: PerformanceMonitor,
|
||||
private val userPreferences: UserPreferences
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
if (modelClass.isAssignableFrom(PerformanceViewModel::class.java)) {
|
||||
return PerformanceViewModel(performanceMonitor, userPreferences) as T
|
||||
}
|
||||
throw IllegalArgumentException("Unknown ViewModel class")
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue