From 20b42d34432e5e1964c3af2b45d01a8d494770fe Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 22 Jun 2026 23:30:30 +0000 Subject: [PATCH] fix: prefetch off main thread; use real Tor logo in sandbox bar - StrictMode DiskReadViolation: PrefetchManifestBlobs read context.cacheDir (ensurePrivateCacheDirExists touches disk) on the composition dispatcher. Move it into withContext(Dispatchers.IO); the prefetch was already IO-bound. - Sandbox top-bar network indicator now uses the app's real Tor logo (ic_tor, copied into :nappletHost since it can't depend on :amethyst) via an ImageView, instead of the onion/globe emoji. Lit when routing through Tor, dimmed when the site loads over the open web. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_016ncMHuBBVHEf7spAoSssde --- .../amethyst/ui/note/types/StaticWebsite.kt | 12 +++++++++--- .../amethyst/napplethost/NappletHostActivity.kt | 16 ++++++++++------ nappletHost/src/main/res/drawable/ic_tor.xml | 9 +++++++++ 3 files changed, 28 insertions(+), 9 deletions(-) create mode 100644 nappletHost/src/main/res/drawable/ic_tor.xml diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/StaticWebsite.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/StaticWebsite.kt index e354499c28..b9f71b520c 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/StaticWebsite.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/StaticWebsite.kt @@ -35,6 +35,8 @@ import com.vitorpamplona.quartz.nip5aStaticWebsites.RootSiteEvent import com.vitorpamplona.quartz.nip5aStaticWebsites.tags.PathTag import com.vitorpamplona.quartz.nip5dNapplets.NamedNappletEvent import com.vitorpamplona.quartz.nip5dNapplets.RootNappletEvent +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext @Composable fun RenderRootNappletEvent( @@ -187,9 +189,13 @@ private fun PrefetchManifestBlobs( ) { val context = LocalContext.current LaunchedEffect(paths, servers) { - runCatching { - val torPort = Amethyst.instance.torManager.activePortOrNull.value ?: -1 - NappletBlobPrefetcher.prefetch(paths, servers, context.cacheDir, torPort) + // Off the main thread: context.cacheDir touches disk (ensurePrivateCacheDirExists), so reading + // it on the composition dispatcher trips StrictMode. The prefetch itself is already IO-bound. + withContext(Dispatchers.IO) { + runCatching { + val torPort = Amethyst.instance.torManager.activePortOrNull.value ?: -1 + NappletBlobPrefetcher.prefetch(paths, servers, context.cacheDir, torPort) + } } } } diff --git a/nappletHost/src/main/kotlin/com/vitorpamplona/amethyst/napplethost/NappletHostActivity.kt b/nappletHost/src/main/kotlin/com/vitorpamplona/amethyst/napplethost/NappletHostActivity.kt index e72cc6a720..81800e9e8e 100644 --- a/nappletHost/src/main/kotlin/com/vitorpamplona/amethyst/napplethost/NappletHostActivity.kt +++ b/nappletHost/src/main/kotlin/com/vitorpamplona/amethyst/napplethost/NappletHostActivity.kt @@ -44,6 +44,7 @@ import android.webkit.WebView import android.webkit.WebViewClient import android.widget.Button import android.widget.FrameLayout +import android.widget.ImageView import android.widget.LinearLayout import android.widget.ProgressBar import android.widget.TextView @@ -672,18 +673,21 @@ class NappletHostActivity : ComponentActivity() { layoutParams = LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 1f) }, ) - // nSite network indicator + toggle: onion = Tor (private), globe = open web (IP visible). Only - // shown when this is a website-mode nSite AND Tor is active — otherwise there is nothing to - // route through. Its own click handler opens the network dialog without firing the access sheet. + // nSite network indicator + toggle: the Tor logo, lit when routing through Tor and dimmed when + // the site loads over the open web. Only shown for a website-mode nSite when Tor is active — + // otherwise there is nothing to route through. Its own click opens the network dialog (and, by + // consuming the click, doesn't also fire the access sheet bound to the bar). if (websiteMode && proxyPort > 0) { bar.addView( - TextView(this).apply { - text = if (useTor) "🧅" else "🌐" - textSize = 18f + ImageView(this).apply { + setImageResource(R.drawable.ic_tor) + setColorFilter(if (useTor) onSurface else resolveThemeColor(android.R.attr.textColorSecondary)) + alpha = if (useTor) 1f else 0.4f setPadding(0, 0, dp(12), 0) isClickable = true setOnClickListener { showNetworkDialog() } contentDescription = getString(if (useTor) R.string.napplet_net_tor_desc else R.string.napplet_net_open_desc) + layoutParams = LinearLayout.LayoutParams(dp(34), dp(22)) }, ) } diff --git a/nappletHost/src/main/res/drawable/ic_tor.xml b/nappletHost/src/main/res/drawable/ic_tor.xml new file mode 100644 index 0000000000..e203b8e733 --- /dev/null +++ b/nappletHost/src/main/res/drawable/ic_tor.xml @@ -0,0 +1,9 @@ + + +