fix(companion): edge-to-edge app webview, whole-overlay touch shield, no raw IP titles — 0.5.19
Demo images / Build & push demo images (push) Successful in 3m0s
Demo images / Build & push demo images (push) Successful in 3m0s
- app webview draws behind the status bar again (the inset rework painted an opaque black bar there); page background owns the top - the in-app overlay eats EVERY touch its children don't handle — a near-miss on Close was falling through to the kiosk AIUI tab - loading screen never titles itself with a raw mesh IPv6/IP host - re-land vc37 connect fixes vc38 shipped without: pairing restarts the mesh immediately when consent exists; session warmer probes all targets concurrently (5s) instead of 20s each in sequence Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -11,8 +11,8 @@ android {
|
|||||||
applicationId = "com.archipelago.app"
|
applicationId = "com.archipelago.app"
|
||||||
minSdk = 26
|
minSdk = 26
|
||||||
targetSdk = 35
|
targetSdk = 35
|
||||||
versionCode = 38
|
versionCode = 39
|
||||||
versionName = "0.5.18"
|
versionName = "0.5.19"
|
||||||
|
|
||||||
vectorDrawables {
|
vectorDrawables {
|
||||||
useSupportLibrary = true
|
useSupportLibrary = true
|
||||||
|
|||||||
@@ -153,19 +153,28 @@ class ArchyVpnService : VpnService() {
|
|||||||
} catch (_: Exception) {
|
} catch (_: Exception) {
|
||||||
emptyList()
|
emptyList()
|
||||||
}.distinct()
|
}.distinct()
|
||||||
for ((ula, port) in targets) {
|
if (round == 0) Log.i(TAG, "session warmer: ${targets.map { it.first }}")
|
||||||
try {
|
// Probe all targets CONCURRENTLY with a short timeout — the
|
||||||
java.net.Socket().use { s ->
|
// old sequential 20s-per-target loop let one cold node starve
|
||||||
s.connect(
|
// every other target for the whole aggressive window.
|
||||||
java.net.InetSocketAddress(java.net.InetAddress.getByName(ula), port),
|
targets.map { (ula, port) ->
|
||||||
20_000,
|
launch {
|
||||||
)
|
try {
|
||||||
|
java.net.Socket().use { s ->
|
||||||
|
s.connect(
|
||||||
|
java.net.InetSocketAddress(
|
||||||
|
java.net.InetAddress.getByName(ula),
|
||||||
|
port,
|
||||||
|
),
|
||||||
|
5_000,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} catch (_: Exception) {
|
||||||
|
// Cold path / node away — the attempt still drove
|
||||||
|
// session establishment; try again next round.
|
||||||
}
|
}
|
||||||
} catch (_: Exception) {
|
|
||||||
// Cold path / node away — the connect attempt still
|
|
||||||
// drove session establishment; try again next round.
|
|
||||||
}
|
}
|
||||||
}
|
}.forEach { it.join() }
|
||||||
round++
|
round++
|
||||||
// Aggressive for the first ~minute (session bring-up), then a
|
// Aggressive for the first ~minute (session bring-up), then a
|
||||||
// slow keep-warm tick that costs nearly nothing.
|
// slow keep-warm tick that costs nearly nothing.
|
||||||
|
|||||||
@@ -41,7 +41,15 @@ object FipsManager {
|
|||||||
ensureIdentity(prefs)
|
ensureIdentity(prefs)
|
||||||
prefs.upsertNodePeer(info, alias)
|
prefs.upsertNodePeer(info, alias)
|
||||||
peersDirty = true
|
peersDirty = true
|
||||||
_consentNeeded.value = true
|
// Restart the mesh with the new peer RIGHT NOW when consent already
|
||||||
|
// exists — relying on the consentNeeded collector left a running
|
||||||
|
// mesh on the OLD peer list whenever the collector wasn't active
|
||||||
|
// (fresh pairings looked dead until a full app restart).
|
||||||
|
if (VpnService.prepare(context) == null) {
|
||||||
|
startService(context)
|
||||||
|
} else {
|
||||||
|
_consentNeeded.value = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Generate-once mesh identity. Returns null only if the RNG/native fails. */
|
/** Generate-once mesh identity. Returns null only if the RNG/native fails. */
|
||||||
|
|||||||
@@ -897,7 +897,17 @@ private fun InAppBrowser(
|
|||||||
fun isSameNode(u: String): Boolean =
|
fun isSameNode(u: String): Boolean =
|
||||||
isSameHost(u, serverUrl) || (meshUrl != null && isSameHost(u, meshUrl))
|
isSameHost(u, serverUrl) || (meshUrl != null && isSameHost(u, meshUrl))
|
||||||
var browser by remember { mutableStateOf<WebView?>(null) }
|
var browser by remember { mutableStateOf<WebView?>(null) }
|
||||||
var title by remember { mutableStateOf(android.net.Uri.parse(url).host ?: url) }
|
// Loader title: never show a raw IP host — a mesh ULA like
|
||||||
|
// [fd79:1aa:…] is technically the host but reads as garbage on the
|
||||||
|
// loading screen. Show a neutral name until the page reports its
|
||||||
|
// real <title> (onReceivedTitle upgrades it).
|
||||||
|
var title by remember {
|
||||||
|
mutableStateOf(
|
||||||
|
android.net.Uri.parse(url).host
|
||||||
|
?.takeUnless { it.contains(':') || it.matches(Regex("^\\d+(\\.\\d+){3}$")) }
|
||||||
|
?: "Archipelago",
|
||||||
|
)
|
||||||
|
}
|
||||||
var favicon by remember { mutableStateOf<Bitmap?>(null) }
|
var favicon by remember { mutableStateOf<Bitmap?>(null) }
|
||||||
var progress by remember { mutableIntStateOf(0) }
|
var progress by remember { mutableIntStateOf(0) }
|
||||||
var loading by remember { mutableStateOf(true) }
|
var loading by remember { mutableStateOf(true) }
|
||||||
@@ -935,12 +945,21 @@ private fun InAppBrowser(
|
|||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.background(SurfaceBlack)
|
.background(SurfaceBlack)
|
||||||
// Bottom inset handled by the touch-shield strip below the bar —
|
// Whole-overlay touch shield: every touch not handled by a child
|
||||||
// NOT by padding: a padded area only paints, it doesn't consume,
|
// (control-bar gaps, inset strips) dies here instead of falling
|
||||||
// so taps in the gesture strip fell straight THROUGH this overlay
|
// through to the kiosk's tab bar behind (a near-miss on Close
|
||||||
// into the kiosk's tab bar behind it (accidental AIUI-tab hits).
|
// was opening the AIUI tab underneath).
|
||||||
|
.clickable(
|
||||||
|
interactionSource = remember { MutableInteractionSource() },
|
||||||
|
indication = null,
|
||||||
|
onClick = {},
|
||||||
|
)
|
||||||
|
// Bottom inset handled by the touch-shield strip below the bar.
|
||||||
|
// No TOP inset padding: the WebView draws edge-to-edge behind the
|
||||||
|
// status bar so the app's own background fills it — the padded
|
||||||
|
// version painted an opaque black bar there (user-rejected look).
|
||||||
.windowInsetsPadding(
|
.windowInsetsPadding(
|
||||||
WindowInsets.safeDrawing.only(WindowInsetsSides.Horizontal + WindowInsetsSides.Top)
|
WindowInsets.safeDrawing.only(WindowInsetsSides.Horizontal)
|
||||||
),
|
),
|
||||||
) {
|
) {
|
||||||
// WebView + loading overlay fill the area above the bottom control bar.
|
// WebView + loading overlay fill the area above the bottom control bar.
|
||||||
|
|||||||
Binary file not shown.
Reference in New Issue
Block a user