mirror of
https://github.com/Novaloop-AG/element-x-android.git
synced 2026-10-31 08:15:02 +00:00
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 `<share-target>` 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
This commit is contained in:
@@ -139,6 +139,12 @@
|
||||
<data android:host="user" />
|
||||
<data android:host="room" />
|
||||
</intent-filter>
|
||||
<!--
|
||||
Conversation shortcuts offered in the Direct Share row of the system share sheet.
|
||||
-->
|
||||
<meta-data
|
||||
android:name="android.app.shortcuts"
|
||||
android:resource="@xml/shortcuts" />
|
||||
<!-- Incoming share simple -->
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.SEND" />
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (c) 2026 Element Creations Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
-->
|
||||
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<!--
|
||||
Lets the system offer the conversation shortcuts published by
|
||||
DefaultNotificationConversationService in the Direct Share row of the share sheet.
|
||||
The category must match SHARE_TARGET_CATEGORY in that class.
|
||||
-->
|
||||
<share-target android:targetClass="io.element.android.x.MainActivity">
|
||||
<data android:mimeType="*/*" />
|
||||
<category android:name="io.element.android.category.SHARE_TARGET" />
|
||||
</share-target>
|
||||
</shortcuts>
|
||||
+8
-1
@@ -42,6 +42,12 @@ import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import timber.log.Timber
|
||||
|
||||
/**
|
||||
* Category shared with the `<share-target>` 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
|
||||
|
||||
+22
@@ -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 <share-target> 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
|
||||
|
||||
Reference in New Issue
Block a user