From e5e375d8ea2f1193ffe01ff02639f002f91f8035 Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Fri, 4 Sep 2026 20:53:13 -0700 Subject: Android wrapper v1.1: scheme + NFC intents move onto a per-intent trampoline (IntentActivity, noHistory) — a TWA already on screen ignores a new intent delivered to its still-alive launcher, so a second lightning: tap did nothing; the trampoline maps the URI to ?u= form and relaunches the TWA with CLEAR_TOP, which navigates the open app. Verified on a real phone over adb: URL-bar-less fullscreen (assetlinks verified after the site began serving /.well-known/assetlinks.json — the deploy hook's dist/* glob was blind to dot-directories, fixed on cs), lightning: intent lands on Send with the invoice filled, and a second bitcoin: intent fired while the app was open navigated it (BIP-21 parsed, address + amount). NFC NDEF filters ride the trampoline for bolt cards / NDEF tags MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01MGzU12QrAA4G6BYGzGVdXz --- android/app/build.gradle | 4 +-- android/app/src/main/AndroidManifest.xml | 18 +++++++---- .../main/java/io/coinos/app/IntentActivity.java | 35 ++++++++++++++++++++++ .../main/java/io/coinos/app/LauncherActivity.java | 16 +++------- 4 files changed, 53 insertions(+), 20 deletions(-) create mode 100644 android/app/src/main/java/io/coinos/app/IntentActivity.java (limited to 'android') diff --git a/android/app/build.gradle b/android/app/build.gradle index 2d9acbd..0378159 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -11,8 +11,8 @@ android { applicationId 'io.coinos.app' minSdk 24 targetSdk 35 - versionCode 1 - versionName '1.0' + versionCode 2 + versionName '1.1' } signingConfigs { release { diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index c6b7a01..4eb96a7 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -32,9 +32,18 @@ - + + + @@ -46,9 +55,6 @@ - diff --git a/android/app/src/main/java/io/coinos/app/IntentActivity.java b/android/app/src/main/java/io/coinos/app/IntentActivity.java new file mode 100644 index 0000000..3de1d62 --- /dev/null +++ b/android/app/src/main/java/io/coinos/app/IntentActivity.java @@ -0,0 +1,35 @@ +package io.coinos.app; + +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; + +/** + * Trampoline for scheme + NFC intents. A TWA already on screen ignores a + * new intent delivered to its (still-alive) launcher, so a second + * lightning: tap used to do nothing. This activity exists per-intent: + * it maps the URI to the web app's ?u= form and relaunches LauncherActivity + * with CLEAR_TOP — the Chrome activity above it is cleared, the launcher is + * recreated with the new data, and the TWA navigates. Then it's gone. + */ +public class IntentActivity extends android.app.Activity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + Uri url = Uri.parse("https://v3.coinos.io/"); + Uri data = getIntent().getData(); + if (data != null) { + String scheme = data.getScheme(); + if (scheme != null && !scheme.equals("https") && !scheme.equals("http")) { + url = Uri.parse("https://v3.coinos.io/?u=" + Uri.encode(data.toString())); + } else { + url = data; + } + } + Intent i = new Intent(this, LauncherActivity.class); + i.setData(url); + i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); + startActivity(i); + finish(); + } +} diff --git a/android/app/src/main/java/io/coinos/app/LauncherActivity.java b/android/app/src/main/java/io/coinos/app/LauncherActivity.java index d0b56bb..c3950ac 100644 --- a/android/app/src/main/java/io/coinos/app/LauncherActivity.java +++ b/android/app/src/main/java/io/coinos/app/LauncherActivity.java @@ -3,11 +3,9 @@ package io.coinos.app; import android.net.Uri; /** - * Forwards scheme intents into the web app. A lightning:/bitcoin:/nostr:/ - * lnurl: URI arrives as this activity's data; the web app receives it as - * https://v3.coinos.io/?u= and routes it (send form - * for payments, profile for nostr npubs). https links to the site pass - * through untouched, everything else opens the default URL. + * The TWA launcher. Scheme/NFC intents arrive via IntentActivity (the + * trampoline that survives the running-TWA case); this activity only needs + * to honor a full URL handed to it, falling back to the site root. */ public class LauncherActivity extends com.google.androidbrowserhelper.trusted.LauncherActivity { @@ -15,13 +13,7 @@ public class LauncherActivity @Override protected Uri getLaunchingUrl() { Uri data = getIntent().getData(); - if (data != null) { - String scheme = data.getScheme(); - if (scheme != null && !scheme.equals("https") && !scheme.equals("http")) { - return Uri.parse("https://v3.coinos.io/?u=" + Uri.encode(data.toString())); - } - if ("v3.coinos.io".equals(data.getHost())) return data; - } + if (data != null && "v3.coinos.io".equals(data.getHost())) return data; return super.getLaunchingUrl(); } } -- cgit v1.2.3