fix(notifications): merge FG-service and message notifications into one
Use a single notification ID (9999) for both idle foreground-service state and incoming-message alerts. Shows sender name for 1 message, summary for multiple senders. Cancelling resets to idle instead of removing. Closes #111
This commit is contained in:
parent
b7ef6af0a4
commit
9eefa79c64
2 changed files with 105 additions and 106 deletions
|
|
@ -73,7 +73,7 @@ internal class MessagingService : Service() {
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val NOTIFICATION_ID = 9999
|
internal const val NOTIFICATION_ID = 9999
|
||||||
|
|
||||||
fun start(context: Context) {
|
fun start(context: Context) {
|
||||||
val intent = Intent(context, MessagingService::class.java)
|
val intent = Intent(context, MessagingService::class.java)
|
||||||
|
|
|
||||||
|
|
@ -108,71 +108,38 @@ internal class NotificationHelper @Inject constructor(
|
||||||
): Boolean {
|
): Boolean {
|
||||||
if (shouldSuppressNotification(senderId)) return false
|
if (shouldSuppressNotification(senderId)) return false
|
||||||
|
|
||||||
val chatIntent = Intent(context, MainActivity::class.java).apply {
|
|
||||||
flags = Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_CLEAR_TOP
|
|
||||||
putExtra(EXTRA_RECIPIENT_ID, senderId)
|
|
||||||
putExtra(EXTRA_RECIPIENT_USERNAME, senderUsername)
|
|
||||||
}
|
|
||||||
|
|
||||||
val chatPendingIntent = PendingIntent.getActivity(
|
|
||||||
context,
|
|
||||||
senderId.hashCode(),
|
|
||||||
chatIntent,
|
|
||||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
|
||||||
)
|
|
||||||
|
|
||||||
val messagesIntent = Intent(context, MainActivity::class.java).apply {
|
|
||||||
flags = Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_CLEAR_TOP
|
|
||||||
putExtra(EXTRA_OPEN_MESSAGES, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
val messagesPendingIntent = PendingIntent.getActivity(
|
|
||||||
context,
|
|
||||||
SUMMARY_NOTIFICATION_ID,
|
|
||||||
messagesIntent,
|
|
||||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
|
||||||
)
|
|
||||||
|
|
||||||
val notification = NotificationCompat.Builder(context, CHANNEL_ID)
|
|
||||||
.setSmallIcon(R.drawable.ic_notification_message)
|
|
||||||
.setContentTitle("Neue Nachricht")
|
|
||||||
.setContentText("$senderUsername hat etwas geschrieben")
|
|
||||||
.setPriority(NotificationCompat.PRIORITY_HIGH)
|
|
||||||
.setAutoCancel(true)
|
|
||||||
.setContentIntent(chatPendingIntent)
|
|
||||||
.setGroup(GROUP_KEY)
|
|
||||||
.build()
|
|
||||||
|
|
||||||
val senderNotificationId = senderId.hashCode()
|
val senderNotificationId = senderId.hashCode()
|
||||||
val notificationManager = NotificationManagerCompat.from(context)
|
val (contentText, pendingIntent) = synchronized(activeSenderNotificationIds) {
|
||||||
try {
|
|
||||||
val (shouldShowSummary, summaryText) = synchronized(activeSenderNotificationIds) {
|
|
||||||
activeSenderNotificationIds.add(senderNotificationId)
|
activeSenderNotificationIds.add(senderNotificationId)
|
||||||
activeSenderNamesById[senderNotificationId] = senderUsername
|
activeSenderNamesById[senderNotificationId] = senderUsername
|
||||||
|
val names = activeSenderNamesById.values.toList()
|
||||||
|
if (activeSenderNotificationIds.size == 1) {
|
||||||
Pair(
|
Pair(
|
||||||
activeSenderNotificationIds.size > 1,
|
"Neue Nachricht von $senderUsername",
|
||||||
buildSummaryText(activeSenderNamesById.values.toList())
|
buildChatPendingIntent(senderId, senderUsername)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
Pair(
|
||||||
|
buildSummaryText(names),
|
||||||
|
buildMessagesPendingIntent()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val summaryNotification = NotificationCompat.Builder(context, CHANNEL_ID)
|
val messageNotification = NotificationCompat.Builder(context, CHANNEL_ID)
|
||||||
.setSmallIcon(R.drawable.ic_notification_message)
|
.setSmallIcon(R.drawable.ic_notification_message)
|
||||||
.setContentTitle("Neue Nachrichten")
|
.setContentTitle("Bollwerk")
|
||||||
.setContentText(summaryText)
|
.setContentText(contentText)
|
||||||
.setGroup(GROUP_KEY)
|
.setPriority(NotificationCompat.PRIORITY_HIGH)
|
||||||
.setGroupSummary(true)
|
.setAutoCancel(false)
|
||||||
.setAutoCancel(true)
|
.setOngoing(true)
|
||||||
.setContentIntent(messagesPendingIntent)
|
.setContentIntent(pendingIntent)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
notificationManager.notify(senderNotificationId, notification)
|
val notificationManager = NotificationManagerCompat.from(context)
|
||||||
if (shouldShowSummary) {
|
try {
|
||||||
notificationManager.notify(SUMMARY_NOTIFICATION_ID, summaryNotification)
|
notificationManager.notify(MessagingService.NOTIFICATION_ID, messageNotification)
|
||||||
} else {
|
|
||||||
notificationManager.cancel(SUMMARY_NOTIFICATION_ID)
|
|
||||||
}
|
|
||||||
} catch (_: SecurityException) {
|
} catch (_: SecurityException) {
|
||||||
// Permission not granted – ignore silently
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
|
|
@ -188,66 +155,53 @@ internal class NotificationHelper @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Entfernt Benachrichtigungen für einen bestimmten Absender (wenn der Chat geöffnet wird).
|
/// Entfernt Benachrichtigungen für einen bestimmten Absender (wenn der Chat geöffnet wird).
|
||||||
|
/// Wenn keine aktiven Absender mehr vorhanden sind, wird auf die Idle-Notification zurückgesetzt.
|
||||||
fun cancelNotificationForSender(senderId: String) {
|
fun cancelNotificationForSender(senderId: String) {
|
||||||
val notificationManager = NotificationManagerCompat.from(context)
|
|
||||||
val senderNotificationId = senderId.hashCode()
|
val senderNotificationId = senderId.hashCode()
|
||||||
notificationManager.cancel(senderNotificationId)
|
val remainingText: String? = synchronized(activeSenderNotificationIds) {
|
||||||
val hasMoreSenders = synchronized(activeSenderNotificationIds) {
|
|
||||||
activeSenderNotificationIds.remove(senderNotificationId)
|
activeSenderNotificationIds.remove(senderNotificationId)
|
||||||
activeSenderNamesById.remove(senderNotificationId)
|
activeSenderNamesById.remove(senderNotificationId)
|
||||||
activeSenderNotificationIds.isNotEmpty()
|
if (activeSenderNotificationIds.isEmpty()) {
|
||||||
|
null
|
||||||
|
} else {
|
||||||
|
buildSummaryText(activeSenderNamesById.values.toList())
|
||||||
}
|
}
|
||||||
if (!hasMoreSenders) {
|
|
||||||
notificationManager.cancel(SUMMARY_NOTIFICATION_ID)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val notificationManager = NotificationManagerCompat.from(context)
|
||||||
|
try {
|
||||||
|
if (remainingText == null) {
|
||||||
|
notificationManager.notify(MessagingService.NOTIFICATION_ID, buildIdleNotification())
|
||||||
|
} else {
|
||||||
|
val updatedNotification = NotificationCompat.Builder(context, CHANNEL_ID)
|
||||||
|
.setSmallIcon(R.drawable.ic_notification_message)
|
||||||
|
.setContentTitle("Bollwerk")
|
||||||
|
.setContentText(remainingText)
|
||||||
|
.setPriority(NotificationCompat.PRIORITY_HIGH)
|
||||||
|
.setAutoCancel(false)
|
||||||
|
.setOngoing(true)
|
||||||
|
.setContentIntent(buildMessagesPendingIntent())
|
||||||
|
.build()
|
||||||
|
notificationManager.notify(MessagingService.NOTIFICATION_ID, updatedNotification)
|
||||||
|
}
|
||||||
|
} catch (_: SecurityException) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Aktualisiert den Launcher-Badge-Zähler für ungelesene Nachrichten.
|
/// Aktualisiert den Launcher-Badge-Zähler für ungelesene Nachrichten.
|
||||||
fun updateBadgeCount(count: Int) {
|
fun updateBadgeCount(count: Int) {
|
||||||
val notificationManager = NotificationManagerCompat.from(context)
|
// Badge-Logik ist mit der vereinheitlichten Notification redundant.
|
||||||
if (count <= 0) {
|
// Kein separater Badge-Notification nötig, da die FG-Notification immer sichtbar ist.
|
||||||
notificationManager.cancel(SUMMARY_NOTIFICATION_ID)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// If per-sender notifications are active, the summary notification is already informative.
|
|
||||||
val hasSenderNotifications = synchronized(activeSenderNotificationIds) {
|
|
||||||
activeSenderNotificationIds.isNotEmpty()
|
|
||||||
}
|
|
||||||
if (hasSenderNotifications) return
|
|
||||||
val messagesIntent = Intent(context, MainActivity::class.java).apply {
|
|
||||||
flags = Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_CLEAR_TOP
|
|
||||||
putExtra(EXTRA_OPEN_MESSAGES, true)
|
|
||||||
}
|
|
||||||
val pendingIntent = PendingIntent.getActivity(
|
|
||||||
context, SUMMARY_NOTIFICATION_ID, messagesIntent,
|
|
||||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
|
||||||
)
|
|
||||||
val badgeNotification = NotificationCompat.Builder(context, CHANNEL_ID)
|
|
||||||
.setSmallIcon(R.drawable.ic_notification_message)
|
|
||||||
.setContentTitle("Ungelesene Nachrichten")
|
|
||||||
.setContentText("$count ungelesene Nachrichten")
|
|
||||||
.setNumber(count)
|
|
||||||
.setGroup(GROUP_KEY)
|
|
||||||
.setGroupSummary(true)
|
|
||||||
.setAutoCancel(true)
|
|
||||||
.setOnlyAlertOnce(true)
|
|
||||||
.setContentIntent(pendingIntent)
|
|
||||||
.build()
|
|
||||||
try {
|
|
||||||
notificationManager.notify(SUMMARY_NOTIFICATION_ID, badgeNotification)
|
|
||||||
} catch (_: SecurityException) {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun cancelAllMessageNotifications() {
|
fun cancelAllMessageNotifications() {
|
||||||
val notificationManager = NotificationManagerCompat.from(context)
|
synchronized(activeSenderNotificationIds) {
|
||||||
val ids = synchronized(activeSenderNotificationIds) {
|
|
||||||
val copy = activeSenderNotificationIds.toList()
|
|
||||||
activeSenderNotificationIds.clear()
|
activeSenderNotificationIds.clear()
|
||||||
activeSenderNamesById.clear()
|
activeSenderNamesById.clear()
|
||||||
copy
|
|
||||||
}
|
}
|
||||||
ids.forEach { notificationManager.cancel(it) }
|
val notificationManager = NotificationManagerCompat.from(context)
|
||||||
notificationManager.cancel(SUMMARY_NOTIFICATION_ID)
|
try {
|
||||||
|
notificationManager.notify(MessagingService.NOTIFICATION_ID, buildIdleNotification())
|
||||||
|
} catch (_: SecurityException) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun buildSummaryText(senderNames: List<String>): String {
|
private fun buildSummaryText(senderNames: List<String>): String {
|
||||||
|
|
@ -263,13 +217,58 @@ internal class NotificationHelper @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun buildIdleNotification(): android.app.Notification {
|
||||||
|
val openAppIntent = PendingIntent.getActivity(
|
||||||
|
context, 0,
|
||||||
|
Intent(context, MainActivity::class.java).apply {
|
||||||
|
flags = Intent.FLAG_ACTIVITY_SINGLE_TOP
|
||||||
|
},
|
||||||
|
PendingIntent.FLAG_IMMUTABLE
|
||||||
|
)
|
||||||
|
return NotificationCompat.Builder(context, SERVICE_CHANNEL_ID)
|
||||||
|
.setContentTitle("Bollwerk")
|
||||||
|
.setContentText("Warten auf Nachrichten…")
|
||||||
|
.setSmallIcon(R.drawable.ic_notification_message)
|
||||||
|
.setOngoing(true)
|
||||||
|
.setSilent(true)
|
||||||
|
.setPriority(NotificationCompat.PRIORITY_MIN)
|
||||||
|
.setContentIntent(openAppIntent)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun buildChatPendingIntent(senderId: String, senderUsername: String): PendingIntent {
|
||||||
|
val chatIntent = Intent(context, MainActivity::class.java).apply {
|
||||||
|
flags = Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_CLEAR_TOP
|
||||||
|
putExtra(EXTRA_RECIPIENT_ID, senderId)
|
||||||
|
putExtra(EXTRA_RECIPIENT_USERNAME, senderUsername)
|
||||||
|
}
|
||||||
|
return PendingIntent.getActivity(
|
||||||
|
context,
|
||||||
|
senderId.hashCode(),
|
||||||
|
chatIntent,
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun buildMessagesPendingIntent(): PendingIntent {
|
||||||
|
val messagesIntent = Intent(context, MainActivity::class.java).apply {
|
||||||
|
flags = Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_CLEAR_TOP
|
||||||
|
putExtra(EXTRA_OPEN_MESSAGES, true)
|
||||||
|
}
|
||||||
|
return PendingIntent.getActivity(
|
||||||
|
context,
|
||||||
|
MESSAGES_REQUEST_CODE,
|
||||||
|
messagesIntent,
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val CHANNEL_ID = "bollwerk_messages"
|
const val CHANNEL_ID = "bollwerk_messages"
|
||||||
private const val CHANNEL_NAME = "Chat-Nachrichten"
|
private const val CHANNEL_NAME = "Chat-Nachrichten"
|
||||||
const val SERVICE_CHANNEL_ID = "bollwerk_service"
|
const val SERVICE_CHANNEL_ID = "bollwerk_service"
|
||||||
private const val SERVICE_CHANNEL_NAME = "Nachrichtendienst"
|
private const val SERVICE_CHANNEL_NAME = "Nachrichtendienst"
|
||||||
private const val GROUP_KEY = "de.bollwerk.app.MESSAGES"
|
private const val MESSAGES_REQUEST_CODE = 1001
|
||||||
private const val SUMMARY_NOTIFICATION_ID = 0
|
|
||||||
|
|
||||||
const val EXTRA_RECIPIENT_ID = "notification_recipient_id"
|
const val EXTRA_RECIPIENT_ID = "notification_recipient_id"
|
||||||
const val EXTRA_RECIPIENT_USERNAME = "notification_recipient_username"
|
const val EXTRA_RECIPIENT_USERNAME = "notification_recipient_username"
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue