feat(deeplink): point deeplinks at HealthChat hosts

Claims app.healthchat.ch and to.healthchat.ch as App Links, registers the
healthchat:// scheme, and drops the unreachable mobile.element.io handling.
This commit is contained in:
2026-08-03 10:53:35 +02:00
parent d6ea73140e
commit 57a2e585ba
15 changed files with 155 additions and 168 deletions
@@ -8,5 +8,7 @@
package io.element.android.libraries.deeplink.impl
internal const val SCHEME = "elementx"
// Internal deep link, only used to build the notification PendingIntents. The "open" host keeps
// it distinct from the healthchat://room|user links handed out by the HealthChat matrix.to site.
internal const val SCHEME = "healthchat"
internal const val HOST = "open"
@@ -28,15 +28,15 @@ class DefaultDeepLinkCreatorTest {
val threadId = A_THREAD_ID
val eventId = AN_EVENT_ID
assertThat(sut.create(sessionId, null, null, null))
.isEqualTo("elementx://open/%40alice%3Aserver.org")
.isEqualTo("healthchat://open/%40alice%3Aserver.org")
assertThat(sut.create(sessionId, roomId, null, null))
.isEqualTo("elementx://open/%40alice%3Aserver.org/%21aRoomId%3Adomain")
.isEqualTo("healthchat://open/%40alice%3Aserver.org/%21aRoomId%3Adomain")
assertThat(sut.create(sessionId, roomId, threadId, null))
.isEqualTo("elementx://open/%40alice%3Aserver.org/%21aRoomId%3Adomain/%24aThreadId")
.isEqualTo("healthchat://open/%40alice%3Aserver.org/%21aRoomId%3Adomain/%24aThreadId")
assertThat(sut.create(sessionId, roomId, threadId, eventId))
.isEqualTo("elementx://open/%40alice%3Aserver.org/%21aRoomId%3Adomain/%24aThreadId/%24anEventId")
.isEqualTo("healthchat://open/%40alice%3Aserver.org/%21aRoomId%3Adomain/%24aThreadId/%24anEventId")
assertThat(sut.create(sessionId, roomId, null, eventId))
.isEqualTo("elementx://open/%40alice%3Aserver.org/%21aRoomId%3Adomain//%24anEventId")
.isEqualTo("healthchat://open/%40alice%3Aserver.org/%21aRoomId%3Adomain//%24anEventId")
}
@Test
@@ -47,12 +47,12 @@ class DefaultDeepLinkCreatorTest {
val threadId = ThreadId("\$a/ThreadId")
val eventId = EventId("\$an/EventId")
assertThat(sut.create(sessionId, roomId, null, null))
.isEqualTo("elementx://open/%40a%2F%3Adomain/%21a%2FRoomId%3Adomain")
.isEqualTo("healthchat://open/%40a%2F%3Adomain/%21a%2FRoomId%3Adomain")
assertThat(sut.create(sessionId, roomId, threadId, null))
.isEqualTo("elementx://open/%40a%2F%3Adomain/%21a%2FRoomId%3Adomain/%24a%2FThreadId")
.isEqualTo("healthchat://open/%40a%2F%3Adomain/%21a%2FRoomId%3Adomain/%24a%2FThreadId")
assertThat(sut.create(sessionId, roomId, threadId, eventId))
.isEqualTo("elementx://open/%40a%2F%3Adomain/%21a%2FRoomId%3Adomain/%24a%2FThreadId/%24an%2FEventId")
.isEqualTo("healthchat://open/%40a%2F%3Adomain/%21a%2FRoomId%3Adomain/%24a%2FThreadId/%24an%2FEventId")
assertThat(sut.create(sessionId, roomId, null, eventId))
.isEqualTo("elementx://open/%40a%2F%3Adomain/%21a%2FRoomId%3Adomain//%24an%2FEventId")
.isEqualTo("healthchat://open/%40a%2F%3Adomain/%21a%2FRoomId%3Adomain//%24an%2FEventId")
}
}
@@ -27,17 +27,17 @@ import org.junit.Test
class DefaultDeeplinkParserTest : RobolectricTest() {
companion object {
const val A_URI =
"elementx://open/@alice:server.org"
"healthchat://open/@alice:server.org"
const val A_URI_WITH_ROOM =
"elementx://open/@alice:server.org/!aRoomId:domain"
"healthchat://open/@alice:server.org/!aRoomId:domain"
const val A_URI_WITH_ROOM_WITH_THREAD =
"elementx://open/@alice:server.org/!aRoomId:domain/\$aThreadId"
"healthchat://open/@alice:server.org/!aRoomId:domain/\$aThreadId"
const val A_URI_WITH_ROOM_WITH_THREAD_AND_EVENT =
"elementx://open/@alice:server.org/!aRoomId:domain/\$aThreadId/\$anEventId"
"healthchat://open/@alice:server.org/!aRoomId:domain/\$aThreadId/\$anEventId"
const val A_URI_WITH_ROOM_WITH_EVENT_AND_NO_THREAD =
"elementx://open/@alice:server.org/!aRoomId:domain//\$anEventId"
"healthchat://open/@alice:server.org/!aRoomId:domain//\$anEventId"
const val A_URI_WITH_ROOM_WITH_THREAD_AND_EVENT_AND_INVALID_CHARACTERS =
"elementx://open/@a%2Flice:server.org/!a%2FRoomId:domain/\$a%2FThreadId/\$an%2FEventId"
"healthchat://open/@a%2Flice:server.org/!a%2FRoomId:domain/\$a%2FThreadId/\$an%2FEventId"
}
@Test
@@ -70,17 +70,17 @@ class DefaultDeeplinkParserTest : RobolectricTest() {
// Bad scheme
assertThat(sut.getFromIntent(createIntent("x://open/@alice:server.org"))).isNull()
// Bad host
assertThat(sut.getFromIntent(createIntent("elementx://close/@alice:server.org"))).isNull()
assertThat(sut.getFromIntent(createIntent("healthchat://close/@alice:server.org"))).isNull()
// No session Id
assertThat(sut.getFromIntent(createIntent("elementx://open"))).isNull()
assertThat(sut.getFromIntent(createIntent("healthchat://open"))).isNull()
assertThrowsInDebug {
// Invalid sessionId
sut.getFromIntent(createIntent("elementx://open/alice:server.org"))
sut.getFromIntent(createIntent("healthchat://open/alice:server.org"))
}
assertThrowsInDebug {
// Empty sessionId
sut.getFromIntent(createIntent("elementx://open//"))
sut.getFromIntent(createIntent("healthchat://open//"))
}
}