From 8f8c55e54a7b406f97a8784f6f530c5d6ffaac7d Mon Sep 17 00:00:00 2001 From: Robin Linden Date: Fri, 21 Feb 2025 01:03:28 +0100 Subject: [PATCH 1/2] Treat the tox:-intent data as a uri --- atox/src/main/kotlin/MainActivity.kt | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/atox/src/main/kotlin/MainActivity.kt b/atox/src/main/kotlin/MainActivity.kt index 25868683d..e3980a227 100644 --- a/atox/src/main/kotlin/MainActivity.kt +++ b/atox/src/main/kotlin/MainActivity.kt @@ -1,10 +1,11 @@ -// SPDX-FileCopyrightText: 2019-2024 Robin Lindén +// SPDX-FileCopyrightText: 2019-2025 Robin Lindén // // SPDX-License-Identifier: GPL-3.0-only package ltd.evilcorp.atox import android.content.Intent +import android.net.Uri import android.os.Build import android.os.Bundle import android.util.Log @@ -20,9 +21,10 @@ import ltd.evilcorp.atox.settings.Settings import ltd.evilcorp.atox.ui.contactlist.ARG_SHARE private const val TAG = "MainActivity" -private const val SCHEME = "tox:" private const val TOX_ID_LENGTH = 76 +private fun isToxUri(uri: Uri) = uri.isOpaque && uri.scheme == "tox" && uri.schemeSpecificPart.length == TOX_ID_LENGTH + class MainActivity : AppCompatActivity() { @Inject lateinit var vmFactory: ViewModelFactory @@ -81,16 +83,16 @@ class MainActivity : AppCompatActivity() { } private fun handleToxLinkIntent(intent: Intent) { - val data = intent.dataString ?: "" + val data = intent.data Log.i(TAG, "Got uri with data: $data") - if (!data.startsWith(SCHEME) || data.length != SCHEME.length + TOX_ID_LENGTH) { + if (data == null || !isToxUri(data)) { Log.e(TAG, "Got malformed uri: $data") return } supportFragmentManager.findFragmentById(R.id.nav_host_fragment)?.findNavController()?.navigate( R.id.addContactFragment, - bundleOf("toxId" to data.drop(SCHEME.length)), + bundleOf("toxId" to data.schemeSpecificPart), ) } From a9fc0627895753168de58b79627ecaa49c4f26d4 Mon Sep 17 00:00:00 2001 From: Robin Linden Date: Fri, 21 Feb 2025 01:49:54 +0100 Subject: [PATCH 2/2] Support tox-uris in the form `tox://0123ABCD?name=robin&message=hello` `tox:0123ABCD` is still supported, but it can't have query-parameters as it's an opaque uri. `tox://0123ABCD` is a hierarchical uri and supports all the fancy features. --- atox/src/main/kotlin/MainActivity.kt | 43 +++++++++++++++++-- .../ui/addcontact/AddContactFragment.kt | 8 +++- 2 files changed, 46 insertions(+), 5 deletions(-) diff --git a/atox/src/main/kotlin/MainActivity.kt b/atox/src/main/kotlin/MainActivity.kt index e3980a227..00fb2d6f5 100644 --- a/atox/src/main/kotlin/MainActivity.kt +++ b/atox/src/main/kotlin/MainActivity.kt @@ -19,11 +19,41 @@ import javax.inject.Inject import ltd.evilcorp.atox.di.ViewModelFactory import ltd.evilcorp.atox.settings.Settings import ltd.evilcorp.atox.ui.contactlist.ARG_SHARE +import ltd.evilcorp.domain.tox.ToxID private const val TAG = "MainActivity" -private const val TOX_ID_LENGTH = 76 -private fun isToxUri(uri: Uri) = uri.isOpaque && uri.scheme == "tox" && uri.schemeSpecificPart.length == TOX_ID_LENGTH +// TODO(robinlinden): Move to common place, use for QR-code-provided uris as well. +data class ToxUri(val toxId: ToxID, val nameSuggestion: String? = null, val messageSuggestion: String? = null) { + companion object { + private const val TOX_ID_LENGTH = 76 + + fun parseFrom(uri: Uri?): ToxUri? { + if (uri == null) { + return null + } + + if (uri.scheme != "tox") { + return null + } + + if (uri.isOpaque) { + if (uri.schemeSpecificPart.length != TOX_ID_LENGTH) { + return null + } + + return ToxUri(ToxID(uri.schemeSpecificPart)) + } + + val maybeToxId = uri.host + if (maybeToxId?.length != TOX_ID_LENGTH) { + return null + } + + return ToxUri(ToxID(maybeToxId), uri.getQueryParameter("name"), uri.getQueryParameter("message")) + } + } +} class MainActivity : AppCompatActivity() { @Inject @@ -85,14 +115,19 @@ class MainActivity : AppCompatActivity() { private fun handleToxLinkIntent(intent: Intent) { val data = intent.data Log.i(TAG, "Got uri with data: $data") - if (data == null || !isToxUri(data)) { + val toxUri = ToxUri.parseFrom(data) + if (toxUri == null) { Log.e(TAG, "Got malformed uri: $data") return } supportFragmentManager.findFragmentById(R.id.nav_host_fragment)?.findNavController()?.navigate( R.id.addContactFragment, - bundleOf("toxId" to data.schemeSpecificPart), + bundleOf( + "toxId" to toxUri.toxId.string(), + "name" to toxUri.nameSuggestion, + "message" to toxUri.messageSuggestion, + ), ) } diff --git a/atox/src/main/kotlin/ui/addcontact/AddContactFragment.kt b/atox/src/main/kotlin/ui/addcontact/AddContactFragment.kt index 684721027..58c28e504 100644 --- a/atox/src/main/kotlin/ui/addcontact/AddContactFragment.kt +++ b/atox/src/main/kotlin/ui/addcontact/AddContactFragment.kt @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2019-2024 Robin Lindén +// SPDX-FileCopyrightText: 2019-2025 Robin Lindén // SPDX-FileCopyrightText: 2020 aTox contributors // // SPDX-License-Identifier: GPL-3.0-only @@ -134,5 +134,11 @@ class AddContactFragment : BaseFragment(FragmentAddCo add.isEnabled = false toxId.setText(arguments?.getString("toxId"), TextView.BufferType.EDITABLE) + val messageSuggestion = arguments?.getString("message") + if (messageSuggestion != null) { + message.setText(messageSuggestion) + } + + // TODO(robinlinden): Hook up the nickname suggestion once we have nicknames. } }