diff --git a/app/app/src/main/java/mx/paputec/rachita/platform/battery/BatteryStatusMonitor.kt b/app/app/src/main/java/mx/paputec/rachita/platform/battery/BatteryStatusMonitor.kt new file mode 100644 index 0000000..bb7f35c --- /dev/null +++ b/app/app/src/main/java/mx/paputec/rachita/platform/battery/BatteryStatusMonitor.kt @@ -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 = 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) +}