From 6d28d91541f9338fc01265659a8feaa4e723d9cb Mon Sep 17 00:00:00 2001 From: hayaksi1 <193020925+hayaksi1@users.noreply.github.com> Date: Wed, 26 Aug 2026 03:18:28 +0300 Subject: [PATCH] Offer conversations in the system share sheet Sharing from another app listed Element X once, with no way to pick a conversation: the Direct Share row never showed any. The app already publishes a long-lived, conversation-categorised shortcut per room, which is most of what the sharing shortcuts API needs. The part that was missing is the declaration that ties those shortcuts to the share intent: the launcher activity had no `android.app.shortcuts` meta-data, so there was no `` for the system to match them against. It now ships one, matching the activity that already receives ACTION_SEND, and the shortcuts carry the category it names. The system offers these from Android 10 onwards, where sharing shortcuts are handled natively. Reaching further back would additionally need the androidx.sharetarget library, which is not added here. Part of #3835 --- app/src/main/AndroidManifest.xml | 6 +++++ app/src/main/res/xml/shortcuts.xml | 18 +++++++++++++++ .../DefaultNotificationConversationService.kt | 9 +++++++- ...aultNotificationConversationServiceTest.kt | 22 +++++++++++++++++++ 4 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 app/src/main/res/xml/shortcuts.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index f6a3225cb8..d5f08e0795 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -139,6 +139,12 @@ + + diff --git a/app/src/main/res/xml/shortcuts.xml b/app/src/main/res/xml/shortcuts.xml new file mode 100644 index 0000000000..d4995157d8 --- /dev/null +++ b/app/src/main/res/xml/shortcuts.xml @@ -0,0 +1,18 @@ + + + + + + + + + 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 f898e4a9ed..7b08958cd1 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 @@ -42,6 +42,12 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import timber.log.Timber +/** + * Category shared with the `` in `app/src/main/res/xml/shortcuts.xml`. The system only offers a shortcut + * in the Direct Share row of the share sheet when the two match, so the literal has to be kept in step with that file. + */ +private const val SHARE_TARGET_CATEGORY = "io.element.android.category.SHARE_TARGET" + @SingleIn(AppScope::class) @ContributesBinding(AppScope::class) class DefaultNotificationConversationService( @@ -86,7 +92,8 @@ class DefaultNotificationConversationService( } val categories = setOfNotNull( - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION else null + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION else null, + SHARE_TARGET_CATEGORY, ) val client = matrixClientProvider.getOrRestore(sessionId).getOrNull() ?: return 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 41e601c50d..cb5fad7509 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 @@ -10,6 +10,7 @@ package io.element.android.libraries.push.impl.notifications.conversations import android.content.Context import android.content.Intent +import android.content.pm.ShortcutInfo import android.os.Build import androidx.core.content.pm.ShortcutInfoCompat import androidx.core.content.pm.ShortcutManagerCompat @@ -52,6 +53,27 @@ class DefaultNotificationConversationServiceTest : RobolectricTest() { assertThat(shortcuts).isNotEmpty() } + @Test + fun `the shortcut is offered to the share sheet as a conversation`() = runTest { + val context = InstrumentationRegistry.getInstrumentation().context + val service = createService(context) + + service.onSendMessage( + sessionId = A_SESSION_ID, + roomId = A_ROOM_ID, + roomName = "Room title", + roomIsDirect = false, + roomAvatarUrl = null, + ) + + val shortcut = ShortcutManagerCompat.getDynamicShortcuts(context).single() + assertThat(shortcut.categories).containsAtLeast( + ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, + // Matches the category in app/src/main/res/xml/shortcuts.xml + "io.element.android.category.SHARE_TARGET", + ) + } + @Test fun `onLeftRoom removes a shortcut`() = runTest { val context = InstrumentationRegistry.getInstrumentation().context