summaryrefslogtreecommitdiff
path: root/android
diff options
context:
space:
mode:
authorPeter Stone <thepeterstone@gmail.com>2026-08-06 09:30:36 +0000
committerPeter Stone <thepeterstone@gmail.com>2026-08-06 09:30:36 +0000
commitab76bcae1324aadb6d163e565f5994d0db10ca61 (patch)
treeb086ec7650a8185f1437598741114f49b3be5568 /android
parentf479211f0885776c379c598dac738ac08ea72548 (diff)
Widget: wrap the web dashboard in a WebView (DashboardActivity)
First cut per the 2026-08-06 feasibility check (verdict: easy, no architecture blockers): a plain WebView pointed at the configured server URL, cookie jar enabled for the existing session-cookie login (internal/auth/middleware.go's RequireAuth) -- no separate auth bridge needed, the widget's own bearer token is a completely different scheme and doesn't need to touch this at all. External links (e.g. a calendar event's source URL) escape to the user's real browser instead of getting stuck in the WebView. No deep-linking to specific tabs, no native-rendered chrome -- this is the minimal first step to validate the wrapped experience is worth building further, not the final shape.
Diffstat (limited to 'android')
-rw-r--r--android/app/src/main/AndroidManifest.xml6
-rw-r--r--android/app/src/main/java/org/terst/doot/widget/ui/DashboardActivity.kt76
2 files changed, 82 insertions, 0 deletions
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index ca96e71..c2200ba 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -33,6 +33,12 @@
android:exported="false"
android:windowSoftInputMode="adjustResize" />
+ <!-- Full dashboard, wrapped in a WebView (see DashboardActivity's doc comment) -->
+ <activity
+ android:name=".ui.DashboardActivity"
+ android:exported="false"
+ android:label="Doot" />
+
<!-- Settings activity (also the widget config screen) -->
<activity
android:name=".ui.SettingsActivity"
diff --git a/android/app/src/main/java/org/terst/doot/widget/ui/DashboardActivity.kt b/android/app/src/main/java/org/terst/doot/widget/ui/DashboardActivity.kt
new file mode 100644
index 0000000..f5ae744
--- /dev/null
+++ b/android/app/src/main/java/org/terst/doot/widget/ui/DashboardActivity.kt
@@ -0,0 +1,76 @@
+package org.terst.doot.widget.ui
+
+import android.content.Intent
+import android.net.Uri
+import android.os.Bundle
+import android.view.KeyEvent
+import android.webkit.CookieManager
+import android.webkit.WebView
+import android.webkit.WebViewClient
+import androidx.activity.ComponentActivity
+import androidx.core.net.toUri
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.launch
+import androidx.lifecycle.lifecycleScope
+import org.terst.doot.widget.data.Keys
+import org.terst.doot.widget.data.dataStore
+
+/**
+ * Wraps the existing web dashboard in a plain WebView -- the same session-cookie
+ * login flow a browser would use (see internal/auth/middleware.go's RequireAuth),
+ * no separate auth bridge needed since the widget's own bearer token is a
+ * completely different scheme. First cut per the 2026-08-06 feasibility check:
+ * one Activity, no deep-linking to specific tabs, no native-rendered chrome --
+ * just prove the wrapped experience is worth building further before investing
+ * in anything past this.
+ */
+class DashboardActivity : ComponentActivity() {
+
+ private lateinit var webView: WebView
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ webView = WebView(this).apply {
+ settings.javaScriptEnabled = true
+ settings.domStorageEnabled = true
+ settings.setSupportZoom(false)
+ webViewClient = object : WebViewClient() {
+ override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean {
+ val uri = url.toUri()
+ val sameHost = uri.host == null || uri.host == serverHost
+ if (sameHost) return false
+ // External links (e.g. a calendar event's source URL) open in the
+ // user's actual browser/app instead of getting stuck in this WebView.
+ startActivity(Intent(Intent.ACTION_VIEW, uri))
+ return true
+ }
+ }
+ }
+ setContentView(webView)
+
+ CookieManager.getInstance().setAcceptCookie(true)
+ CookieManager.getInstance().setAcceptThirdPartyCookies(webView, true)
+
+ lifecycleScope.launch {
+ val prefs = dataStore.data.first()
+ val url = prefs[Keys.SERVER_URL]?.trimEnd('/')
+ if (url.isNullOrBlank()) {
+ finish()
+ return@launch
+ }
+ serverHost = url.toUri().host
+ webView.loadUrl(url)
+ }
+ }
+
+ private var serverHost: String? = null
+
+ override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
+ if (keyCode == KeyEvent.KEYCODE_BACK && webView.canGoBack()) {
+ webView.goBack()
+ return true
+ }
+ return super.onKeyDown(keyCode, event)
+ }
+}