From fd5471b61532d7c605ddd223cab2183c7231b57f Mon Sep 17 00:00:00 2001 From: dece Date: Wed, 16 Feb 2022 11:06:26 +0100 Subject: [PATCH] Uri: add resolveLinkUri with tests --- .../dev/lowrespalmtree/comet/UriUtilsTest.kt | 42 ++++++++++++++++--- .../dev/lowrespalmtree/comet/PageFragment.kt | 9 +--- .../dev/lowrespalmtree/comet/PageViewModel.kt | 5 +-- .../dev/lowrespalmtree/comet/utils/Uri.kt | 20 +++++++++ 4 files changed, 61 insertions(+), 15 deletions(-) diff --git a/app/src/androidTest/java/dev/lowrespalmtree/comet/UriUtilsTest.kt b/app/src/androidTest/java/dev/lowrespalmtree/comet/UriUtilsTest.kt index 1690193..cce46a9 100644 --- a/app/src/androidTest/java/dev/lowrespalmtree/comet/UriUtilsTest.kt +++ b/app/src/androidTest/java/dev/lowrespalmtree/comet/UriUtilsTest.kt @@ -1,14 +1,46 @@ package dev.lowrespalmtree.comet +import dev.lowrespalmtree.comet.utils.* import org.junit.Assert.assertEquals import org.junit.Test -/** - * Example local unit test, which will execute on the development machine (host). - * - * See [testing documentation](http://d.android.com/tools/testing). - */ +/** Test utils.Uri functions. Runs of the device due to the Uri functions being Android-only .*/ class UriUtilsTest { + @Test + fun resolveLinkUri() { + // Absolute URLs. + assertEquals( + "gemini://example.com/", + resolveLinkUri("gemini://example.com", "gemini://dece.space/").toString() + ) + // Relative links. + assertEquals( + "gemini://example.com/", + resolveLinkUri(".", "gemini://example.com/").toString() + ) + assertEquals( + "gemini://example.com/", + resolveLinkUri("..", "gemini://example.com/").toString() + ) + assertEquals( + "gemini://example.com/page", + resolveLinkUri("./page", "gemini://example.com/").toString() + ) + assertEquals( + "gemini://example.com/page", + resolveLinkUri("page", "gemini://example.com/").toString() + ) + assertEquals( + "gemini://example.com/page.com", + resolveLinkUri("page.com", "gemini://example.com/").toString() + ) + // Scheme-less URLs. + assertEquals( + "gemini://someone.smol.pub/somepage", + resolveLinkUri("//someone.smol.pub/somepage", "gemini://smol.pub/feed").toString() + ) + } + @Test fun joinUrls() { assertEquals( diff --git a/app/src/main/java/dev/lowrespalmtree/comet/PageFragment.kt b/app/src/main/java/dev/lowrespalmtree/comet/PageFragment.kt index af0e7b5..7931590 100644 --- a/app/src/main/java/dev/lowrespalmtree/comet/PageFragment.kt +++ b/app/src/main/java/dev/lowrespalmtree/comet/PageFragment.kt @@ -20,6 +20,7 @@ import androidx.recyclerview.widget.LinearLayoutManager import dev.lowrespalmtree.comet.databinding.FragmentPageViewBinding import dev.lowrespalmtree.comet.utils.isConnectedToNetwork import dev.lowrespalmtree.comet.utils.joinUrls +import dev.lowrespalmtree.comet.utils.resolveLinkUri import dev.lowrespalmtree.comet.utils.toGeminiUri import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -107,13 +108,7 @@ class PageFragment : Fragment(), PageAdapter.Listener { return } - var uri = Uri.parse(url) - if (!uri.isAbsolute) { - uri = if (!base.isNullOrEmpty()) joinUrls(base, url) else toGeminiUri(uri) - } else if (uri.scheme == "gemini" && uri.path.isNullOrEmpty()) { - uri = uri.buildUpon().path("/").build() - } - + val uri = resolveLinkUri(url, base) when (uri.scheme) { "gemini" -> vm.sendGeminiRequest(uri, requireContext()) else -> openUnknownScheme(uri) diff --git a/app/src/main/java/dev/lowrespalmtree/comet/PageViewModel.kt b/app/src/main/java/dev/lowrespalmtree/comet/PageViewModel.kt index eb834bc..4884d90 100644 --- a/app/src/main/java/dev/lowrespalmtree/comet/PageViewModel.kt +++ b/app/src/main/java/dev/lowrespalmtree/comet/PageViewModel.kt @@ -9,6 +9,7 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import androidx.preference.PreferenceManager import dev.lowrespalmtree.comet.utils.joinUrls +import dev.lowrespalmtree.comet.utils.resolveLinkUri import kotlinx.coroutines.* import kotlinx.coroutines.channels.onSuccess import java.net.ConnectException @@ -163,9 +164,7 @@ class PageViewModel(@Suppress("unused") private val savedStateHandle: SavedState lineChannelResult.onSuccess { line -> if (line is LinkLine) { // Mark visited links here as we have a access to the history. - val fullUrl = - if (Uri.parse(line.url).isAbsolute) line.url - else joinUrls(uriString, line.url).toString() + val fullUrl = resolveLinkUri(line.url, uriString).toString() if (History.contains(fullUrl)) line.visited = true } diff --git a/app/src/main/java/dev/lowrespalmtree/comet/utils/Uri.kt b/app/src/main/java/dev/lowrespalmtree/comet/utils/Uri.kt index a6063a8..89d0fb6 100644 --- a/app/src/main/java/dev/lowrespalmtree/comet/utils/Uri.kt +++ b/app/src/main/java/dev/lowrespalmtree/comet/utils/Uri.kt @@ -2,6 +2,26 @@ package dev.lowrespalmtree.comet.utils import android.net.Uri +/** + * Resolve the URI of a link found on a page. + * + * Links can take various forms: absolute links to a page on a capsule, relative links on the same + * capsule, but also fancy scheme-less absolute URLs (i.e. starting with "//") for cross-protocol + * linking. This function returns the resolved URI from any type of link, opt. using current URL. + */ +fun resolveLinkUri(url: String, base: String?): Uri { + var uri = Uri.parse(url) + if (!uri.isAbsolute) { + uri = + if (url.startsWith("//")) uri.buildUpon().scheme("gemini").build() + else if (!base.isNullOrEmpty()) joinUrls(base, url) + else toGeminiUri(uri) + } else if (uri.scheme == "gemini" && uri.path.isNullOrEmpty()) { + uri = uri.buildUpon().path("/").build() + } + return uri +} + /** * Transform a relative URI to an absolute Gemini URI *