aboutsummaryrefslogtreecommitdiffstats
path: root/android/app/src/twa/java
diff options
context:
space:
mode:
Diffstat (limited to 'android/app/src/twa/java')
-rw-r--r--android/app/src/twa/java/io/coinos/app/IntentActivity.java35
-rw-r--r--android/app/src/twa/java/io/coinos/app/LauncherActivity.java54
-rw-r--r--android/app/src/twa/java/io/coinos/app/Wake.java21
3 files changed, 110 insertions, 0 deletions
diff --git a/android/app/src/twa/java/io/coinos/app/IntentActivity.java b/android/app/src/twa/java/io/coinos/app/IntentActivity.java
new file mode 100644
index 0000000..3de1d62
--- /dev/null
+++ b/android/app/src/twa/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/twa/java/io/coinos/app/LauncherActivity.java b/android/app/src/twa/java/io/coinos/app/LauncherActivity.java
new file mode 100644
index 0000000..654265b
--- /dev/null
+++ b/android/app/src/twa/java/io/coinos/app/LauncherActivity.java
@@ -0,0 +1,54 @@
+package io.coinos.app;
+
+import android.Manifest;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+
+import java.net.URLEncoder;
+
+/**
+ * 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.
+ *
+ * <p>It also carries the UnifiedPush endpoint across: the Android side is
+ * what a distributor talks to, and the web app is what knows which pubkeys
+ * to watch, so the endpoint rides into the web app as ?up= exactly the way
+ * payment intents ride in as ?u=.
+ */
+public class LauncherActivity
+ extends com.google.androidbrowserhelper.trusted.LauncherActivity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ // Ask the distributor on every launch: it's idempotent, and it's how a
+ // distributor installed after us ever gets noticed.
+ Push.register(this);
+ // Android 13+ won't show our notifications without this, and the TWA's
+ // own permission covers the browser's, not ours.
+ if (Build.VERSION.SDK_INT >= 33
+ && checkSelfPermission(Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED
+ && Push.distributor(this) != null) {
+ try { requestPermissions(new String[] { Manifest.permission.POST_NOTIFICATIONS }, 1); } catch (Exception ignored) {}
+ }
+ }
+
+ @Override
+ protected Uri getLaunchingUrl() {
+ Uri data = getIntent().getData();
+ Uri base = (data != null && "v3.coinos.io".equals(data.getHost())) ? data : super.getLaunchingUrl();
+ String endpoint = Push.endpoint(this);
+ if (endpoint == null || Push.endpointDelivered(this)) return base;
+ try {
+ String sep = base.toString().contains("?") ? "&" : "?";
+ Uri withEndpoint = Uri.parse(base + sep + "up=" + URLEncoder.encode(endpoint, "UTF-8"));
+ Push.markEndpointDelivered(this);
+ return withEndpoint;
+ } catch (Exception e) {
+ return base;
+ }
+ }
+}
diff --git a/android/app/src/twa/java/io/coinos/app/Wake.java b/android/app/src/twa/java/io/coinos/app/Wake.java
new file mode 100644
index 0000000..558a6f5
--- /dev/null
+++ b/android/app/src/twa/java/io/coinos/app/Wake.java
@@ -0,0 +1,21 @@
+package io.coinos.app;
+
+import android.content.Context;
+
+/**
+ * Whether this build can wake the wallet itself.
+ *
+ * <p>This is the TWA build's answer: no. The wallet lives in Chrome's storage
+ * for its own origin, and nothing in this app can reach into that, so a push
+ * can only raise a notification for someone to tap. The WebView build has its
+ * own version of this class (src/webview) that says yes. One class per
+ * flavor, so the shared receiver needs no flavor checks.
+ */
+final class Wake {
+ private Wake() {}
+
+ /** @return true when the wallet was woken and the notification isn't needed. */
+ static boolean start(Context context, String payload) {
+ return false;
+ }
+}