feat: Add notification for new node seen (#1316)

* Add notification for new node seen

This change adds a new notification that is displayed when a new node is seen.
The notification includes the node's
 name.
It also updates the message notification to include the node name.

* Show individual notification for each new node

* Add new nodes notifications

Adds a new notification channel for new nodes seen.
This channel is set to high importance with sound and light.
This commit is contained in:
James Rich
2024-10-15 04:07:22 -05:00
committed by GitHub
parent af56fff216
commit f6ec3e8bab
3 changed files with 68 additions and 0 deletions

View File

@@ -803,6 +803,8 @@ class MeshService : Service(), Logging {
// Update our DB of users based on someone sending out a User subpacket
private fun handleReceivedUser(fromNum: Int, p: MeshProtos.User, channel: Int = 0) {
updateNodeInfo(fromNum) {
val newNode = (it.isUnknownUser && p.hwModel != MeshProtos.HardwareModel.UNSET)
val keyMatch = !it.hasPKC || it.user.publicKey == p.publicKey
it.user = if (keyMatch) p else p.copy {
warn("Public key mismatch from $longName ($shortName)")
@@ -811,6 +813,9 @@ class MeshService : Service(), Logging {
it.longName = p.longName
it.shortName = p.shortName
it.channel = channel
if (newNode) {
serviceNotifications.showNewNodeSeenNotification(it)
}
}
}

View File

@@ -19,12 +19,14 @@ import com.geeksville.mesh.MainActivity
import com.geeksville.mesh.R
import com.geeksville.mesh.TelemetryProtos.LocalStats
import com.geeksville.mesh.android.notificationManager
import com.geeksville.mesh.database.entity.NodeEntity
import com.geeksville.mesh.util.PendingIntentCompat
import java.io.Closeable
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
@Suppress("TooManyFunctions")
class MeshServiceNotifications(
private val context: Context
) : Closeable {
@@ -81,6 +83,30 @@ class MeshServiceNotifications(
return channelId
}
@RequiresApi(Build.VERSION_CODES.O)
private fun createNewNodeNotificationChannel(): String {
val channelId = "new_nodes"
val channelName = context.getString(R.string.meshtastic_new_nodes_notifications)
val channel = NotificationChannel(
channelId,
channelName,
NotificationManager.IMPORTANCE_HIGH
).apply {
lightColor = Color.BLUE
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()
@@ -101,6 +127,14 @@ class MeshServiceNotifications(
}
}
private val newNodeChannelId: String by lazy {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
createNewNodeNotificationChannel()
} else {
""
}
}
private fun formatStatsString(stats: LocalStats?, currentStatsUpdatedAtMillis: Long?): String {
val updatedAt = "Next update at: ${
currentStatsUpdatedAtMillis?.let {
@@ -139,6 +173,13 @@ class MeshServiceNotifications(
createMessageNotification(name, message)
)
fun showNewNodeSeenNotification(node: NodeEntity) {
notificationManager.notify(
node.num, // show unique notifications
createNewNodeSeenNotification(node.user.shortName, node.user.longName)
)
}
private val openAppIntent: PendingIntent by lazy {
PendingIntent.getActivity(
context,
@@ -224,6 +265,27 @@ class MeshServiceNotifications(
return messageNotificationBuilder.build()
}
lateinit var newNodeSeenNotificationBuilder: NotificationCompat.Builder
private fun createNewNodeSeenNotification(name: String, message: String? = null): Notification {
if (!::newNodeSeenNotificationBuilder.isInitialized) {
newNodeSeenNotificationBuilder = commonBuilder(newNodeChannelId)
}
with(newNodeSeenNotificationBuilder) {
priority = NotificationCompat.PRIORITY_DEFAULT
setCategory(Notification.CATEGORY_STATUS)
setAutoCancel(true)
setContentTitle("New Node Seen: $name")
message?.let {
setContentText(it)
setStyle(
NotificationCompat.BigTextStyle()
.bigText(message),
)
}
}
return newNodeSeenNotificationBuilder.build()
}
override fun close() {
largeIcon?.recycle()
largeIcon = null

View File

@@ -271,4 +271,5 @@
<string name="encryption_error">Public key mismatch</string>
<string name="encryption_error_text">The public key does not match the recorded key. You may remove the node and let it exchange keys again, but this may indicate a more serious security problem. Contact the user through another trusted channel, to determine if the key change was due to a factory reset or other intentional action.</string>
<string name="request_userinfo">Request user info</string>
<string name="meshtastic_new_nodes_notifications">New nodes notifications</string>
</resources>