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