Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/calmerism/Tamed/llms.txt

Use this file to discover all available pages before exploring further.

Tamed’s build system is driven by the Gradle wrapper and a Kotlin DSL build script (app/build.gradle.kts). All dependency versions are locked via a Version Catalog (libs.versions.toml), which means a freshly cloned repository produces a deterministic build without any manual dependency management. This page walks you through every step from prerequisites to producing a signed APK.

Prerequisites

The following tools must be installed and available on your PATH before opening the project.
ToolVersionNotes
Android StudioLadybug 2024.2.1+Official IDE; earlier versions may not support compileSdk 37
JDK17Amazon Corretto or Azul Zulu recommended for deterministic builds
Android SDKAPI 34+ (Upside Down Cake)Target SDK is 36; compile SDK is 37
Kotlin2.0Configured via Gradle Version Catalog — no manual install needed
Git2.40+Required for version control and submodule handling
The JVM toolchain is pinned to JVM 21 inside app/build.gradle.kts via kotlin { jvmToolchain(21) }, so the compiler output targets JVM 21 class files. Your local JDK only needs to be 17 or newer to run Gradle itself.

Clone and Open

1

Clone the repository

Clone from GitHub and change into the project directory:
git clone https://github.com/calmerism/Tamed.git
cd Tamed
2

Open in Android Studio

Launch Android Studio and choose File → Open, then select the Tamed folder. Do not use Import Project — opening the folder directly preserves the Gradle wrapper configuration.
3

Wait for Gradle sync

Android Studio automatically triggers a Gradle sync on first open. This downloads all dependencies declared in libs.versions.toml, including Media3, Hilt, Compose, Ktor, Room, and all multi-module project dependencies (:innertube, :kugou, :lrclib, etc.). Sync time varies by connection speed; expect 3–8 minutes on a fresh machine.
4

Create local.properties (optional)

For features that require API credentials, create a local.properties file in the project root (next to settings.gradle.kts). This file is git-ignored and never committed. See the full key reference below.

local.properties Reference

The app/build.gradle.kts reads local.properties at configuration time and injects values into BuildConfig fields. All keys are optional — the build will succeed without them, but the corresponding features will be disabled or degraded.
# Android SDK path — Android Studio sets this automatically
sdk.dir=/Users/yourname/Library/Android/sdk

# Last.fm API credentials (required for scrobbling)
LASTFM_API_KEY=your_key_here
LASTFM_SECRET=your_secret_here

# Music Together relay authentication
TOGETHER_BEARER_TOKEN=your_token_here

# Canvas animated backdrop server
CANVAS_BEARER_TOKEN=your_token_here
CANVAS_BASE_URL=https://your-canvas-server.example.com

# Release signing (falls back to debug keystore if absent)
RELEASE_STORE_FILE=keystore/release.keystore
STORE_PASSWORD=yourpassword
KEY_ALIAS=youralias
KEY_PASSWORD=yourpassword
LASTFM_API_KEY and LASTFM_SECRET can also be supplied as environment variables — the build script checks System.getenv() as a fallback. This makes CI/CD integration straightforward without storing secrets in the repository.

Build Commands

Use the Gradle wrapper (./gradlew on macOS/Linux, gradlew.bat on Windows) for all build operations. Do not rely on a system-wide Gradle installation — only the wrapper version is tested against this codebase.
Gradle TaskOutputPurpose
./gradlew assembleDebugapp/build/outputs/apk/*/debug/app-*-debug.apkLocal testing and feature development
./gradlew assembleReleaseapp/build/outputs/apk/*/release/app-*-release.apkAll ABI flavors, R8-optimized
./gradlew :app:assembleArm64Releaseapp/build/outputs/apk/arm64/release/app-arm64-release.apkarm64-v8a only
./gradlew buildReleaseApkrelease/Tamed.apkUniversal release APK renamed to Tamed.apk in release/
./gradlew bundleReleaseapp/build/outputs/bundle/*/release/app-*-release.aabPlay Store distribution bundle
./gradlew cleanClears the build cache; run before re-syncing after dependency changes

ABI Product Flavors

The build defines two ABI flavors in the abi flavor dimension:

arm64

Packages only arm64-v8a native libraries. This produces the smallest APK and is the flavor used for the official GitHub release binary (Tamed.apk). Use assembleArm64Release to build it.

universal

Packages armeabi-v7a, arm64-v8a, x86, and x86_64 native libraries. Use this flavor when distributing to emulators or 32-bit devices. Use assembleUniversalRelease or buildReleaseApk (which renames the output to Tamed.apk in the release/ folder).

Release Signing

The signing configuration in app/build.gradle.kts resolves at configuration time using the following priority order:
  1. RELEASE_STORE_FILE path from local.properties or environment variable
  2. If that file does not exist, falls back to ~/.android/debug.keystore automatically
When the debug keystore fallback is used, the build script also applies the standard debug keystore credentials (password: android, alias: androiddebugkey) so a release build can be signed without any manual configuration. This means assembleRelease works out of the box on any developer machine.
APKs signed with the debug keystore cannot be installed over APKs signed with a release keystore, and vice versa. If you are testing upgrades from an official release, you must uninstall the existing app first.

Gradle Performance Settings

The committed gradle.properties already includes optimized JVM settings for building Tamed on typical developer hardware:
org.gradle.jvmargs=-Xmx6144M -Dfile.encoding=UTF-8 -Djava.awt.headless=true -XX:+UseParallelGC
kotlin.daemon.jvmargs=-Xmx6144M -Djava.awt.headless=true
org.gradle.workers.max=2
org.gradle.parallel=true
org.gradle.daemon=true
org.gradle.configuration-cache=true
These settings allocate 6 GB to the Gradle daemon and enable the configuration cache for faster incremental builds. KSP incremental processing is disabled (ksp.incremental=false) to avoid stale code generation issues with Room and Hilt.

Troubleshooting

If Gradle exits with an out-of-memory error during compilation, the Gradle daemon needs more heap. Add or update the following line in your local gradle.properties:
org.gradle.jvmargs=-Xmx4g -XX:MaxMetaspaceSize=1g
The committed value is already 6 GB (-Xmx6144M), but on machines with less RAM you may need to reduce it while also raising the system-level limit.
If the build fails with a message about the Compose compiler extension version, verify that the compose.compiler plugin version in libs.versions.toml is compatible with Kotlin 2.0. Tamed uses the standalone alias(libs.plugins.compose.compiler) plugin rather than inline configuration, so the version is managed entirely through the catalog.
Run ./gradlew clean to clear stale build outputs, then trigger a re-sync from Android Studio via File → Sync Project with Gradle Files. If JitPack dependencies time out, the committed gradle.properties already extends connection and socket timeouts to 180 seconds:
systemProp.org.gradle.internal.http.connectionTimeout=180000
systemProp.org.gradle.internal.http.socketTimeout=180000
Re-run the sync after a brief wait if the JitPack CDN is slow.
Room is configured to export schemas via the KSP argument room.schemaLocation=$projectDir/schemas. If you see an error about schema export during compilation, ensure the app/schemas/ directory exists and is writable. The exported JSON files should be committed alongside code changes that modify the database schema.

Build docs developers (and LLMs) love