Skip to content

Commit

Permalink
Battery alerts (#303)
Browse files Browse the repository at this point in the history
  • Loading branch information
valterc authored Feb 16, 2025
1 parent 5b90d95 commit b4b49fb
Show file tree
Hide file tree
Showing 8 changed files with 191 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.valterc.ki2.karoo
import android.content.Context
import com.valterc.ki2.input.ActionManager
import com.valterc.ki2.karoo.audio.AudioManager
import com.valterc.ki2.karoo.device.KarooSensorDeviceTracking
import com.valterc.ki2.karoo.service.ServiceClient
import com.valterc.ki2.karoo.shifting.ShiftCountHandler
import io.hammerhead.karooext.KarooSystemService
Expand All @@ -14,4 +15,5 @@ class Ki2ExtensionContext(val extension: String, val context: Context) {
val audioManager: AudioManager = AudioManager(this)
val actionManager: ActionManager = ActionManager(this)
val shiftCountHandler: ShiftCountHandler = ShiftCountHandler(this)
val karooDeviceTracking: KarooSensorDeviceTracking = KarooSensorDeviceTracking()
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import android.content.ComponentName
import android.content.Intent
import com.valterc.ki2.BuildConfig
import com.valterc.ki2.data.device.DeviceId
import com.valterc.ki2.karoo.battery.BatteryAlertHandler
import com.valterc.ki2.karoo.datatypes.text.FrontGearIndexDataType
import com.valterc.ki2.karoo.datatypes.text.FrontGearSizeDataType
import com.valterc.ki2.karoo.datatypes.text.FrontShiftCountDataType
Expand All @@ -24,7 +25,7 @@ import com.valterc.ki2.karoo.datatypes.visual.GearsIndexVisualDataType
import com.valterc.ki2.karoo.datatypes.visual.GearsSizeVisualDataType
import com.valterc.ki2.karoo.overlay.OverlayWindowHandler
import com.valterc.ki2.karoo.shifting.ShiftingAudioAlertHandler
import com.valterc.ki2.karoo.shifting.ShiftingDevice
import com.valterc.ki2.karoo.device.ShiftingDevice
import com.valterc.ki2.karoo.update.UpdateHandler
import io.hammerhead.karooext.extension.KarooExtension
import io.hammerhead.karooext.internal.Emitter
Expand Down Expand Up @@ -94,6 +95,7 @@ class Ki2ExtensionService : KarooExtension("ki2", BuildConfig.VERSION_NAME) {
handlers.add(UpdateHandler(extensionContext))
handlers.add(OverlayWindowHandler(this, extensionContext))
handlers.add(ShiftingAudioAlertHandler(extensionContext))
handlers.add(BatteryAlertHandler(extensionContext))
}
}
}
Expand All @@ -116,7 +118,9 @@ class Ki2ExtensionService : KarooExtension("ki2", BuildConfig.VERSION_NAME) {

override fun connectDevice(uid: String, emitter: Emitter<DeviceEvent>) {
Timber.i("[%s] Device connect", uid)
ShiftingDevice(extensionContext, DeviceId(uid)).connect(emitter)
val deviceId = DeviceId(uid)
extensionContext.karooDeviceTracking.deviceConnect(deviceId)
ShiftingDevice(extensionContext, deviceId).connect(emitter)
}

override fun onDestroy() {
Expand Down
14 changes: 14 additions & 0 deletions app/src/main/java/com/valterc/ki2/karoo/audio/AudioManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,20 @@ class AudioManager(private val context: Ki2ExtensionContext) {
)
}

fun playKarooDeviceWarning() {
context.karooSystem.dispatch(
PlayBeepPattern(
listOf(
PlayBeepPattern.Tone(1975.f, 125.d),
PlayBeepPattern.Tone(null, 125.d),
PlayBeepPattern.Tone(1975.f, 125.d),
PlayBeepPattern.Tone(null, 125.d),
PlayBeepPattern.Tone(1975.f, 125.d)
)
)
)
}

fun playLowestGearAudioAlert() {
tryTriggerAudioAlert {
playAudio(audioAlertLowestGear)
Expand Down
135 changes: 135 additions & 0 deletions app/src/main/java/com/valterc/ki2/karoo/battery/BatteryAlertHandler.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
package com.valterc.ki2.karoo.battery

import com.valterc.ki2.R
import com.valterc.ki2.data.device.BatteryInfo
import com.valterc.ki2.data.device.DeviceId
import com.valterc.ki2.data.device.DeviceName
import com.valterc.ki2.data.preferences.PreferencesView
import com.valterc.ki2.karoo.Ki2ExtensionContext
import com.valterc.ki2.karoo.RideHandler
import io.hammerhead.karooext.models.InRideAlert
import io.hammerhead.karooext.models.RideState
import io.hammerhead.karooext.models.SystemNotification
import java.util.function.BiConsumer
import java.util.function.Consumer

class BatteryAlertHandler(extensionContext: Ki2ExtensionContext) : RideHandler(extensionContext) {

private var batteryLevelLow: Int? = null
private var batteryLevelCritical: Int? = null
private val notificationMap = mutableMapOf<DeviceId, BatteryAlertRecord>()

private val preferencesConsumer = Consumer<PreferencesView> { preferences ->
batteryLevelLow = preferences.getBatteryLevelLow(extensionContext.context)
batteryLevelCritical = preferences.getBatteryLevelCritical(extensionContext.context)

notificationMap.values.forEach { batteryRecord ->
checkBatteryAndNotify(batteryRecord)
}
}

private val batteryConsumer = BiConsumer<DeviceId, BatteryInfo> { deviceId, batteryInfo ->
notificationMap[deviceId]?.let { batteryRecord ->
batteryRecord.batteryInfo = batteryInfo
} ?: run {
notificationMap[deviceId] = BatteryAlertRecord(deviceId, batteryInfo)
}

notificationMap[deviceId]?.let { batteryRecord ->
checkBatteryAndNotify(batteryRecord)
}
}

init {
extensionContext.serviceClient.registerUnfilteredBatteryInfoWeakListener(batteryConsumer)
extensionContext.serviceClient.registerPreferencesWeakListener(preferencesConsumer)
}

override fun onRideStart() {
notificationMap.values.forEach { batteryRecord ->
checkBatteryAndNotify(batteryRecord)
}
}

override fun onRideResume() {
notificationMap.values.forEach { batteryRecord ->
checkBatteryAndNotify(batteryRecord)
}
}

override fun onRideEnd() {
notificationMap.values.forEach { batteryRecord ->
batteryRecord.alertedInRide = false
}
}

private fun checkBatteryAndNotify(batteryAlertRecord: BatteryAlertRecord) {
if (extensionContext.karooDeviceTracking.isDeviceConnected(batteryAlertRecord.deviceId)) {
// If the device was added to Karoo, let Karoo handle the battery notification
return
}

batteryAlertRecord.batteryInfo?.let { batteryInfo ->
val batteryLevelCritical = batteryLevelCritical
val batteryLevelLow = batteryLevelLow

if (batteryLevelCritical != null && batteryInfo.value <= batteryLevelCritical) {
notify(batteryAlertRecord, batteryInfo, BatteryAlertType.Critical)
} else if (batteryLevelLow != null && batteryInfo.value <= batteryLevelLow) {
notify(batteryAlertRecord, batteryInfo, BatteryAlertType.Low)
}
}
}

private fun notify(
batteryAlertRecord: BatteryAlertRecord,
batteryInfo: BatteryInfo,
batteryAlertType: BatteryAlertType
) {
if (batteryAlertRecord.alert == batteryAlertType && batteryAlertRecord.alertedInRide) {
return
}

val devicePreferences =
extensionContext.serviceClient.getDevicePreferences(batteryAlertRecord.deviceId)
val deviceName =
devicePreferences?.getName(extensionContext.context) ?: DeviceName.getDefaultName(
extensionContext.context,
batteryAlertRecord.deviceId
)
val title = extensionContext.context.getString(R.string.text_di2_low_battery)
val detail = extensionContext.context.getString(
R.string.text_param_low_battery,
deviceName,
batteryInfo.value
)

if (!batteryAlertRecord.alertedInRide && rideState is RideState.Recording) {
batteryAlertRecord.alertedInRide = true
extensionContext.karooSystem.dispatch(
InRideAlert(
"ki2-ride-alert-battery-${batteryAlertRecord.deviceId.uid}",
R.drawable.ic_hh_battery,
title,
detail,
12_000,
backgroundColor = R.color.hh_red_600,
textColor = R.color.white
)
)
extensionContext.audioManager.playKarooDeviceWarning()
}

extensionContext.karooSystem.dispatch(
SystemNotification(
"ki2-notification-battery-${batteryAlertRecord.deviceId.uid}",
detail,
header = title,
style = if (batteryAlertType == BatteryAlertType.Critical) SystemNotification.Style.ERROR else SystemNotification.Style.EVENT
)
)

batteryAlertRecord.alert = batteryAlertType
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.valterc.ki2.karoo.battery

import com.valterc.ki2.data.device.BatteryInfo
import com.valterc.ki2.data.device.DeviceId

class BatteryAlertRecord(val deviceId: DeviceId,
var batteryInfo: BatteryInfo? = null,
var alert : BatteryAlertType? = null,
var alertedInRide: Boolean = false)
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.valterc.ki2.karoo.battery

enum class BatteryAlertType {
Low,
Critical
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.valterc.ki2.karoo.device

import com.valterc.ki2.data.device.DeviceId

class KarooSensorDeviceTracking {

private val deviceMap = mutableMapOf<DeviceId, Boolean>()

fun deviceConnect(uid: DeviceId) {
deviceMap[uid] = true
}

fun isDeviceConnected(uid: DeviceId) : Boolean {
return deviceMap[uid] == true
}

}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.valterc.ki2.karoo.shifting
package com.valterc.ki2.karoo.device

import com.valterc.ki2.data.connection.ConnectionInfo
import com.valterc.ki2.data.connection.ConnectionStatus
Expand All @@ -10,6 +10,7 @@ import com.valterc.ki2.data.preferences.device.DevicePreferencesView
import com.valterc.ki2.data.shifting.ShiftingInfo
import com.valterc.ki2.karoo.Ki2ExtensionContext
import com.valterc.ki2.karoo.datatypes.Ki2DataType
import com.valterc.ki2.karoo.shifting.ShiftingGearingHelper
import io.hammerhead.karooext.internal.Emitter
import io.hammerhead.karooext.models.BatteryStatus
import io.hammerhead.karooext.models.ConnectionStatus as KarooConnectionStatus
Expand Down

0 comments on commit b4b49fb

Please sign in to comment.