feat: show unique messaging notifications per contact (#1381)

* Show unique notifications per contact

Instead of a single notification for all messages, each contact now has its own, unique notification. This uses the `NotificationCompat.MessagingStyle` and the contact's name to create distinct notifications, enhancing message organization.

* feat: Add notification tap action to open contacts tab

This is done by:
- Adding an intent extra to the notification with the contact key for future use to navigate to the message thread.
- Adding a new action to the MainActivity to handle the intent.
- Updating the message notification to include the intent.

* Open message notification to the correct conversation

Adds an extra to the message notification intent to open the correct conversation. This ensures that when a user taps on a message notification, they are taken to the conversation with the sender of that message.
pull/1384/head
James Rich 2024-11-04 16:05:39 -06:00 zatwierdzone przez GitHub
rodzic eea62e6533
commit 80e915a36c
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: B5690EEEBB952194
3 zmienionych plików z 45 dodań i 14 usunięć

Wyświetl plik

@ -270,6 +270,14 @@ class MainActivity : AppCompatActivity(), Logging {
// We now wait for the device to connect, once connected, we ask the user if they want to switch to the new channel // We now wait for the device to connect, once connected, we ask the user if they want to switch to the new channel
} }
MeshServiceNotifications.OPEN_MESSAGE_ACTION -> {
val contactKey =
intent.getStringExtra(MeshServiceNotifications.OPEN_MESSAGE_EXTRA_CONTACT_KEY)
val contactName =
intent.getStringExtra(MeshServiceNotifications.OPEN_MESSAGE_EXTRA_CONTACT_NAME)
showMessages(contactKey, contactName)
}
UsbManager.ACTION_USB_DEVICE_ATTACHED -> { UsbManager.ACTION_USB_DEVICE_ATTACHED -> {
showSettingsPage() showSettingsPage()
} }
@ -599,6 +607,13 @@ class MainActivity : AppCompatActivity(), Logging {
binding.pager.currentItem = 5 binding.pager.currentItem = 5
} }
private fun showMessages(contactKey: String?, contactName: String?) {
model.setCurrentTab(0)
if (contactKey != null && contactName != null) {
supportFragmentManager.navigateToMessages(contactKey, contactName)
}
}
override fun onCreateOptionsMenu(menu: Menu): Boolean { override fun onCreateOptionsMenu(menu: Menu): Boolean {
// Inflate the menu; this adds items to the action bar if it is present. // Inflate the menu; this adds items to the action bar if it is present.
menuInflater.inflate(R.menu.menu_main, menu) menuInflater.inflate(R.menu.menu_main, menu)

Wyświetl plik

@ -247,7 +247,7 @@ class MeshService : Service(), Logging {
startPacketQueue() startPacketQueue()
} }
private fun updateMessageNotification(dataPacket: DataPacket) { private fun updateMessageNotification(contactKey: String, dataPacket: DataPacket) {
val message: String = when (dataPacket.dataType) { val message: String = when (dataPacket.dataType) {
Portnums.PortNum.TEXT_MESSAGE_APP_VALUE -> dataPacket.text!! Portnums.PortNum.TEXT_MESSAGE_APP_VALUE -> dataPacket.text!!
Portnums.PortNum.WAYPOINT_APP_VALUE -> { Portnums.PortNum.WAYPOINT_APP_VALUE -> {
@ -256,7 +256,7 @@ class MeshService : Service(), Logging {
else -> return else -> return
} }
serviceNotifications.updateMessageNotification(getSenderName(dataPacket), message) serviceNotifications.updateMessageNotification(contactKey, getSenderName(dataPacket), message)
} }
override fun onCreate() { override fun onCreate() {
@ -627,7 +627,7 @@ class MeshService : Service(), Logging {
packetRepository.get().apply { packetRepository.get().apply {
insert(packetToSave) insert(packetToSave)
val isMuted = getContactSettings(contactKey).isMuted val isMuted = getContactSettings(contactKey).isMuted
if (updateNotification && !isMuted) updateMessageNotification(dataPacket) if (updateNotification && !isMuted) updateMessageNotification(contactKey, dataPacket)
} }
} }
} }

Wyświetl plik

@ -12,6 +12,7 @@ import android.media.RingtoneManager
import android.os.Build import android.os.Build
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import androidx.core.app.Person
import com.geeksville.mesh.MainActivity import com.geeksville.mesh.MainActivity
import com.geeksville.mesh.R import com.geeksville.mesh.R
import com.geeksville.mesh.TelemetryProtos.LocalStats import com.geeksville.mesh.TelemetryProtos.LocalStats
@ -29,13 +30,16 @@ class MeshServiceNotifications(
companion object { companion object {
private const val FIFTEEN_MINUTES_IN_MILLIS = 15L * 60 * 1000 private const val FIFTEEN_MINUTES_IN_MILLIS = 15L * 60 * 1000
const val OPEN_MESSAGE_ACTION = "com.geeksville.mesh.OPEN_MESSAGE_ACTION"
const val OPEN_MESSAGE_EXTRA_CONTACT_KEY = "com.geeksville.mesh.OPEN_MESSAGE_EXTRA_CONTACT_KEY"
const val OPEN_MESSAGE_EXTRA_CONTACT_NAME =
"com.geeksville.mesh.OPEN_MESSAGE_EXTRA_CONTACT_NAME"
} }
private val notificationManager: NotificationManager get() = context.notificationManager private val notificationManager: NotificationManager get() = context.notificationManager
// We have two notification channels: one for general service status and another one for messages // We have two notification channels: one for general service status and another one for messages
val notifyId = 101 val notifyId = 101
private val messageNotifyId = 102
@RequiresApi(Build.VERSION_CODES.O) @RequiresApi(Build.VERSION_CODES.O)
private fun createNotificationChannel(): String { private fun createNotificationChannel(): String {
@ -165,10 +169,10 @@ class MeshServiceNotifications(
) )
} }
fun updateMessageNotification(name: String, message: String) = fun updateMessageNotification(contactKey: String, name: String, message: String) =
notificationManager.notify( notificationManager.notify(
messageNotifyId, contactKey.hashCode(), // show unique notifications,
createMessageNotification(name, message) createMessageNotification(contactKey, name, message)
) )
fun showNewNodeSeenNotification(node: NodeEntity) { fun showNewNodeSeenNotification(node: NodeEntity) {
@ -187,6 +191,21 @@ class MeshServiceNotifications(
) )
} }
private fun openMessageIntent(contactKey: String, contactName: String): PendingIntent {
val intent = Intent(context, MainActivity::class.java)
intent.action = OPEN_MESSAGE_ACTION
intent.putExtra(OPEN_MESSAGE_EXTRA_CONTACT_KEY, contactKey)
intent.putExtra(OPEN_MESSAGE_EXTRA_CONTACT_NAME, contactName)
val pendingIntent = PendingIntent.getActivity(
context,
0,
intent,
PendingIntentCompat.FLAG_IMMUTABLE
)
return pendingIntent
}
private fun commonBuilder(channel: String): NotificationCompat.Builder { private fun commonBuilder(channel: String): NotificationCompat.Builder {
val builder = NotificationCompat.Builder(context, channel) val builder = NotificationCompat.Builder(context, channel)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC) .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
@ -246,22 +265,19 @@ class MeshServiceNotifications(
} }
lateinit var messageNotificationBuilder: NotificationCompat.Builder lateinit var messageNotificationBuilder: NotificationCompat.Builder
private fun createMessageNotification(name: String, message: String): Notification { private fun createMessageNotification(contactKey: String, name: String, message: String): Notification {
if (!::messageNotificationBuilder.isInitialized) { if (!::messageNotificationBuilder.isInitialized) {
messageNotificationBuilder = commonBuilder(messageChannelId) messageNotificationBuilder = commonBuilder(messageChannelId)
} }
val person = Person.Builder().setName(name).build()
with(messageNotificationBuilder) { with(messageNotificationBuilder) {
setContentIntent(openMessageIntent(contactKey, name))
priority = NotificationCompat.PRIORITY_DEFAULT priority = NotificationCompat.PRIORITY_DEFAULT
setCategory(Notification.CATEGORY_MESSAGE) setCategory(Notification.CATEGORY_MESSAGE)
setAutoCancel(true) setAutoCancel(true)
setContentTitle(name)
setContentText(message)
setStyle( setStyle(
NotificationCompat.BigTextStyle() NotificationCompat.MessagingStyle(person).addMessage(message, System.currentTimeMillis(), person)
.bigText(message),
) )
setWhen(System.currentTimeMillis())
setShowWhen(true)
} }
return messageNotificationBuilder.build() return messageNotificationBuilder.build()
} }