Skip to content

Build from Source

Lwb89dev edited this page Oct 19, 2026 · 1 revision

Build from source

Prerequisites

Tool Version
JDK 17
Android SDK platform 36 and build tools
Android NDK 27.1.12297006
Flutter SDK The build goes through Flutter because the APK carries the Flutter engine that reads the previous version's data once
Dart comes with Flutter

For the optional offline-speech engine rebuild you also need Rust with the aarch64-linux-android target, cargo-ndk and CMake.

Build

git clone https://github.com/roadstrapp/roadstr-app.git
cd roadstr-app
flutter pub get
flutter gen-l10n                 # the ARB files are the source of every translation

flutter build apk --release      # debug: flutter run

Only the Kotlin side and one ABI:

cd android && ./gradlew :app:assembleRelease -Ptarget-platform=android-arm64

The Kotlin launcher is the default. -Pnative_launcher=false builds the old Flutter launcher instead, to compare the two.

Signed releases

A signed release uses android/key.properties (copy android/key.properties.template and fill in the keystore). It is never committed. Then:

./build_release.sh

It builds the four APKs (universal, arm64-v8a, armeabi-v7a, x86_64), checks package, version, certificate, ABIs and 16 KB page alignment, and writes the SHA-256 sums. It publishes nothing. A release candidate must advance beyond the currently shipped version code; release tooling and the rollback plan are described in docs/kotlin-rewrite/RELEASE_TOOLING.md.

Without a keystore Gradle produces an unsigned release rather than disguising a debug-signed one as official.

Tests

flutter test                                          # Dart contract tests (resources, build, docs)
cd android && ./gradlew :app:testDebugUnitTest        # Kotlin JVM tests (about 1,500 cases)
tools/kotlin_rewrite/audit_android_release.sh --source-only
python3 tools/interop/generate_vectors.py --check     # the published road-report test vectors are current

The Dart tests are contract tests: they pin the generated resources, the build configuration, the manifest permissions, the vendored native binaries and the documents the README links to. A change that drifts from any of them fails.

Translations

After editing a translation, regenerate both the Dart classes and the Android resources:

flutter gen-l10n
for g in tools/kotlin_rewrite/generate_android_*_strings.dart; do dart run "$g"; done

Native libraries that are committed

Some native libraries are committed so that normal builds stay offline, and each is checked against a SHA-256 before the build starts (bundledNativeChecksums in android/app/build.gradle.kts):

Library Rebuild with
eSpeak NG (voice phonemisation), three ABIs tools/build_espeak_android.sh
The offline speech engine (libofflinestt_*.so), arm64 only third_party/offline-stt-engine/rebuild.sh

Both rebuild byte for byte from pinned sources.

Build variants

  • Road-test app (native-android/): the same Compose shell with its own stores; a separate Gradle project that compiles the shared source directories. Its package (app.roadstr.roadtest) cannot replace the real app.
  • In-app browser variant (GeckoView, optional): native-android/gradlew-geckoview :app:assembleDebug. About 130 MiB bigger (debug build, one ABI), needs Android 8, and native-android/verify-geckoview-variant.sh checks what must stay true of both builds. It has not been run on a device yet.

Repository layout

See Architecture.

Clone this wiki locally