Citrine: allow loopback ws:// in WebView so the local relay connects (1.3.5)
The real fix for "Citrine running but 0 connections": the app is served over https in the WebView and Citrine listens on plaintext ws://127.0.0.1, which modern Android WebViews block as mixed content BEFORE it reaches the network layer — allowMixedContent + the loopback network-security-config don't fully cover WebSocket mixed content. MainActivity now sets the bridge WebView's mixed-content mode to MIXED_CONTENT_ALWAYS_ALLOW after super.onCreate(), which lets the localhost ws:// connection through for every user. All real traffic stays wss:///https://; this only unblocks the loopback relay. The WebViewClient is left untouched (overriding it previously caused a black screen). Wrapped in try/catch so it can never crash the launcher. Also removed the temporary on-screen probe debug readout (kept the diagnostic fields in useCitrine, unused by UI, for future use). Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
This commit is contained in:
parent
97adfba7e0
commit
c7e890e65c
3 changed files with 26 additions and 12 deletions
|
|
@ -2,6 +2,8 @@ package org.skatespots.app;
|
||||||
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.webkit.WebSettings;
|
||||||
|
import android.webkit.WebView;
|
||||||
|
|
||||||
import androidx.activity.result.ActivityResultLauncher;
|
import androidx.activity.result.ActivityResultLauncher;
|
||||||
import androidx.activity.result.contract.ActivityResultContracts;
|
import androidx.activity.result.contract.ActivityResultContracts;
|
||||||
|
|
@ -49,5 +51,28 @@ public class MainActivity extends BridgeActivity {
|
||||||
registerPlugin(AmberIntentPlugin.class);
|
registerPlugin(AmberIntentPlugin.class);
|
||||||
|
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
// ── Allow the on-device Citrine relay (ws://127.0.0.1) ──────────────
|
||||||
|
// The app is served to the WebView over the https scheme, and Citrine
|
||||||
|
// listens on a PLAINTEXT WebSocket (ws://, not wss://). Modern Android
|
||||||
|
// WebViews treat "secure page → insecure ws" as mixed content and block
|
||||||
|
// it BEFORE it ever reaches the network layer — which is why the local
|
||||||
|
// relay showed 0 connections despite allowMixedContent + the loopback
|
||||||
|
// network-security-config (neither fully covers WebSocket mixed content).
|
||||||
|
//
|
||||||
|
// MIXED_CONTENT_ALWAYS_ALLOW on the bridge WebView lets the localhost
|
||||||
|
// ws:// connection through. All real traffic remains wss:// / https://;
|
||||||
|
// this only unblocks the loopback relay. We do NOT touch the
|
||||||
|
// WebViewClient (overriding it previously caused a black-screen crash).
|
||||||
|
try {
|
||||||
|
WebView webView = getBridge().getWebView();
|
||||||
|
if (webView != null) {
|
||||||
|
WebSettings settings = webView.getSettings();
|
||||||
|
settings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
// Non-fatal: if this fails the app still runs, the local relay just
|
||||||
|
// won't connect. Never crash the launcher over an optional feature.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "skate-spots",
|
"name": "skate-spots",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "1.3.4",
|
"version": "1.3.5",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"homepage": "https://skate-spots.com",
|
"homepage": "https://skate-spots.com",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|
|
||||||
|
|
@ -490,17 +490,6 @@ function CitrineCard() {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Debug readout — shows the exact probe URL + failure reason on the
|
|
||||||
device so connectivity can be diagnosed without USB/adb. Only shown
|
|
||||||
while not connected. */}
|
|
||||||
{state !== 'live' && citrine.reason && citrine.reason !== 'not-android' && (
|
|
||||||
<div className="rounded-lg bg-black/40 border border-white/10 p-2.5 font-mono text-[10px] leading-relaxed text-gray-400">
|
|
||||||
<div className="text-gray-500">debug</div>
|
|
||||||
<div>url: <span className="text-gray-300">{citrine.probeUrl}</span></div>
|
|
||||||
<div>result: <span className="text-orange-300">{citrine.reason}</span></div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Privacy reassurance — always present when enabled */}
|
{/* Privacy reassurance — always present when enabled */}
|
||||||
<div className="flex items-start gap-2 rounded-xl bg-black/20 border border-white/5 p-2.5">
|
<div className="flex items-start gap-2 rounded-xl bg-black/20 border border-white/5 p-2.5">
|
||||||
<Shield className="w-3.5 h-3.5 text-white/40 flex-shrink-0 mt-0.5" />
|
<Shield className="w-3.5 h-3.5 text-white/40 flex-shrink-0 mt-0.5" />
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue