feat(app-glasses): панель статуса — время + заряд (цвет по статусу)
Поверх видео (GL-оверлей в EglVideoRenderer: bitmap-текстура, правый верхний угол, обе поверхности) и композ-панель на экране соединения. Цвета (из удачной панели pair-view StatusComponent): CHARGING/FULL — зелёный, DISCHARGING — белый, NOT_CHARGING — красный. Данные: StatusPanelProvider (sticky ACTION_BATTERY_CHANGED + тик 1с, HH:mm). Тесты 4 (chargeColorArgb). Железо: «00:02 94%» + зелёный квадрат (очки на зарядке), скрин подтверждён.
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
package pw.binom.viewmate.glasses.status
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.os.BatteryManager
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
import kotlin.math.roundToInt
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
/** Данные панели статуса: время + заряд + цвет индикатора. */
|
||||
data class StatusPanelData(
|
||||
val time: String, // "23:51"
|
||||
val percent: Int, // 0..100
|
||||
val chargeColorArgb: Int, // цвет индикатора
|
||||
)
|
||||
|
||||
const val CHARGE_COLOR_CHARGING = 0xFF4CAF50.toInt() // зелёный — идёт заряд
|
||||
const val CHARGE_COLOR_DISCHARGING = 0xFFFFFFFF.toInt() // белый — разряд
|
||||
const val CHARGE_COLOR_PLUGGED_NOT_CHARGING = 0xFFE53935.toInt() // красный — подключен, но не заряжается
|
||||
|
||||
/**
|
||||
* Цвет индикатора: не подключен=белый, CHARGING=зелёный,
|
||||
* подключен-не-заряжается (NOT_CHARGING/FULL)=красный.
|
||||
*/
|
||||
fun chargeColorArgb(plugged: Boolean, status: Int): Int = when {
|
||||
!plugged -> CHARGE_COLOR_DISCHARGING
|
||||
status == BatteryManager.BATTERY_STATUS_CHARGING ||
|
||||
status == BatteryManager.BATTERY_STATUS_FULL -> CHARGE_COLOR_CHARGING
|
||||
else -> CHARGE_COLOR_PLUGGED_NOT_CHARGING
|
||||
}
|
||||
|
||||
/** Провайдер: обновляет раз в секунду (время) + батарея из ACTION_BATTERY_CHANGED. */
|
||||
class StatusPanelProvider(
|
||||
private val context: Context,
|
||||
private val onUpdate: (StatusPanelData) -> Unit,
|
||||
) {
|
||||
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Main)
|
||||
private val timeFormat = SimpleDateFormat("HH:mm", Locale.getDefault())
|
||||
|
||||
private var percent = 0
|
||||
private var plugged = false
|
||||
private var status = BatteryManager.BATTERY_STATUS_UNKNOWN
|
||||
private var running = false
|
||||
|
||||
fun start() {
|
||||
if (running) return
|
||||
running = true
|
||||
readBattery()
|
||||
scope.launch {
|
||||
while (isActive && running) {
|
||||
emit()
|
||||
delay(1_000)
|
||||
readBattery()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun stop() {
|
||||
if (!running) return
|
||||
running = false
|
||||
scope.coroutineContext.cancel()
|
||||
}
|
||||
|
||||
/** Sticky ACTION_BATTERY_CHANGED: level/scale/status/plugged. */
|
||||
private fun readBattery() {
|
||||
val intent = context.registerReceiver(null, IntentFilter(Intent.ACTION_BATTERY_CHANGED))
|
||||
?: return
|
||||
val level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1)
|
||||
val scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1)
|
||||
status = intent.getIntExtra(
|
||||
BatteryManager.EXTRA_STATUS, BatteryManager.BATTERY_STATUS_UNKNOWN
|
||||
)
|
||||
plugged = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0
|
||||
percent = if (scale > 0 && level >= 0) {
|
||||
(level * 100f / scale).roundToInt().coerceIn(0, 100)
|
||||
} else {
|
||||
level.coerceIn(0, 100)
|
||||
}
|
||||
}
|
||||
|
||||
private fun emit() {
|
||||
val time = timeFormat.format(Date())
|
||||
onUpdate(StatusPanelData(time, percent, chargeColorArgb(plugged, status)))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user