Amber never appeared for delete/accept-invite/zap because Capacitor's default WebView silently drops JS-initiated intent:/nostrsigner: navigations — the request that opens Amber never reached the OS. Login worked only because it uses the plugin's native startActivityForResult. Add a custom MainActivity (android-templates/MainActivity.java) that overrides shouldOverrideUrlLoading to launch intent:/nostrsigner: URLs via startActivity() — a real native launch that opens Amber on GrapheneOS, matching login. Injected into the generated android/ project at build time by inject-main-activity.mjs, wired into android:build/sync/init. Simplify the JS launcher to navigate the WebView (now intercepted natively). Bump to 1.2.8. Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
101 lines
3.8 KiB
Java
101 lines
3.8 KiB
Java
package org.skatespots.app;
|
|
|
|
import android.content.Intent;
|
|
import android.net.Uri;
|
|
import android.os.Bundle;
|
|
import android.util.Log;
|
|
import android.webkit.WebView;
|
|
import android.webkit.WebViewClient;
|
|
|
|
import com.getcapacitor.BridgeActivity;
|
|
|
|
/**
|
|
* Custom MainActivity that lets the WebView hand OFF `intent:` / `nostrsigner:`
|
|
* URLs to the Android OS so Amber's approval screen actually opens.
|
|
*
|
|
* WHY THIS EXISTS
|
|
* ───────────────
|
|
* When the JS signer navigates to an `intent:...;scheme=nostrsigner;...;end`
|
|
* URL, Capacitor's default WebView treats it as a page navigation and silently
|
|
* DROPS it — so Amber never launches and every signing action (delete, accept
|
|
* crew invite, zap, profile edit) hangs forever. Login works only because it
|
|
* goes through the native plugin's startActivityForResult; JS-initiated intents
|
|
* were never reaching the OS. On GrapheneOS this is enforced strictly.
|
|
*
|
|
* The fix: override shouldOverrideUrlLoading. Whenever the WebView tries to load
|
|
* an `intent:` or `nostrsigner:` URL, we parse it and call startActivity()
|
|
* ourselves — a REAL native launch that opens Amber, exactly like login. The
|
|
* result comes back via the app's custom-scheme deep link
|
|
* (org.skatespots.app://amber-callback...), handled by DeepLinkHandler /
|
|
* resolveAmberIntent in the web layer.
|
|
*
|
|
* This file is injected into the generated android/ project at build time by
|
|
* scripts/inject-main-activity.mjs (android/ is not committed).
|
|
*/
|
|
public class MainActivity extends BridgeActivity {
|
|
|
|
private static final String TAG = "SkateSpotsAmber";
|
|
|
|
@Override
|
|
public void onCreate(Bundle savedInstanceState) {
|
|
super.onCreate(savedInstanceState);
|
|
|
|
final WebView webView = getBridge().getWebView();
|
|
if (webView == null) {
|
|
return;
|
|
}
|
|
|
|
final WebViewClient existing = new WebViewClient();
|
|
|
|
webView.setWebViewClient(new WebViewClient() {
|
|
@Override
|
|
public boolean shouldOverrideUrlLoading(WebView view, String url) {
|
|
if (handleExternalScheme(url)) {
|
|
return true;
|
|
}
|
|
return super.shouldOverrideUrlLoading(view, url);
|
|
}
|
|
|
|
@Override
|
|
public boolean shouldOverrideUrlLoading(WebView view, android.webkit.WebResourceRequest request) {
|
|
final String url = request.getUrl() != null ? request.getUrl().toString() : null;
|
|
if (url != null && handleExternalScheme(url)) {
|
|
return true;
|
|
}
|
|
return super.shouldOverrideUrlLoading(view, request);
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* If the URL is an Amber/signer intent, launch it natively and return true.
|
|
* Returns false for normal in-app URLs so Capacitor handles them as usual.
|
|
*/
|
|
private boolean handleExternalScheme(String url) {
|
|
if (url == null) {
|
|
return false;
|
|
}
|
|
|
|
try {
|
|
if (url.startsWith("intent:")) {
|
|
// Parse the full Android intent: URI (carries scheme, extras, end).
|
|
Intent intent = Intent.parseUri(url, Intent.URI_INTENT_SCHEME);
|
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
startActivity(intent);
|
|
return true;
|
|
}
|
|
|
|
if (url.startsWith("nostrsigner:")) {
|
|
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
|
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
startActivity(intent);
|
|
return true;
|
|
}
|
|
} catch (Exception e) {
|
|
Log.e(TAG, "Failed to launch external signer intent: " + e.getMessage(), e);
|
|
// Fall through — let the WebView try to handle it (will no-op safely).
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|