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