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>
78 lines
3.5 KiB
Java
78 lines
3.5 KiB
Java
package org.skatespots.app;
|
|
|
|
import android.content.Intent;
|
|
import android.os.Bundle;
|
|
import android.webkit.WebSettings;
|
|
import android.webkit.WebView;
|
|
|
|
import androidx.activity.result.ActivityResultLauncher;
|
|
import androidx.activity.result.contract.ActivityResultContracts;
|
|
|
|
import com.getcapacitor.BridgeActivity;
|
|
|
|
/**
|
|
* SkateSpots launcher activity.
|
|
*
|
|
* SAFE BY DESIGN: this extends the stock Capacitor BridgeActivity and does NOT
|
|
* override the WebViewClient (an earlier custom version did and caused a
|
|
* black-screen crash). The ONLY thing we add is the registration of the
|
|
* ActivityResultLauncher used by AmberIntentPlugin.
|
|
*
|
|
* WHY THE LAUNCHER LIVES HERE
|
|
* ───────────────────────────
|
|
* AndroidX requires registerForActivityResult(...) to be called BEFORE the
|
|
* Activity is STARTED (i.e. during onCreate). A Capacitor Plugin is instantiated
|
|
* too late to register its own launcher, so we create it here and hand it to the
|
|
* plugin via AmberIntentPlugin.setLauncher(). Results are routed straight into
|
|
* AmberIntentPlugin.handleResult().
|
|
*
|
|
* This is the Amethyst NIP-55 pattern: a foreground-intent signer whose result
|
|
* comes back through a properly-registered ActivityResultLauncher — the reliable
|
|
* path that OPENS Amber's Approve/Reject screen and never hangs, on stock Android
|
|
* and on GrapheneOS alike.
|
|
*
|
|
* Injected into the generated android/ project at build time by
|
|
* scripts/inject-main-activity.mjs.
|
|
*/
|
|
public class MainActivity extends BridgeActivity {
|
|
|
|
private ActivityResultLauncher<Intent> amberLauncher;
|
|
|
|
@Override
|
|
public void onCreate(Bundle savedInstanceState) {
|
|
// Register the launcher BEFORE super.onCreate() puts us past STARTED.
|
|
amberLauncher = registerForActivityResult(
|
|
new ActivityResultContracts.StartActivityForResult(),
|
|
result -> AmberIntentPlugin.handleResult(result)
|
|
);
|
|
AmberIntentPlugin.setLauncher(amberLauncher);
|
|
|
|
// Register our native plugin so the WebView can call it.
|
|
registerPlugin(AmberIntentPlugin.class);
|
|
|
|
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.
|
|
}
|
|
}
|
|
}
|