From e0e7ad38063619c1be62dc6d15a84d52a0d5f8fe Mon Sep 17 00:00:00 2001 From: Himanshu Gupta Date: Wed, 17 Jun 2026 13:05:13 +0300 Subject: [PATCH 1/2] fix(webview): keep same-origin URLs in app --- .../android/webview/WebViewActivity.kt | 2 +- .../android/webview/WebViewUrlOverride.kt | 8 ++++ .../android/webview/WebViewUrlOverrideTest.kt | 48 +++++++++++++++++++ 3 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 app/src/main/kotlin/io/homeassistant/companion/android/webview/WebViewUrlOverride.kt create mode 100644 app/src/test/kotlin/io/homeassistant/companion/android/webview/WebViewUrlOverrideTest.kt diff --git a/app/src/main/kotlin/io/homeassistant/companion/android/webview/WebViewActivity.kt b/app/src/main/kotlin/io/homeassistant/companion/android/webview/WebViewActivity.kt index 32acf2abf69..6a6df1fa83d 100644 --- a/app/src/main/kotlin/io/homeassistant/companion/android/webview/WebViewActivity.kt +++ b/app/src/main/kotlin/io/homeassistant/companion/android/webview/WebViewActivity.kt @@ -653,7 +653,7 @@ class WebViewActivity : startActivity(intent) } return true - } else if (!webView.url.toString().contains(it)) { + } else if (shouldOpenInExternalBrowser(currentUrl = webView.url, targetUrl = it.toUri())) { Timber.d("Launching browser") val browserIntent = Intent(Intent.ACTION_VIEW, it.toUri()) startActivity(browserIntent) diff --git a/app/src/main/kotlin/io/homeassistant/companion/android/webview/WebViewUrlOverride.kt b/app/src/main/kotlin/io/homeassistant/companion/android/webview/WebViewUrlOverride.kt new file mode 100644 index 00000000000..314991e1c7d --- /dev/null +++ b/app/src/main/kotlin/io/homeassistant/companion/android/webview/WebViewUrlOverride.kt @@ -0,0 +1,8 @@ +package io.homeassistant.companion.android.webview + +import android.net.Uri +import io.homeassistant.companion.android.util.hasSameOrigin + +internal fun shouldOpenInExternalBrowser(currentUrl: String?, targetUrl: Uri): Boolean { + return !targetUrl.hasSameOrigin(currentUrl) +} diff --git a/app/src/test/kotlin/io/homeassistant/companion/android/webview/WebViewUrlOverrideTest.kt b/app/src/test/kotlin/io/homeassistant/companion/android/webview/WebViewUrlOverrideTest.kt new file mode 100644 index 00000000000..4fbbc37cc27 --- /dev/null +++ b/app/src/test/kotlin/io/homeassistant/companion/android/webview/WebViewUrlOverrideTest.kt @@ -0,0 +1,48 @@ +package io.homeassistant.companion.android.webview + +import android.net.Uri +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner + +@RunWith(RobolectricTestRunner::class) +class WebViewUrlOverrideTest { + + @Test + fun `Given same origin URL with different path then WebView should handle it`() { + val targetUrl = Uri.parse("http://homeassistant.local:8123/lovelace/default") + + val shouldOpen = shouldOpenInExternalBrowser( + currentUrl = "http://homeassistant.local:8123/", + targetUrl = targetUrl, + ) + + assertFalse(shouldOpen) + } + + @Test + fun `Given different origin URL then external browser should handle it`() { + val targetUrl = Uri.parse("https://www.home-assistant.io/docs") + + val shouldOpen = shouldOpenInExternalBrowser( + currentUrl = "http://homeassistant.local:8123/", + targetUrl = targetUrl, + ) + + assertTrue(shouldOpen) + } + + @Test + fun `Given missing current URL then external browser should handle it`() { + val targetUrl = Uri.parse("http://homeassistant.local:8123/lovelace/default") + + val shouldOpen = shouldOpenInExternalBrowser( + currentUrl = null, + targetUrl = targetUrl, + ) + + assertTrue(shouldOpen) + } +} From 5298fec4b6b65c3ad6374270bf490975ed459190 Mon Sep 17 00:00:00 2001 From: Himanshu Gupta Date: Thu, 18 Jun 2026 12:13:57 +0300 Subject: [PATCH 2/2] fix(webview): handle non-web URL overrides safely --- .../android/webview/WebViewActivity.kt | 7 +++- .../android/webview/WebViewUrlOverride.kt | 7 ++++ .../android/webview/WebViewUrlOverrideTest.kt | 39 +++++++++++++++++++ 3 files changed, 51 insertions(+), 2 deletions(-) diff --git a/app/src/main/kotlin/io/homeassistant/companion/android/webview/WebViewActivity.kt b/app/src/main/kotlin/io/homeassistant/companion/android/webview/WebViewActivity.kt index 6a6df1fa83d..0afc0f6a4c1 100644 --- a/app/src/main/kotlin/io/homeassistant/companion/android/webview/WebViewActivity.kt +++ b/app/src/main/kotlin/io/homeassistant/companion/android/webview/WebViewActivity.kt @@ -653,9 +653,12 @@ class WebViewActivity : startActivity(intent) } return true - } else if (shouldOpenInExternalBrowser(currentUrl = webView.url, targetUrl = it.toUri())) { + } + + val targetUrl = it.toUri() + if (shouldOpenInExternalBrowser(currentUrl = webView.url, targetUrl = targetUrl)) { Timber.d("Launching browser") - val browserIntent = Intent(Intent.ACTION_VIEW, it.toUri()) + val browserIntent = Intent(Intent.ACTION_VIEW, targetUrl) startActivity(browserIntent) return true } else { diff --git a/app/src/main/kotlin/io/homeassistant/companion/android/webview/WebViewUrlOverride.kt b/app/src/main/kotlin/io/homeassistant/companion/android/webview/WebViewUrlOverride.kt index 314991e1c7d..228f9039f94 100644 --- a/app/src/main/kotlin/io/homeassistant/companion/android/webview/WebViewUrlOverride.kt +++ b/app/src/main/kotlin/io/homeassistant/companion/android/webview/WebViewUrlOverride.kt @@ -4,5 +4,12 @@ import android.net.Uri import io.homeassistant.companion.android.util.hasSameOrigin internal fun shouldOpenInExternalBrowser(currentUrl: String?, targetUrl: Uri): Boolean { + if (!targetUrl.isHttpOrHttpsWithHost()) return true + return !targetUrl.hasSameOrigin(currentUrl) } + +private fun Uri.isHttpOrHttpsWithHost(): Boolean { + return host != null && + (scheme.equals("http", ignoreCase = true) || scheme.equals("https", ignoreCase = true)) +} diff --git a/app/src/test/kotlin/io/homeassistant/companion/android/webview/WebViewUrlOverrideTest.kt b/app/src/test/kotlin/io/homeassistant/companion/android/webview/WebViewUrlOverrideTest.kt index 4fbbc37cc27..7e81ba7264a 100644 --- a/app/src/test/kotlin/io/homeassistant/companion/android/webview/WebViewUrlOverrideTest.kt +++ b/app/src/test/kotlin/io/homeassistant/companion/android/webview/WebViewUrlOverrideTest.kt @@ -1,13 +1,16 @@ package io.homeassistant.companion.android.webview import android.net.Uri +import dagger.hilt.android.testing.HiltTestApplication import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.junit.Test import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner +import org.robolectric.annotation.Config @RunWith(RobolectricTestRunner::class) +@Config(application = HiltTestApplication::class) class WebViewUrlOverrideTest { @Test @@ -45,4 +48,40 @@ class WebViewUrlOverrideTest { assertTrue(shouldOpen) } + + @Test + fun `Given mail URL then external browser should handle it`() { + val targetUrl = Uri.parse("mailto:user@example.com") + + val shouldOpen = shouldOpenInExternalBrowser( + currentUrl = "http://homeassistant.local:8123/", + targetUrl = targetUrl, + ) + + assertTrue(shouldOpen) + } + + @Test + fun `Given telephone URL then external browser should handle it`() { + val targetUrl = Uri.parse("tel:+15555555555") + + val shouldOpen = shouldOpenInExternalBrowser( + currentUrl = "http://homeassistant.local:8123/", + targetUrl = targetUrl, + ) + + assertTrue(shouldOpen) + } + + @Test + fun `Given relative URL then external browser should handle it`() { + val targetUrl = Uri.parse("/lovelace/default") + + val shouldOpen = shouldOpenInExternalBrowser( + currentUrl = "http://homeassistant.local:8123/", + targetUrl = targetUrl, + ) + + assertTrue(shouldOpen) + } }