aboutsummaryrefslogtreecommitdiffstats
path: root/src/lib/parse.js
blob: e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 (plain) (blame)

oinos.io> 2026-09-04 20:48:27 -0700 Android wrapper app (android/): a minimal TWA around v3.coinos.io whose whole reason to exist is intent registration — lightning:/bitcoin:/nostr:/lnurl:/lnurlw:/lnurlp: VIEW intents and the same schemes via NFC NDEF dispatch (bolt cards, address stickers; NFC declared required=false), plus verified https links to the site. LauncherActivity forwards a scheme URI into the web app as ?u=<encoded>; the web app reads it BEFORE features construct — a nostr npub/nprofile rewrites itself onto the existing /npub profile deep link, payment URIs stash and dispatch through handleScanned (the QR path) once a wallet is open, and the param is stripped from the URL either way. assetlinks.json (static/.well-known/, copied by build.js) vouches for the signing cert so the TWA runs URL-bar-less; the built APK is served at /coinos.apk for sideloading. Signing keystore lives in android/ GITIGNORED (keystore.properties beside it) — the assetlinks fingerprint pins it, so losing it means re-shipping assetlinks too. Verified: headless ?u= routing (lightning → Send with invoice filled + URL cleaned; nostr → profile page), APK installed on a real phone via adb, schemes registered (chooser appears where other wallets also claim lightning:)' href='/coinosv3.git/commit/android/app/build.gradle?id=8d27a5d2c59e0f1824753d59a7326b6b76887bf7'>8d27a5d ^
60e7552 ^








1bd3e26 ^



2f87941 ^





60e7552 ^
1bd3e26 ^
60e7552 ^



1bd3e26 ^
60e7552 ^





1bd3e26 ^















60e7552 ^

8d27a5d ^

1bd3e26 ^
8d27a5d ^









1bd3e26 ^
8d27a5d ^



60e7552 ^

8d27a5d ^
1bd3e26 ^

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107




                                                                                





                                                                                   
                                                                            





                                                                                  
                                                                                                           



                                                                              







                                 

                      
   








                                                                            



                                                                             





                                                                         
                             
                                    



                       
                                                    





                                                           















                                                                     

     

                  
                        









                                                              
                                                           



              

                                                                                
 

                          
apply plugin: 'com.android.application'

def keystoreProps = new Properties()
def keystoreFile = rootProject.file('keystore.properties')
if (keystoreFile.exists()) keystoreProps.load(new FileInputStream(keystoreFile))
// COINOS_UNSIGNED=1 builds without the keystore even when it is present —
// the reproducible-build check compares two unsigned builds byte for byte,
// and F-Droid builds unsigned and compares against our signed release.
def signRelease = keystoreFile.exists() && System.getenv('COINOS_UNSIGNED') == null

// The bundled shell carries the built web app as assets. It is copied from
// ../dist, which the web build produces first: COINOS_NO_SW=1 bun run build
// (the service worker is left out — inside the WebView the app is served
// from these assets, and a worker would fetch the live site over it).
tasks.register('copyWeb', Copy) {
  def dist = rootProject.file('../dist') // the repo root's dist, not the module's
  doFirst {
    if (!new File(dist, 'index.html').exists())
      throw new GradleException('build the web app first: COINOS_NO_SW=1 bun run build (in the repo root)')
  }
  from(dist) { exclude '*.apk', 'standalone.html', 'sw.js', 'zmq-watcher.js' }
  into layout.buildDirectory.dir('www')
}

android {
  namespace 'io.coinos.app'
  compileSdk 35
  defaultConfig {
    applicationId 'io.coinos.app'
    minSdk 24
    targetSdk 35
    versionCode 15
    versionName '1.14'
  }
  // Two builds of the same wallet.
  //
  //  twa      — Chrome renders the site (the better browser, and the one
  //             people already have installed). Its storage is Chrome's, so
  //             nothing outside Chrome can wake the wallet, and on a phone
  //             without Google Play Services nothing inside it can either.
  //  webview  — the wallet runs in the app, so a UnifiedPush message can
  //             start it off-screen and let it answer. A separate app id,
  //             because it is a separate wallet: its storage is its own.
  //  fdroid   — the webview shell with the web app BUNDLED as assets: what
  //             runs is what was built from this source tree, reproducibly,
  //             which is what F-Droid publishes. No service worker, no
  //             remote code; the live site is only reached for its APIs.
  // Google's dependency-metadata block (a Play-only extra in the APK
  // signing block, encrypted to Google) is refused by F-Droid's scanner.
  dependenciesInfo {
    includeInApk = false
    includeInBundle = false
  }
  flavorDimensions += 'shell'
  buildFeatures { buildConfig true }
  productFlavors {
    twa {
      dimension 'shell'
      isDefault = true
      buildConfigField 'boolean', 'BUNDLED', 'false'
    }
    webview {
      dimension 'shell'
      applicationId 'io.coinos.webview'
      versionNameSuffix '-webview'
      resValue 'string', 'app_name', 'Coinos (UnifiedPush)'
      buildConfigField 'boolean', 'BUNDLED', 'false'
    }
    fdroid {
      dimension 'shell'
      applicationId 'io.coinos.fdroid'
      resValue 'string', 'app_name', 'Coinos'
      buildConfigField 'boolean', 'BUNDLED', 'true'
    }
  }
  sourceSets {
    // the same shell as webview, plus the bundled web app
    fdroid {
      java.srcDirs = ['src/webview/java']
      res.srcDirs = ['src/webview/res']
      manifest.srcFile 'src/webview/AndroidManifest.xml'
      assets.srcDirs += layout.buildDirectory.dir('www').get().asFile
    }
  }
  signingConfigs {
    release {
      if (signRelease) {
        storeFile rootProject.file(keystoreProps['storeFile'])
        storePassword keystoreProps['storePassword']
        keyAlias keystoreProps['keyAlias']
        keyPassword keystoreProps['keyPassword']
      }
    }
  }
  buildTypes {
    release {
      minifyEnabled false
      if (signRelease) signingConfig signingConfigs.release
    }
  }
}
dependencies {
  // only the TWA shell needs Chrome's helper; the WebView build carries none
  twaImplementation 'com.google.androidbrowserhelper:androidbrowserhelper:2.5.0'
}

preBuild.dependsOn copyWeb