feat(battery): monitor de batería por ACTION_BATTERY_CHANGED

callbackFlow sobre el sticky broadcast (sin polling, CLAUDE.md §8).
Alimenta el chip DA-08 del dashboard y es la base del módulo 3.
This commit is contained in:
Johann
2026-07-03 22:41:43 -06:00
parent 28f2359dda
commit 33de656931
@@ -0,0 +1,43 @@
package mx.paputec.rachita.platform.battery
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.BatteryManager
import javax.inject.Inject
import javax.inject.Singleton
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.distinctUntilChanged
import mx.paputec.rachita.domain.model.BatteryStatus
/**
* Real battery level + charging state from ACTION_BATTERY_CHANGED (sticky
* broadcast — no polling, CLAUDE.md §8). Feeds the dashboard chip (DA-08) now
* and the low-battery/charging-lock flows of module 3 later.
*/
@Singleton
class BatteryStatusMonitor @Inject constructor(
private val context: Context,
) {
val status: Flow<BatteryStatus> = callbackFlow {
val receiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
trySend(intent.toBatteryStatus())
}
}
val sticky = context.registerReceiver(receiver, IntentFilter(Intent.ACTION_BATTERY_CHANGED))
sticky?.let { trySend(it.toBatteryStatus()) }
awaitClose { context.unregisterReceiver(receiver) }
}.distinctUntilChanged()
}
private fun Intent.toBatteryStatus(): BatteryStatus {
val level = getIntExtra(BatteryManager.EXTRA_LEVEL, -1)
val scale = getIntExtra(BatteryManager.EXTRA_SCALE, -1)
val percent = if (level >= 0 && scale > 0) (level * 100) / scale else 100
val plugged = getIntExtra(BatteryManager.EXTRA_PLUGGED, 0)
return BatteryStatus(percent = percent, isCharging = plugged != 0)
}