diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/conversations/DefaultNotificationConversationService.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/conversations/DefaultNotificationConversationService.kt index 2f535c9691..24b69a2e58 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/conversations/DefaultNotificationConversationService.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/conversations/DefaultNotificationConversationService.kt @@ -33,6 +33,8 @@ import io.element.android.libraries.matrix.ui.media.InitialsAvatarBitmapGenerato import io.element.android.libraries.push.api.notifications.NotificationBitmapLoader import io.element.android.libraries.push.api.notifications.conversations.NotificationConversationService import io.element.android.libraries.push.impl.intent.IntentProvider +import io.element.android.libraries.push.impl.notifications.shortcut.createShortcutId +import io.element.android.libraries.push.impl.notifications.shortcut.filterBySession import io.element.android.libraries.sessionstorage.api.observer.SessionListener import io.element.android.libraries.sessionstorage.api.observer.SessionObserver import io.element.android.libraries.ui.strings.CommonStrings @@ -106,7 +108,7 @@ class DefaultNotificationConversationService( .generateBitmap(defaultShortcutIconSize, AvatarData(id = roomId.value, name = roomName, size = AvatarSize.RoomHeader)) ?.let(IconCompat::createWithAdaptiveBitmap) - val shortcutInfo = ShortcutInfoCompat.Builder(context, "$sessionId-$roomId") + val shortcutInfo = ShortcutInfoCompat.Builder(context, createShortcutId(sessionId, roomId)) .setShortLabel(roomName) .setIcon(icon) .setIntent(intentProvider.getViewRoomIntent(sessionId, roomId, threadId = null)) @@ -127,7 +129,7 @@ class DefaultNotificationConversationService( } override suspend fun onLeftRoom(sessionId: SessionId, roomId: RoomId) { - val shortcutsToRemove = listOf("$sessionId-$roomId") + val shortcutsToRemove = listOf(createShortcutId(sessionId, roomId)) runCatchingExceptions { ShortcutManagerCompat.removeDynamicShortcuts(context, shortcutsToRemove) if (isRequestPinShortcutSupported) { @@ -181,7 +183,7 @@ class DefaultNotificationConversationService( private fun onSessionLogOut(sessionId: SessionId) { runCatchingExceptions { val shortcuts = ShortcutManagerCompat.getDynamicShortcuts(context) - val shortcutIdsToRemove = shortcuts.filter { it.id.startsWith(sessionId.value) }.map { it.id } + val shortcutIdsToRemove = shortcuts.filterBySession(sessionId).map { it.id } ShortcutManagerCompat.removeDynamicShortcuts(context, shortcutIdsToRemove) if (isRequestPinShortcutSupported) { diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationCreator.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationCreator.kt index 894c71f176..3c6a1adbbb 100755 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationCreator.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationCreator.kt @@ -41,6 +41,7 @@ import io.element.android.libraries.push.impl.notifications.model.FallbackNotifi import io.element.android.libraries.push.impl.notifications.model.InviteNotifiableEvent import io.element.android.libraries.push.impl.notifications.model.NotifiableMessageEvent import io.element.android.libraries.push.impl.notifications.model.SimpleNotifiableEvent +import io.element.android.libraries.push.impl.notifications.shortcut.createShortcutId import io.element.android.services.toolbox.api.strings.StringProvider interface NotificationCreator { @@ -140,7 +141,7 @@ class DefaultNotificationCreator( // Must match those created in the ShortcutInfoCompat.Builder() // for the notification to appear as a "Conversation": // https://developer.android.com/develop/ui/views/notifications/conversations - .setShortcutId("${roomInfo.sessionId.value}-${roomInfo.roomId.value}") + .setShortcutId(createShortcutId(roomInfo.sessionId, roomInfo.roomId)) // Auto-bundling is enabled for 4 or more notifications on API 24+ (N+) // devices and all Wear devices. But we want a custom grouping, so we specify the groupID .setGroup(roomInfo.sessionId.value) diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/shortcut/Utils.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/shortcut/Utils.kt new file mode 100644 index 0000000000..6176886dbc --- /dev/null +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/shortcut/Utils.kt @@ -0,0 +1,19 @@ +/* + * Copyright 2025 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.push.impl.notifications.shortcut + +import androidx.core.content.pm.ShortcutInfoCompat +import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.api.core.SessionId + +internal fun createShortcutId(sessionId: SessionId, roomId: RoomId) = "$sessionId-$roomId" + +internal fun Iterable.filterBySession(sessionId: SessionId): Iterable { + val prefix = "$sessionId-" + return filter { it.id.startsWith(prefix) } +} diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/conversations/DefaultNotificationConversationServiceTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/conversations/DefaultNotificationConversationServiceTest.kt index 92adfd7812..d05966f9a6 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/conversations/DefaultNotificationConversationServiceTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/conversations/DefaultNotificationConversationServiceTest.kt @@ -21,6 +21,7 @@ import io.element.android.libraries.matrix.test.A_SESSION_ID import io.element.android.libraries.matrix.test.A_SESSION_ID_2 import io.element.android.libraries.matrix.test.FakeMatrixClientProvider import io.element.android.libraries.push.impl.notifications.factories.FakeIntentProvider +import io.element.android.libraries.push.impl.notifications.shortcut.createShortcutId import io.element.android.libraries.push.test.notifications.FakeImageLoaderHolder import io.element.android.libraries.push.test.notifications.push.FakeNotificationBitmapLoader import io.element.android.libraries.sessionstorage.test.observer.FakeSessionObserver @@ -58,7 +59,7 @@ class DefaultNotificationConversationServiceTest { val context = InstrumentationRegistry.getInstrumentation().context val service = createService(context) - val shortcutId = "$A_SESSION_ID-$A_ROOM_ID" + val shortcutId = createShortcutId(A_SESSION_ID, A_ROOM_ID) val shortcutInfo = ShortcutInfoCompat.Builder(context, shortcutId) .setShortLabel("Room title") .setIntent(Intent(Intent.ACTION_VIEW))