feat: Initial implementation of low battery notifications for nodes (#1653)

* Add initial implementation of low battery notifications for locally-connected and favorite nodes

* Hopefully make detekt happy

* detekt pls.

* Deduplicate some of the code

* detekt'd
pull/1659/head
Ken Piper 2025-03-08 04:52:30 -06:00 zatwierdzone przez GitHub
rodzic f7731f0c98
commit c7c29cb1c1
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: B5690EEEBB952194
3 zmienionych plików z 145 dodań i 1 usunięć

Wyświetl plik

@ -213,6 +213,9 @@ class MeshService : Service(), Logging {
private var locationFlow: Job? = null
private var mqttMessageFlow: Job? = null
private val batteryPercentUnsupported = 0.0
private val batteryPercentLowThreshold = 20.0
private fun getSenderName(packet: DataPacket?): String {
val name = nodeDBbyID[packet?.from]?.user?.longName
return name ?: getString(R.string.unknown_username)
@ -939,7 +942,16 @@ class MeshService : Service(), Logging {
}
updateNodeInfo(fromNum) {
when {
t.hasDeviceMetrics() -> it.deviceTelemetry = t
t.hasDeviceMetrics() -> {
it.deviceTelemetry = t
val isRemote = (fromNum != myNodeNum)
if (fromNum == myNodeNum || (isRemote && it.isFavorite)) {
if (t.deviceMetrics.voltage > batteryPercentUnsupported &&
t.deviceMetrics.batteryLevel < batteryPercentLowThreshold) {
serviceNotifications.showOrUpdateLowBatteryNotification(it, isRemote)
}
}
}
t.hasEnvironmentMetrics() -> it.environmentTelemetry = t
t.hasPowerMetrics() -> it.powerTelemetry = t
}

Wyświetl plik

@ -65,6 +65,8 @@ class MeshServiceNotifications(
createMessageNotificationChannel()
createAlertNotificationChannel()
createNewNodeNotificationChannel()
createLowBatteryNotificationChannel()
createLowBatteryRemoteNotificationChannel()
}
}
@ -173,6 +175,60 @@ class MeshServiceNotifications(
return channelId
}
@RequiresApi(Build.VERSION_CODES.O)
private fun createLowBatteryNotificationChannel(): String {
val channelId = "low_battery"
if (notificationManager.getNotificationChannel(channelId) == null) {
val channelName = context.getString(R.string.meshtastic_low_battery_notifications)
val channel = NotificationChannel(
channelId,
channelName,
NotificationManager.IMPORTANCE_HIGH
).apply {
lightColor = notificationLightColor
lockscreenVisibility = Notification.VISIBILITY_PUBLIC
setShowBadge(true)
setSound(
RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION),
AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_NOTIFICATION)
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.build()
)
}
notificationManager.createNotificationChannel(channel)
}
return channelId
}
// FIXME, Once we get a dedicated settings page in the app, this function should be removed and
// the feature should be implemented in the regular low battery notification stuff
@RequiresApi(Build.VERSION_CODES.O)
private fun createLowBatteryRemoteNotificationChannel(): String {
val channelId = "low_battery_remote"
if (notificationManager.getNotificationChannel(channelId) == null) {
val channelName = context.getString(R.string.meshtastic_low_battery_temporary_remote_notifications)
val channel = NotificationChannel(
channelId,
channelName,
NotificationManager.IMPORTANCE_HIGH
).apply {
lightColor = notificationLightColor
lockscreenVisibility = Notification.VISIBILITY_PUBLIC
setShowBadge(true)
setSound(
RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION),
AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_NOTIFICATION)
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.build()
)
}
notificationManager.createNotificationChannel(channel)
}
return channelId
}
private val channelId: String by lazy {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
createNotificationChannel()
@ -209,6 +265,24 @@ class MeshServiceNotifications(
}
}
private val lowBatteryChannelId: String by lazy {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
createLowBatteryNotificationChannel()
} else {
""
}
}
// FIXME, Once we get a dedicated settings page in the app, this function should be removed and
// the feature should be implemented in the regular low battery notification stuff
private val lowBatteryRemoteChannelId: String by lazy {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
createLowBatteryRemoteNotificationChannel()
} else {
""
}
}
private fun LocalStats?.formatToString(): String = this?.allFields?.mapNotNull { (k, v) ->
when (k.name) {
"num_online_nodes", "num_total_nodes" -> return@mapNotNull null
@ -258,6 +332,13 @@ class MeshServiceNotifications(
)
}
fun showOrUpdateLowBatteryNotification(node: NodeEntity, isRemote: Boolean) {
notificationManager.notify(
node.num, // show unique notifications
createLowBatteryNotification(node, isRemote)
)
}
private val openAppIntent: PendingIntent by lazy {
PendingIntent.getActivity(
context,
@ -409,4 +490,51 @@ class MeshServiceNotifications(
}
return newNodeSeenNotificationBuilder.build()
}
lateinit var lowBatteryRemoteNotificationBuilder: NotificationCompat.Builder
lateinit var lowBatteryNotificationBuilder: NotificationCompat.Builder
private fun createLowBatteryNotification(node: NodeEntity, isRemote: Boolean): Notification {
val tempNotificationBuilder: NotificationCompat.Builder = if (isRemote) {
if (!::lowBatteryRemoteNotificationBuilder.isInitialized) {
lowBatteryRemoteNotificationBuilder = commonBuilder(lowBatteryChannelId)
}
lowBatteryRemoteNotificationBuilder
} else {
if (!::lowBatteryNotificationBuilder.isInitialized) {
lowBatteryNotificationBuilder = commonBuilder(lowBatteryRemoteChannelId)
}
lowBatteryNotificationBuilder
}
with(tempNotificationBuilder) {
priority = NotificationCompat.PRIORITY_DEFAULT
setCategory(Notification.CATEGORY_STATUS)
setAutoCancel(true)
setShowWhen(true)
setOnlyAlertOnce(true)
setWhen(System.currentTimeMillis())
setContentTitle(
context.getString(R.string.low_battery_title).format(
node.shortName
)
)
val message = context.getString(R.string.low_battery_message).format(
node.longName,
node.deviceMetrics.batteryLevel
)
message.let {
setContentText(it)
setStyle(
NotificationCompat.BigTextStyle()
.bigText(it),
)
}
}
if (isRemote) {
lowBatteryRemoteNotificationBuilder = tempNotificationBuilder
return lowBatteryRemoteNotificationBuilder.build()
} else {
lowBatteryNotificationBuilder = tempNotificationBuilder
return lowBatteryNotificationBuilder.build()
}
}
}

Wyświetl plik

@ -337,4 +337,8 @@
<string name="are_you_sure">Are you sure?</string>
<string name="router_role_confirmation_text"><![CDATA[I have read the <a href="https://meshtastic.org/docs/configuration/radio/device/#roles">Device Role Documentation</a> and the blog post about <a href="http://meshtastic.org/blog/choosing-the-right-device-role">Choosing The Right Device Role</a>.]]></string>
<string name="i_know_what_i_m_doing">I know what I\'m doing.</string>
<string name="low_battery_message">Node %s has a low battery (%d%%)</string>
<string name="meshtastic_low_battery_notifications">Low battery notifications</string>
<string name="low_battery_title">Low battery: %s</string>
<string name="meshtastic_low_battery_temporary_remote_notifications">Low battery notifications (favorite nodes)</string>
</resources>