build-release.yml is tag-driven, builds three platforms and then creates a release -- none of which is what you want from a feature branch, and there was otherwise no way to get an installable build out of CI without cutting one. workflow_dispatch only, deliberately. The runner has a single slot shared with two other projects, so an APK on every feature-branch commit would starve them; dispatch it when you actually want to install something. Defaults to the R8-minified release build in the debug applicationId slot rather than a plain debug APK. Minification is where Android releases have actually broken here (R8 stripping JNI-loaded player and security classes), and a debug build cannot catch it. Neither variant needs the real signing key, and both install side by side with a real install. Builds through scripts/build-android.sh rather than a hand-rolled tauri invocation, so CI and a developer's machine produce the same thing and the script's applicationId assertion still runs. Shares the existing cargo registry cache key -- no fourth copy of the registry on a disk that has filled before.
5.5 KiB
⚠️ IMPORTANT: Android Build File Locations
Critical Information for Future Development
DO NOT EDIT FILES IN src-tauri/gen/android/ DIRECTLY!
File Structure
This project has TWO sets of Android source files:
-
src-tauri/android/- SOURCE FILES (edit these!)- This is the template directory
- Changes here need to be copied to the generated directory
-
src-tauri/gen/android/- GENERATED BUILD DIRECTORY (do not edit directly!)- This is where Gradle actually builds the APK
- Files here may be overwritten during builds
How to Make Changes to Android Code
When you need to modify Android/Kotlin files:
-
Edit the files in
src-tauri/android/src/main/java/ -
Build using the provided script (which auto-syncs files)
./scripts/build-android.shThe build script automatically runs
./scripts/sync-android-sources.shwhich copies:src-tauri/android/src/main/java/com/dtourolle/jellytau/player/→ generated directorysrc-tauri/android/src/main/java/com/dtourolle/jellytau/security/→ generated directory
-
Manual sync (if needed)
./scripts/sync-android-sources.sh
Why This Matters
- If you only edit
src-tauri/gen/android/, your changes will be lost - If you only edit
src-tauri/android/, your changes won't be in the build - You must edit both (or edit source and copy to generated)
Debug and release install side by side
The debug build type sets applicationIdSuffix = ".debug" in
app/build.gradle.kts, so a debug build is a genuinely separate Android app:
| build | applicationId | launcher name | versionName | signed with |
|---|---|---|---|---|
release |
com.dtourolle.jellytau |
jellytau | 0.5.5 |
real key (.env) |
release --debug |
com.dtourolle.jellytau.debug |
JellyTau Debug | 0.5.5-debug-release |
debug keystore |
debug |
com.dtourolle.jellytau.debug |
JellyTau Debug | 0.5.5-debug |
debug keystore |
release --debug is the side-by-side release: fully R8-minified, exactly
what ships, but packaged into the debug slot and signed with the local debug
keystore. It exists because R8 has broken release APKs here before (stripping
JNI-loaded player/security classes), and reproducing that previously meant
building with the real key and clobbering your working install. It shares the
applicationId and signature with the plain debug build, so the two replace
each other cleanly; only the versionName suffix tells you which is installed.
./scripts/build-and-deploy.sh release --device --debug # build + install it
The flag is plumbed through as JT_SIDE_BY_SIDE=1, read by build.gradle.kts.
CI never sets it, so distributable release builds are untouched.
That means:
- No uninstall step. Debug builds are signed with the local auto-generated
~/.android/debug.keystore, release builds with the real key. Two different keys on the same package isINSTALL_FAILED_UPDATE_INCOMPATIBLE; two different packages is just two apps. - Each has its own data directory — separate settings, credentials,
downloads and offline cache. A debug experiment cannot corrupt the state of
the build you actually use. This is not optional and cannot be shared:
Android gives each applicationId its own UID and enforces the boundary in the
kernel. (
sharedUserIdis deprecated since API 29 and cannot be added to an already-installed app anyway.) You log in again in the debug app, once. - Only the application id changes. Kotlin classes stay in the
namespacepackagecom.dtourolle.jellytau, so the JNI class lookups insrc-tauri/src/player/android/mod.rs, the manifest<service>entry and the R8 keep rules inproguard-jellytau.proare all unaffected. The FileProvider authority is${applicationId}.fileprovider, so it follows the suffix automatically. - The launcher labels come from the
appLabel/activityLabelmanifestPlaceholders (AndroidManifest.xmluses${appLabel}), not fromresValue, which would collide with Tauri's generatedstrings.xml.
Follow the right log stream with ./scripts/logcat.sh [debug|release]
(defaults to debug).
Getting a test APK out of CI
.gitea/workflows/build-test-apk.yml builds one from any branch, on demand
— run it from Gitea's Actions tab (workflow_dispatch) against the ref you want.
It is not a release: nothing is tagged, published, or signed with the real key.
Two variants, both installing into the com.dtourolle.jellytau.debug slot:
| Variant | What it is | When |
|---|---|---|
side-by-side-release (default) |
R8-minified, exactly what ships, signed with the debug keystore | Almost always — a plain debug build cannot catch R8 stripping JNI-loaded classes, which has broken release APKs here before |
debug |
Unminified | When you need readable stack traces |
There is deliberately no push trigger: the runner has one slot shared with
two other projects, so building on every feature-branch commit would starve
them. The APK lands as the jellytau-test-apk artifact (7-day retention), named
for the branch and short SHA, with its size and SHA256 in the run summary.
Key Files
Player-related Kotlin files:
player/JellyTauPlayer.kt- Main player implementationplayer/JellyTauPlaybackService.kt- MediaSession service for lockscreen controlssecurity/SecureStorage.kt- Android Keystore integration for secure credential storage
Always check BOTH locations exist and match after making changes!