mirror of
https://github.com/anyproto/anytype-kotlin.git
synced 2025-06-08 13:57:10 +09:00
DROID-3397 Deep links | Fix | Misc. deep link fixes (#2145)
This commit is contained in:
parent
3932c2fe87
commit
ec9cdd1c60
2 changed files with 126 additions and 52 deletions
|
@ -18,21 +18,19 @@ const val DEEP_LINK_TO_OBJECT_BASE_URL = "https://object.any.coop"
|
|||
* Regex pattern for matching
|
||||
*/
|
||||
const val DEEP_LINK_INVITE_REG_EXP = "invite.any.coop/([a-zA-Z0-9]+)#([a-zA-Z0-9]+)"
|
||||
const val DEEP_LINK_TO_OBJECT_REG_EXP = """object\.any\.coop/([a-zA-Z0-9?=&._-]+)"""
|
||||
|
||||
const val DEE_LINK_INVITE_CUSTOM_REG_EXP = "anytype://invite/\\?cid=([a-zA-Z0-9]+)&key=([a-zA-Z0-9]+)"
|
||||
|
||||
const val MAIN_PATH = "main"
|
||||
const val OBJECT_PATH = "object"
|
||||
const val IMPORT_PATH = "import"
|
||||
const val INVITE_PATH = "invite"
|
||||
const val MEMBERSHIP_PATH = "membership"
|
||||
|
||||
const val TYPE_PARAM = "type"
|
||||
const val OBJECT_ID_PARAM = "objectId"
|
||||
const val SPACE_ID_PARAM = "spaceId"
|
||||
const val CONTENT_ID_PARAM = "cid"
|
||||
const val INVITE_ID_PARAM = "inviteID"
|
||||
const val ENCRYPTION_KEY_PARAM = "key"
|
||||
const val SOURCE_PARAM = "source"
|
||||
const val TYPE_VALUE_EXPERIENCE = "experience"
|
||||
const val TIER_ID_PARAM = "tier"
|
||||
|
@ -42,60 +40,78 @@ const val IMPORT_EXPERIENCE_DEEPLINK = "$DEEP_LINK_PATTERN$MAIN_PATH/$IMPORT_PAT
|
|||
object DefaultDeepLinkResolver : DeepLinkResolver {
|
||||
|
||||
private val defaultInviteRegex = Regex(DEEP_LINK_INVITE_REG_EXP)
|
||||
private val defaultLinkToObjectRegex = Regex(DEEP_LINK_TO_OBJECT_REG_EXP)
|
||||
|
||||
override fun resolve(
|
||||
deeplink: String
|
||||
): DeepLinkResolver.Action = when {
|
||||
deeplink.contains(IMPORT_EXPERIENCE_DEEPLINK) -> {
|
||||
try {
|
||||
val type = Uri.parse(deeplink).getQueryParameter(TYPE_PARAM)
|
||||
val source = Uri.parse(deeplink).getQueryParameter(SOURCE_PARAM)
|
||||
DeepLinkResolver.Action.Import.Experience(
|
||||
type = type.orEmpty(),
|
||||
source = source.orEmpty()
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
DeepLinkResolver.Action.Unknown
|
||||
}
|
||||
override fun resolve(deeplink: String): DeepLinkResolver.Action {
|
||||
val uri = Uri.parse(deeplink)
|
||||
|
||||
return when {
|
||||
deeplink.contains(IMPORT_EXPERIENCE_DEEPLINK) -> resolveImportExperience(uri)
|
||||
defaultInviteRegex.containsMatchIn(deeplink) -> DeepLinkResolver.Action.Invite(deeplink)
|
||||
defaultLinkToObjectRegex.containsMatchIn(deeplink) -> resolveDeepLinkToObject(uri)
|
||||
deeplink.contains(OBJECT_PATH) -> resolveObjectPath(uri)
|
||||
deeplink.contains(MEMBERSHIP_PATH) -> resolveMembershipPath(uri)
|
||||
else -> DeepLinkResolver.Action.Unknown
|
||||
}.also {
|
||||
Timber.d("Resolving deep link: $deeplink")
|
||||
}
|
||||
deeplink.contains(INVITE_PATH) -> {
|
||||
DeepLinkResolver.Action.Invite(deeplink)
|
||||
}
|
||||
|
||||
private fun resolveImportExperience(uri: Uri): DeepLinkResolver.Action {
|
||||
return try {
|
||||
val type = uri.getQueryParameter(TYPE_PARAM).orEmpty()
|
||||
val source = uri.getQueryParameter(SOURCE_PARAM).orEmpty()
|
||||
DeepLinkResolver.Action.Import.Experience(type, source)
|
||||
} catch (e: Exception) {
|
||||
DeepLinkResolver.Action.Unknown
|
||||
}
|
||||
defaultInviteRegex.containsMatchIn(deeplink) -> {
|
||||
DeepLinkResolver.Action.Invite(deeplink)
|
||||
}
|
||||
deeplink.contains(OBJECT_PATH) -> {
|
||||
val uri = Uri.parse(deeplink)
|
||||
val obj = uri.getQueryParameter(OBJECT_ID_PARAM)
|
||||
val space = uri.getQueryParameter(SPACE_ID_PARAM)
|
||||
if (!obj.isNullOrEmpty() && !space.isNullOrEmpty()) {
|
||||
val cid = uri.getQueryParameter(CONTENT_ID_PARAM)
|
||||
val key = uri.getQueryParameter(ENCRYPTION_KEY_PARAM)
|
||||
DeepLinkResolver.Action.DeepLinkToObject(
|
||||
obj = obj,
|
||||
space = SpaceId(space),
|
||||
invite = if (!cid.isNullOrEmpty() && !key.isNullOrEmpty()) {
|
||||
DeepLinkResolver.Action.DeepLinkToObject.Invite(
|
||||
cid = cid,
|
||||
key = key
|
||||
)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
)
|
||||
} else {
|
||||
DeepLinkResolver.Action.Unknown
|
||||
}
|
||||
}
|
||||
deeplink.contains(MEMBERSHIP_PATH) -> {
|
||||
val uri = Uri.parse(deeplink)
|
||||
DeepLinkResolver.Action.DeepLinkToMembership(
|
||||
tierId = uri.getQueryParameter(TIER_ID_PARAM)
|
||||
}
|
||||
|
||||
private fun resolveDeepLinkToObject(uri: Uri): DeepLinkResolver.Action {
|
||||
val obj = uri.pathSegments.getOrNull(0) ?: return DeepLinkResolver.Action.Unknown
|
||||
val space = uri.getQueryParameter(SPACE_ID_PARAM)?.takeIf { it.isNotEmpty() }
|
||||
?: return DeepLinkResolver.Action.Unknown // Ensure spaceId is required
|
||||
|
||||
return DeepLinkResolver.Action.DeepLinkToObject(
|
||||
obj = obj,
|
||||
space = SpaceId(space),
|
||||
invite = parseInvite(uri)
|
||||
)
|
||||
}
|
||||
|
||||
private fun resolveObjectPath(uri: Uri): DeepLinkResolver.Action {
|
||||
val obj = uri.getQueryParameter(OBJECT_ID_PARAM)?.takeIf { it.isNotEmpty() }
|
||||
val space = uri.getQueryParameter(SPACE_ID_PARAM)?.takeIf { it.isNotEmpty() }
|
||||
?: return DeepLinkResolver.Action.Unknown // Ensure spaceId is required
|
||||
|
||||
return if (obj != null) {
|
||||
DeepLinkResolver.Action.DeepLinkToObject(
|
||||
obj = obj,
|
||||
space = SpaceId(space),
|
||||
invite = parseInvite(uri)
|
||||
)
|
||||
} else {
|
||||
DeepLinkResolver.Action.Unknown
|
||||
}
|
||||
}
|
||||
|
||||
private fun resolveMembershipPath(uri: Uri): DeepLinkResolver.Action {
|
||||
return DeepLinkResolver.Action.DeepLinkToMembership(
|
||||
tierId = uri.getQueryParameter(TIER_ID_PARAM)
|
||||
)
|
||||
}
|
||||
|
||||
private fun parseInvite(uri: Uri): DeepLinkResolver.Action.DeepLinkToObject.Invite? {
|
||||
val inviteId = uri.getQueryParameter(INVITE_ID_PARAM)?.takeIf { it.isNotEmpty() }
|
||||
val encryption = uri.fragment?.takeIf { it.isNotEmpty() }
|
||||
return if (inviteId != null && encryption != null) {
|
||||
DeepLinkResolver.Action.DeepLinkToObject.Invite(
|
||||
key = encryption,
|
||||
cid = inviteId
|
||||
)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
else -> DeepLinkResolver.Action.Unknown
|
||||
}.also {
|
||||
Timber.d("Resolving deep link: $deeplink")
|
||||
}
|
||||
|
||||
override fun createObjectDeepLink(obj: Id, space: SpaceId): Url {
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
package com.anytypeio.anytype.other
|
||||
|
||||
import android.os.Build
|
||||
import androidx.compose.runtime.key
|
||||
import com.anytypeio.anytype.core_models.primitives.SpaceId
|
||||
import com.anytypeio.anytype.domain.misc.DeepLinkResolver
|
||||
import com.anytypeio.anytype.test_utils.MockDataFactory
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.Mock
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
import org.robolectric.annotation.Config
|
||||
|
||||
|
@ -51,6 +53,62 @@ class DefaultDeepLinkResolverTest {
|
|||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `resolve https deep link to object`() {
|
||||
// Given
|
||||
|
||||
val obj = MockDataFactory.randomUuid()
|
||||
|
||||
val space = MockDataFactory.randomUuid()
|
||||
|
||||
val deeplink = "https://object.any.coop/$obj?spaceId=$space"
|
||||
|
||||
// When
|
||||
val result = deepLinkResolver.resolve(deeplink)
|
||||
|
||||
// Then
|
||||
assertEquals(
|
||||
DeepLinkResolver.Action.DeepLinkToObject(
|
||||
space = SpaceId(space),
|
||||
obj = obj
|
||||
),
|
||||
result
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `resolve https deep link to object with invite`() {
|
||||
// Given
|
||||
|
||||
val obj = MockDataFactory.randomUuid()
|
||||
|
||||
val space = MockDataFactory.randomUuid()
|
||||
|
||||
val cid = MockDataFactory.randomUuid()
|
||||
|
||||
val encryption = MockDataFactory.randomUuid()
|
||||
|
||||
val invite = "$cid#$encryption"
|
||||
|
||||
val deeplink = "https://object.any.coop/$obj?spaceId=$space&inviteID=$invite"
|
||||
|
||||
// When
|
||||
val result = deepLinkResolver.resolve(deeplink)
|
||||
|
||||
// Then
|
||||
assertEquals(
|
||||
DeepLinkResolver.Action.DeepLinkToObject(
|
||||
space = SpaceId(space),
|
||||
obj = obj,
|
||||
invite = DeepLinkResolver.Action.DeepLinkToObject.Invite(
|
||||
cid = cid,
|
||||
key = encryption
|
||||
)
|
||||
),
|
||||
result
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `resolve returns Invite with deeplink for invite deep links`() {
|
||||
// Given
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue