|
| 1 | +# Android Build & Release |
| 2 | + |
| 3 | +This document covers building and releasing LunchJS for Android using Task and Fastlane. |
| 4 | + |
| 5 | +--- |
| 6 | + |
| 7 | +## Prerequisites |
| 8 | + |
| 9 | +### Environment Variables |
| 10 | + |
| 11 | +Add to `~/.bash_profile` or `~/.zshrc`: |
| 12 | + |
| 13 | +```bash |
| 14 | +# Java |
| 15 | +export PATH="/opt/homebrew/opt/openjdk@24/bin:$PATH" |
| 16 | +export JAVA_HOME="/opt/homebrew/opt/openjdk@24" |
| 17 | + |
| 18 | +# Android SDK |
| 19 | +export ANDROID_HOME="/opt/homebrew/share/android-commandlinetools" |
| 20 | +export PATH="$ANDROID_HOME/platform-tools:$ANDROID_HOME/cmdline-tools/latest/bin:$PATH" |
| 21 | + |
| 22 | +# Android NDK |
| 23 | +export ANDROID_NDK_HOME="$ANDROID_HOME/ndk/28.2.13676358" |
| 24 | +``` |
| 25 | + |
| 26 | +### Rust Targets |
| 27 | + |
| 28 | +```bash |
| 29 | +rustup target add aarch64-linux-android armv7-linux-androideabi i686-linux-android x86_64-linux-android |
| 30 | +``` |
| 31 | + |
| 32 | +--- |
| 33 | + |
| 34 | +## Project Configuration |
| 35 | + |
| 36 | +| Setting | Value | |
| 37 | +|---------|-------| |
| 38 | +| Package name | `com.lunch.app` | |
| 39 | +| Min SDK | 24 (Android 7.0) | |
| 40 | +| Target SDK | 36 | |
| 41 | +| Build location | `src-tauri/gen/android/` | |
| 42 | + |
| 43 | +--- |
| 44 | + |
| 45 | +## Development |
| 46 | + |
| 47 | +### Emulator |
| 48 | + |
| 49 | +```bash |
| 50 | +# Run on Android emulator (default: pixel_7) |
| 51 | +task android:dev |
| 52 | + |
| 53 | +# Configure emulator keyboard settings |
| 54 | +task android:emulator:setup |
| 55 | +``` |
| 56 | + |
| 57 | +### Physical Device |
| 58 | + |
| 59 | +```bash |
| 60 | +# Hot-reload development on connected device |
| 61 | +task android:dev:device |
| 62 | + |
| 63 | +# Build release APK, sign with debug key, install on device |
| 64 | +task android:run:device |
| 65 | +``` |
| 66 | + |
| 67 | +--- |
| 68 | + |
| 69 | +## Building |
| 70 | + |
| 71 | +### Debug Builds |
| 72 | + |
| 73 | +```bash |
| 74 | +# Build APK for testing/sideloading |
| 75 | +task android:build:apk |
| 76 | +``` |
| 77 | + |
| 78 | +Output: `src-tauri/gen/android/app/build/outputs/apk/universal/release/app-universal-release-unsigned.apk` |
| 79 | + |
| 80 | +### Release Builds |
| 81 | + |
| 82 | +```bash |
| 83 | +# Build signed AAB for Google Play |
| 84 | +task android:build:aab |
| 85 | +``` |
| 86 | + |
| 87 | +Output: `src-tauri/gen/android/app/build/outputs/bundle/universalRelease/app-universal-release.aab` |
| 88 | + |
| 89 | +### Full Distribution Build |
| 90 | + |
| 91 | +```bash |
| 92 | +# Build both APK and AAB |
| 93 | +task android:build |
| 94 | +``` |
| 95 | + |
| 96 | +--- |
| 97 | + |
| 98 | +## Release Signing |
| 99 | + |
| 100 | +### Create Keystore (One-Time Setup) |
| 101 | + |
| 102 | +```bash |
| 103 | +λ keytool -genkey -v -keystore ~/lunchjs-release.jks \ |
| 104 | + -keyalg RSA -keysize 2048 -validity 10000 -alias lunch \ |
| 105 | + -dname "CN=Lunch App, OU=Mobile, O=LunchJS, L=Unknown, ST=Unknown, C=US" |
| 106 | +Enter keystore password: |
| 107 | +Re-enter new password: |
| 108 | +Generating 2048-bit RSA key pair and self-signed certificate (SHA384withRSA) with a validity of 10,000 days |
| 109 | + for: CN=Lunch App, OU=Mobile, O=LunchJS, L=Unknown, ST=Unknown, C=US |
| 110 | +[Storing ~/lunchjs-release.jks] |
| 111 | +``` |
| 112 | + |
| 113 | +### Configure Signing |
| 114 | + |
| 115 | +1. Add to `.env`: |
| 116 | + ``` |
| 117 | + ANDROID_KEYSTORE_PASSWORD=<your-password> |
| 118 | + ANDROID_KEYSTORE_PATH=/path/to/lunchjs-release.jks |
| 119 | + ``` |
| 120 | + |
| 121 | +2. Generate `keystore.properties`: |
| 122 | + ```bash |
| 123 | + task android:keystore:setup |
| 124 | + ``` |
| 125 | + |
| 126 | +This creates `src-tauri/gen/android/keystore.properties` with your credentials. |
| 127 | + |
| 128 | +### Verify Signature |
| 129 | + |
| 130 | +```bash |
| 131 | +jarsigner -verify src-tauri/gen/android/app/build/outputs/bundle/universalRelease/app-universal-release.aab |
| 132 | +``` |
| 133 | + |
| 134 | +--- |
| 135 | + |
| 136 | +## Google Play Release |
| 137 | + |
| 138 | +### Prerequisites |
| 139 | + |
| 140 | +1. Create app in [Google Play Console](https://play.google.com/console) with package name `com.lunch.app` |
| 141 | +2. Create service account for API access: |
| 142 | + - Go to Setup > API access > Service accounts |
| 143 | + - Create new service account with "Release Manager" permissions |
| 144 | + - Download JSON key file |
| 145 | +3. Add to `.env`: |
| 146 | + ``` |
| 147 | + SUPPLY_JSON_KEY=/path/to/service-account.json |
| 148 | + ``` |
| 149 | + |
| 150 | +### First Release (Manual) |
| 151 | + |
| 152 | +The first AAB must be uploaded manually through Google Play Console: |
| 153 | + |
| 154 | +1. Build signed AAB: `task android:build:aab` |
| 155 | +2. Go to Play Console > Release > Production > Create new release |
| 156 | +3. Upload `app-universal-release.aab` |
| 157 | +4. Complete store listing, content rating, and pricing |
| 158 | + |
| 159 | +### Automated Releases |
| 160 | + |
| 161 | +After the first manual upload, use Fastlane for subsequent releases: |
| 162 | + |
| 163 | +```bash |
| 164 | +# Upload to internal testing track |
| 165 | +task android:internal |
| 166 | + |
| 167 | +# Upload to beta track |
| 168 | +task android:testflight |
| 169 | + |
| 170 | +# Upload to production |
| 171 | +task android:release |
| 172 | +``` |
| 173 | + |
| 174 | +--- |
| 175 | + |
| 176 | +## Fastlane Lanes |
| 177 | + |
| 178 | +| Lane | Description | Task Command | |
| 179 | +|------|-------------|--------------| |
| 180 | +| `android build` | Build AAB and APK | `bundle exec fastlane android build` | |
| 181 | +| `android internal` | Upload to internal testing | `task android:internal` | |
| 182 | +| `android beta` | Upload to beta track | `task android:testflight` | |
| 183 | +| `android release` | Upload to production | `task android:release` | |
| 184 | + |
| 185 | +All lanes automatically: |
| 186 | +- Update version from `Cargo.toml` |
| 187 | +- Generate unique build number (seconds since 2024-01-01) |
| 188 | +- Build using Tauri CLI |
| 189 | +- Upload to specified Play Store track |
| 190 | + |
| 191 | +--- |
| 192 | + |
| 193 | +## Task Commands Reference |
| 194 | + |
| 195 | +| Command | Description | |
| 196 | +|---------|-------------| |
| 197 | +| `task android:init` | Initialize Android project | |
| 198 | +| `task android:dev` | Run on emulator | |
| 199 | +| `task android:dev:device` | Run on device (hot-reload) | |
| 200 | +| `task android:run:device` | Build and install release APK on device | |
| 201 | +| `task android:build` | Build APK and AAB | |
| 202 | +| `task android:build:apk` | Build APK only | |
| 203 | +| `task android:build:aab` | Build signed AAB | |
| 204 | +| `task android:keystore:setup` | Generate keystore.properties from env vars | |
| 205 | +| `task android:internal` | Upload to internal testing | |
| 206 | +| `task android:testflight` | Upload to beta track | |
| 207 | +| `task android:release` | Upload to production | |
| 208 | +| `task android:clean` | Clean build artifacts | |
| 209 | +| `task android:emulator:keyboard` | Enable hardware keyboard for emulator | |
| 210 | +| `task android:emulator:setup` | Full emulator configuration | |
| 211 | + |
| 212 | +--- |
| 213 | + |
| 214 | +## Build Artifacts |
| 215 | + |
| 216 | +| Type | Path | |
| 217 | +|------|------| |
| 218 | +| Unsigned APK | `src-tauri/gen/android/app/build/outputs/apk/universal/release/app-universal-release-unsigned.apk` | |
| 219 | +| Signed APK (debug key) | `src-tauri/gen/android/app/build/outputs/apk/universal/release/app-universal-release-signed.apk` | |
| 220 | +| Signed AAB | `src-tauri/gen/android/app/build/outputs/bundle/universalRelease/app-universal-release.aab` | |
| 221 | + |
| 222 | +--- |
| 223 | + |
| 224 | +## Troubleshooting |
| 225 | + |
| 226 | +### Build Fails with Signing Error |
| 227 | + |
| 228 | +Ensure `keystore.properties` exists and contains valid paths: |
| 229 | +```bash |
| 230 | +task android:keystore:setup |
| 231 | +``` |
| 232 | + |
| 233 | +### Emulator Keyboard Not Working |
| 234 | + |
| 235 | +Run keyboard configuration: |
| 236 | +```bash |
| 237 | +task android:emulator:setup |
| 238 | +``` |
| 239 | + |
| 240 | +### Upload Fails with Package Name Mismatch |
| 241 | + |
| 242 | +Verify package name is `com.lunch.app` in: |
| 243 | +- `src-tauri/gen/android/app/build.gradle.kts` (namespace and applicationId) |
| 244 | +- Google Play Console app settings |
| 245 | + |
| 246 | +### Version Code Too Low |
| 247 | + |
| 248 | +Fastlane uses timestamp-based version codes. If Play Console rejects the build, wait a second and rebuild - each build gets a unique version code. |
| 249 | + |
| 250 | +--- |
| 251 | + |
| 252 | +## File Structure |
| 253 | + |
| 254 | +``` |
| 255 | +src-tauri/gen/android/ |
| 256 | +├── app/ |
| 257 | +│ ├── build.gradle.kts # Package name, signing config |
| 258 | +│ ├── src/main/java/com/lunch/app/ |
| 259 | +│ │ └── MainActivity.kt # Entry point |
| 260 | +│ └── build/outputs/ |
| 261 | +│ ├── apk/ # APK builds |
| 262 | +│ └── bundle/ # AAB builds |
| 263 | +├── keystore.properties # Signing credentials (gitignored) |
| 264 | +└── keystore.properties.example |
| 265 | +``` |
0 commit comments