fix(browser): show load progress once, on the pill grabber

The full-screen nsite/napplet and web browser windows drew two progress
indicators while a page loaded: the 3dp View ProgressBar pinned to the top
edge (added in June, before the pill existed) and the progress line under
the pill's grabber that the Compose pill redesign added. The redesign
moved loading state onto the grabber but never removed the old bar, which
also painted over the grabber's top edge.

Drop the old View bar from NappletHostActivity and NappletBrowserActivity.
The grabber line (and the ring around Stop in the open pill) is now the
only indicator, which matches the embedded tabs and the design doc.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JmY5pZFXST3LWsEr9hcHHr
This commit is contained in:
Claude
2026-10-02 15:00:24 +00:00
parent 6a699dcc4c
commit 4a146f874a
2 changed files with 7 additions and 49 deletions
@@ -27,7 +27,6 @@ import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.content.pm.PackageManager
import android.content.res.ColorStateList
import android.content.res.Configuration
import android.graphics.Bitmap
import android.graphics.Color
@@ -142,10 +141,6 @@ class NappletBrowserActivity : ComponentActivity() {
// The pill, find, console and page dialogs — the shared Compose chrome (see BrowserChromeHost).
private var chrome: BrowserChromeHost? = null
// A thin determinate progress bar pinned to the top edge (browser-style), driven by the chrome
// client's onProgressChanged; hidden at 100%.
private val topProgressBar by lazy { buildTopProgressBar() }
// The NIP-07 shim + browser extras, injected at document start in this window and in its popups.
private var shimJs: String = ""
@@ -348,8 +343,6 @@ class NappletBrowserActivity : ComponentActivity() {
}
this.root = root
chrome = buildChrome().also { it.attach(root) }
// Added last so the thin loading bar paints above the content (and over the grabber's top edge).
root.addView(topProgressBar)
setContentView(root)
// Pad by the system bars + cutout AND the IME: on an edge-to-edge window (enforced for targetSdk
// 35+ on Android 15+) windowSoftInputMode=adjustResize no longer shrinks the window, so without
@@ -1840,26 +1833,13 @@ class NappletBrowserActivity : ComponentActivity() {
}
/**
* A thin determinate progress bar pinned to the top edge, like a browser's. Driven by
* [BrowserChromeClient.onProgressChanged]: visible while the page loads and gone at 100%.
* Feeds the page's load progress to the pill, which draws it under the grabber (and as a ring around
* Stop when open); cleared at 100%.
*/
private fun buildTopProgressBar(): ProgressBar =
ProgressBar(this, null, android.R.attr.progressBarStyleHorizontal).apply {
max = 100
isIndeterminate = false
visibility = View.GONE
progressTintList = ColorStateList.valueOf(resolveThemeColor(android.R.attr.colorPrimary))
layoutParams = FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, dp(3), Gravity.TOP)
}
/** Shows the thin top bar at [progress]% while loading, hiding it once the page is fully loaded. */
private fun updateLoadProgress(progress: Int) {
if (progress >= 100) {
topProgressBar.visibility = View.GONE
updateUi { copy(loadProgress = null, chrome = chrome.copy(isLoading = false)) }
} else {
topProgressBar.progress = progress
topProgressBar.visibility = View.VISIBLE
updateUi { copy(loadProgress = progress / 100f) }
}
}
@@ -24,7 +24,6 @@ import android.annotation.SuppressLint
import android.content.ComponentName
import android.content.Intent
import android.content.ServiceConnection
import android.content.res.ColorStateList
import android.content.res.Configuration
import android.net.Uri
import android.os.Bundle
@@ -198,10 +197,6 @@ class NappletHostActivity : ComponentActivity() {
// first paint, so there's never a blank/dark gap between the index probe and the shell's first frame.
private var loadingView: View? = null
// A thin determinate progress bar pinned to the top edge (browser-style), driven by the
// WebChromeClient's onProgressChanged; hidden at 100%.
private val topProgressBar by lazy { buildTopProgressBar() }
// The trusted pull-down pill, find and the developer console — the shared Compose chrome.
private var chrome: BrowserChromeHost? = null
@@ -354,9 +349,6 @@ class NappletHostActivity : ComponentActivity() {
addView(contentFrame, FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT))
}
chrome = buildChrome().also { it.attach(root) }
// Added last so the thin loading bar paints above the content (and over the grabber's top edge); it's
// GONE except while loading, so it never obscures the trusted chrome.
root.addView(topProgressBar)
setContentView(root)
// Activities are edge-to-edge by default on recent Android; pad by the system bar, display-cutout
// and IME insets so neither the chrome nor the applet draws under the system bars or the soft
@@ -736,7 +728,7 @@ class NappletHostActivity : ComponentActivity() {
}
}
/** Drives the top loading bar, opens the file picker, and forwards `console.*` output to the panel. */
/** Feeds load progress to the pill, opens the file picker, and forwards `console.*` output to the panel. */
private inner class NappletWebChromeClient : WebChromeClient() {
/**
* Without this override WebView's base implementation returns false and shows no picker at all, so
@@ -784,14 +776,14 @@ class NappletHostActivity : ComponentActivity() {
return true
}
/** Shows the thin top bar at [progress]% while loading, hiding it once the page is fully loaded. */
/**
* Feeds the page's load progress to the pill, which draws it under the grabber (and as a ring around
* Stop when open); cleared at 100%.
*/
private fun updateLoadProgress(progress: Int) {
if (progress >= 100) {
topProgressBar.visibility = View.GONE
chrome?.let { it.ui = it.ui.copy(loadProgress = null, chrome = it.ui.chrome.copy(isLoading = false)) }
} else {
topProgressBar.progress = progress
topProgressBar.visibility = View.VISIBLE
chrome?.let { it.ui = it.ui.copy(loadProgress = progress / 100f, chrome = it.ui.chrome.copy(isLoading = true)) }
}
}
@@ -1082,20 +1074,6 @@ class NappletHostActivity : ComponentActivity() {
if (brokerMessenger != null) sendToBroker(msg)
}
/**
* A thin determinate progress bar pinned to the top edge, like a browser's. Driven by
* [NappletWebChromeClient.onProgressChanged]: visible while the shell + verified blobs load and gone
* at 100%, so a slow load (e.g. a large bundle over Tor) shows progress instead of a blank dark WebView.
*/
private fun buildTopProgressBar(): ProgressBar =
ProgressBar(this, null, android.R.attr.progressBarStyleHorizontal).apply {
max = 100
isIndeterminate = false
visibility = View.GONE
progressTintList = ColorStateList.valueOf(resolveThemeColor(android.R.attr.colorPrimary))
layoutParams = FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, dp(3), Gravity.TOP)
}
/** Persists the new routing choice in the main process, then relaunches this screen to apply it. */
private fun setNetworkMode(newUseTor: Boolean) {
val msg =