aboutsummaryrefslogtreecommitdiffstats
path: root/android/app/src/main/AndroidManifest.xml
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-09-12 10:05:10 -0700
committerAdam Soltys <[email protected]>2026-09-12 10:05:10 -0700
commita846211262f37238c001e5210938749294f8db6a (patch)
treeeb2f1c313051a739ef18a32d39bc4988a63e44fd /android/app/src/main/AndroidManifest.xml
parentcab447766734cb99821c36988ebc8d27a0bce228 (diff)
downloadcoinosv3-a846211262f37238c001e5210938749294f8db6a.tar.gz
coinosv3-a846211262f37238c001e5210938749294f8db6a.zip
UnifiedPush: reach a phone that has no Google in it.
Web push on Android is Firebase's, so a phone without Google Play Services — GrapheneOS, CalyxOS, any de-Googled Android — cannot be woken by a web app at all. UnifiedPush is the way that doesn't involve Google: the user runs a distributor (ntfy and friends), apps ask it for an endpoint, and whoever wants to reach the device POSTs to that endpoint. THE WRAPPER speaks the protocol directly — four broadcasts each way, so it pulls in no library; an APK this small has no business growing a Kotlin runtime for eight intents. It asks the distributor on every launch (the call is idempotent, and it's how a distributor installed later gets noticed), raises a notification when a message arrives, and acknowledges it so the distributor stops resending. THE ENDPOINT reaches the web app on the launch URL as ?up=, the same road payment intents already travel — because the distributor talks to the app and only the web app knows which pubkeys are worth watching. It's registered with the notifier in place of a browser subscription, for notifications and for NWC alike, and needs neither permission nor push service. THE NOTIFIER sends to either kind: one deliver() that POSTs the payload to a UnifiedPush endpoint, or hands it to web-push as before. Same payloads, same 404/410 pruning. The endpoint is the only secret there, so it's never logged in full. Verified: a POST to a live ntfy endpoint delivers the payload byte for byte, so the app parses exactly what the notifier sent; the APK builds, carries the receiver, and is signed with the certificate assetlinks already pins (v1.6). tools/unifiedpush-test.js drives a browser with no push service and checks the endpoint is taken off the URL, kept, and registered. Honest about the limit: a UnifiedPush message wakes the ANDROID app, not Chrome's service worker, and the wallet's keys live in the browser's storage where nothing here can reach them. So a zap request arrives as a notification you tap, and the wallet answers when it opens — one tap instead of none. Automatic answering while closed still needs a browser with a push service. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_019mrt228Upn9nNCwGixzSLx
Diffstat (limited to 'android/app/src/main/AndroidManifest.xml')
-rw-r--r--android/app/src/main/AndroidManifest.xml22
1 files changed, 22 insertions, 0 deletions
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index e2f4e16..36f9d1e 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -7,6 +7,15 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- NFC is a way in (bolt cards, NDEF tags), never a requirement -->
<uses-feature android:name="android.hardware.nfc" android:required="false" />
+ <!-- our own notifications, raised from UnifiedPush messages (Android 13+) -->
+ <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
+ <!-- Which apps can act as a UnifiedPush distributor: Android 11+ hides the
+ package list unless we say what we're looking for. -->
+ <queries>
+ <intent>
+ <action android:name="org.unifiedpush.android.distributor.REGISTER" />
+ </intent>
+ </queries>
<application
android:label="@string/app_name"
android:icon="@mipmap/ic_launcher"
@@ -86,5 +95,18 @@
<data android:scheme="https" android:host="v3.coinos.io" />
</intent-filter>
</activity>
+ <!-- UnifiedPush: the distributor broadcasts here. Web push needs Google
+ Play Services on Android, so on a de-Googled phone this is the only
+ way the wallet can be reached at all while it isn't open. -->
+ <receiver
+ android:name="io.coinos.app.PushReceiver"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="org.unifiedpush.android.connector.NEW_ENDPOINT" />
+ <action android:name="org.unifiedpush.android.connector.REGISTRATION_FAILED" />
+ <action android:name="org.unifiedpush.android.connector.UNREGISTERED" />
+ <action android:name="org.unifiedpush.android.connector.MESSAGE" />
+ </intent-filter>
+ </receiver>
</application>
</manifest>