Build the Android app yourself
The Android app is the same Tauri 2 project as the desktop launcher, built for Android. This page is for contributors and for players who want to build and sideload their own APK. If you only want to play, take the signed APK from our website – see Android: install the APK.
What you get: the full app – sign-in, instances, modpacks, social, wardrobe and Minecraft: Java Edition through the built-in game engine with the touch overlay. What you don't get: automatic updates. The app checks our official update channel, but Android only installs an update that has the same package name and the same signing key – your own build has a different key, so you update it by building again.
.github/workflows/ci.yml and release.yml, jobs
Android) plus the local notes from docs/mobile.md in the repository. The CI builds on Linux; local builds are
described for Windows. Building the Android app on macOS is not tested.What is where
| Path | What it is |
|---|---|
src-tauri/gen/android/ | The Gradle project of the app (checked in, minSdk 26 = Android 8.0) |
src-tauri/gen/android/app/build.gradle.kts | App ID, SDK levels, release signing |
src-tauri/tauri.android.conf.json | Android overrides of the Tauri config (no desktop updater, no bundled TRS Client jars) |
src-tauri/plugins/tauri-plugin-trs-game/android/ | The game engine (parts of Amethyst/PojavLauncher, ndk-build) and the touch overlay |
src-tauri/plugins/tauri-plugin-trs-game/android/prebuilt.lock | Prebuilt engine parts that Gradle downloads and checks with SHA-256 |
Requirements
| Tool | Version (as in the CI) |
|---|---|
| Node.js and pnpm | Node 22, pnpm as pinned in package.json (corepack enable) |
| Rust | stable via rustup, targets aarch64-linux-android (phones) and x86_64-linux-android (emulator) |
| JDK | 21 (CI: Temurin; the JBR that comes with Android Studio works too) |
| Android SDK | with cmdline-tools and platform-tools |
| Android NDK | 28.2.13676358 for the app (NDK_HOME) and 27.3.13750724 (r27d) for the game engine |
| Gradle | nothing to install – the wrapper in src-tauri/gen/android uses Gradle 8.14.3 |
The app compiles against Android SDK 36 (compileSdk/targetSdk). In the CI only platform-tools and the two NDKs
are installed by hand; the rest comes through the Android Gradle plugin. Accept the SDK licenses once with
sdkmanager --licenses.
ndkVersion = "27.3.13750724" in its own build.gradle.kts; the
Rust part is linked with the NDK from NDK_HOME (28.2).Build a debug APK
Get the code
git clone https://github.com/theredstonee/TRS-Launcher.git
cd TRS-Launcher
pnpm install
Install the SDK parts and Rust targets
sdkmanager --install "platform-tools" "ndk;28.2.13676358" "ndk;27.3.13750724"
rustup target add aarch64-linux-android x86_64-linux-android
You can also install the two NDK versions in Android Studio (SDK Manager → SDK Tools → NDK (Side by side), tick Show Package Details).
Set the environment
On Windows (PowerShell) or Linux:
$env:ANDROID_HOME = "$env:LOCALAPPDATA\Android\Sdk"
$env:NDK_HOME = "$env:ANDROID_HOME\ndk\28.2.13676358"
$env:JAVA_HOME = "C:\Program Files\Android\Android Studio\jbr"
export ANDROID_HOME="$HOME/Android/Sdk" # where your SDK is
export NDK_HOME="$ANDROID_HOME/ndk/28.2.13676358"
export JAVA_HOME=/path/to/jdk-21
On Windows, turn on Developer Mode first – see Troubleshooting.
Build
pnpm tauri android build --apk --debug --target aarch64
This builds the frontend (pnpm generate), the Rust library for arm64 and then the APK with Gradle. The first Gradle
run downloads the prebuilt engine parts from prebuilt.lock (checked with SHA-256, cached in
~/.gradle/caches/trs-engine-prebuilt), so it needs internet and takes a while.
Find the APK
The APKs end up under src-tauri/gen/android/app/build/outputs/apk/, in a folder per ABI and build type. The CI
doesn't rely on the exact folder name, and you don't have to either:
find src-tauri/gen/android/app/build/outputs/apk -name '*.apk'
Get-ChildItem -Recurse -Filter *.apk src-tauri\gen\android\app\build\outputs\apk
Install it on your phone
Turn on USB debugging in the developer options of your phone, connect it and run:
adb install -r path/to/the.apk
Or copy the APK to the phone and open it there, as described in Android: install the APK. A debug APK is signed with the debug key of your Android SDK, so it installs right away.
For the emulator
pnpm tauri android build --apk --debug --target x86_64
Start the emulator with -gpu host – with SwiftShader the renderer crashes in its Vulkan check. Minecraft 26.x can't
be played in the emulator (its Vulkan lacks the needed extensions and OpenGL hangs there); test those versions on a
real phone.
Build a release APK
pnpm tauri android build --apk --target aarch64
That is the release command of the CI without --ci. Release builds are minified with R8. Without a signing key the
result is an unsigned APK (…-unsigned.apk) – Android can't install that. Sign it with your own key:
Create a keystore once
keytool -genkeypair -v -keystore my-release.jks -alias my-key \
-keyalg RSA -keysize 4096 -validity 10000
keytool comes with the JDK. Keep the file and the passwords safe: Android only installs updates of an app that are
signed with the same key.
Set the four variables
Gradle signs the release APK itself (signingConfigs in src-tauri/gen/android/app/build.gradle.kts) when
ANDROID_KEYSTORE_PATH points to an existing file:
export ANDROID_KEYSTORE_PATH=/path/to/my-release.jks
export ANDROID_KEYSTORE_PASSWORD='…'
export ANDROID_KEY_ALIAS=my-key
export ANDROID_KEY_PASSWORD='…' # same as the keystore password unless you chose another one
On Windows set them with $env:NAME = "…" like above.
Build and check
pnpm tauri android build --apk --target aarch64
"$ANDROID_HOME"/build-tools/<version>/apksigner verify --print-certs <the.apk>
apksigner shows the SHA-256 of your certificate – not the one listed on the
install page, which belongs to our official key.
TAURI_SIGNING_PRIVATE_KEY) only signs the update
channel files in the release workflow, and Android builds don't create updater files anyway
(tauri.android.conf.json). The CurseForge key is optional, exactly as on the desktop: the environment variable
CURSEFORGE_API_KEY or the file ~/.tauri/curseforge-api-key (%USERPROFILE%\.tauri\curseforge-api-key on
Windows). Without it the build works and simply hides CurseForge.Make it your own app
If you want to install your build next to the official app or share it with friends, give it its own app ID and name:
| What | Where | Change |
|---|---|---|
| App ID (package name) | src-tauri/gen/android/app/build.gradle.kts | applicationId = "dev.theredstonee.trslauncher" → for example "io.github.yourname.trslauncher" |
| Name on the home screen | src-tauri/gen/android/app/src/main/res/values/strings.xml | app_name and main_activity_title |
| Icon | src-tauri/gen/android/app/src/main/res/mipmap-*/ | ic_launcher*.png (the ic_launcher_halloween* files are the event icon) |
| Signing key | your own keystore | see above |
applicationId – not namespace in the same file and not identifier in src-tauri/tauri.conf.json.
The Kotlin package dev.theredstonee.trslauncher is wired into the Rust code (register_android_plugin in
src-tauri/src/mobile.rs, the JNI function Java_dev_theredstonee_trslauncher_PushPollWorker_nativePoll in
src-tauri/src/push.rs), and the Tauri CLI stops the build with "you have modified your "identifier" in the Tauri
configuration" when the identifier no longer matches the Kotlin folder. The engine and push plugins read the package
name at runtime, so a new applicationId is fine for them.What still works in your own build, according to the code:
- Microsoft sign-in: the app uses Microsoft's device-code sign-in (you type a code on Microsoft's page). It doesn't depend on the package name or a redirect into the app.
- TRS services (friends, chat, capes, TRS API): the app talks to the same API; there is no check of the package name or signature.
- Links like
trs-launcher://pack/<code>use the same scheme in every build. With the official app and your build installed side by side, Android asks which app should open them. - Updates: see the top of the page. The in-app installer only accepts an APK for its own package name, so it can't replace your build with the official one, and Android rejects our APK over a build with your key.
We haven't tested a renamed build ourselves – if something breaks, please report it.
Run the checks
The same checks as the CI job Android:
pnpm generate # generate_context! needs the built frontend
cd src-tauri
cargo clippy --workspace --target aarch64-linux-android -- -D warnings
cd gen/android
./gradlew --no-daemon :tauri-plugin-trs-game:testDebugUnitTest # touch overlay and engine logic, no device
Clippy runs outside tauri android build and needs the NDK compiler for the C parts (SQLite, ring). The CI sets these
on Linux:
bin="$NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin"
export CC_aarch64_linux_android="$bin/aarch64-linux-android26-clang"
export AR_aarch64_linux_android="$bin/llvm-ar"
export CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER="$bin/aarch64-linux-android26-clang"
Troubleshooting
"A required privilege is not held by the client"
On Windows, tauri android build links the Rust library into gen/android/app/src/main/jniLibs with a symbolic link.
Without Developer Mode (or admin rights) that fails. Turn on Developer Mode in the Windows settings, or copy the
library yourself (src-tauri/target/<triple>/debug/libtrs_launcher_lib.so → jniLibs/<abi>/) and run the Gradle task
in src-tauri/gen/android, for the emulator gradlew assembleX86_64Debug.
NDK not found or wrong NDK version
NDK_HOME must point to 28.2.13676358, and 27.3.13750724 must be installed in the SDK's ndk/ folder as well.
If your r27d lives somewhere else, set android.ndkPath in src-tauri/gen/android/local.properties or the variable
ANDROID_NDK_HOME (see the comment in the plugin's build.gradle.kts).
The SDK setup asks for the package "tools"
The old SDK package tools doesn't exist any more with current cmdline-tools. Install only cmdline-tools,
platform-tools and the two NDKs – the CI does the same.
The release APK doesn't install
It is unsigned (…-unsigned.apk). Set the four signing variables and build again, or use a debug build.
"App not installed" or "conflicts with an existing package"
Another build of the app with a different key is installed – often our official app. Uninstall it first (this removes its instances) or give your build its own app ID.
"SHA-256 stimmt nicht" while Gradle downloads the engine parts
A download from prebuilt.lock didn't match its checksum (for example a broken connection or a proxy). The file is
thrown away; run the build again.
The game crashes in the emulator or 26.x doesn't start there
Use -gpu host, and test Minecraft 26.x on a real phone (see For the emulator).
The app can't be installed on an older or 32-bit phone
The app needs Android 8.0 and a 64-bit processor: only arm64-v8a and x86_64 are packaged because the game engine
exists only for 64 bit.
CurseForge is missing
The build had no CurseForge API key. That's expected for own builds – see the note on the key above.
License and name
TRS Launcher is free software under the GNU General Public License v3.0 (LICENSE, copyright notices in NOTICE).
You may build, change and share the app. If you share an APK, the GPL requires that you share it under the same license
and make the complete source code of exactly that build available, including your changes. The game engine
contains third-party parts with their own licenses (Amethyst/PojavLauncher under LGPL-3.0 and more) – they are listed
in NOTICE and must stay in.
The repository has no separate trademark rules. Still: if you hand out your own build, give it its own name and app ID so nobody mistakes it for the official TRS Launcher, and point people to your source code – not to our support.
Next steps
Public API
Read-only TRS API endpoints you can use without an account – status, releases, capes, circuits, modpack codes, blog, issues, team and achievements.
Build the iOS app yourself with Xcode
Build the TRS Launcher IPA for iPhone and iPad on a Mac: Xcode project, Rust library, game engine, sideloading and running it with your own Apple ID.