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 is a production-grade application with a large, reactive codebase. Keeping it maintainable requires everyone working on it to follow the same conventions — for naming, state management, layer boundaries, and tooling. Contributors are expected to pass three automated quality gates before every pull request, follow the Kotlin and architecture conventions enforced by the project, and respect the GPL-3.0 licensing terms in all modified files. This page describes the full expectations and the step-by-step process for getting a contribution merged cleanly.

Required Skill Set

The Tamed codebase is built on a modern, reactive architecture. Before contributing, ensure you have solid familiarity with the following areas:

Kotlin (Advanced)

Proficiency with Coroutines, the Flow API, StateFlow/MutableStateFlow, supervisorScope, and functional paradigms. Most data pipelines in Tamed are expressed as Flow transformations — side effects and imperative logic should be kept to a minimum.

Jetpack Compose

Understanding of state hoisting, recomposition scoping, remember/derivedStateOf, and Material 3 design conventions. Avoid triggering unnecessary recompositions by keeping state close to where it is consumed.

Gradle KTS

Ability to read and modify Kotlin DSL build scripts and Version Catalogs (libs.versions.toml). All dependency declarations go through the catalog — do not hardcode version strings in build.gradle.kts.

Modern Android Architecture

Deep understanding of MVVM, Repository patterns, and Unidirectional Data Flow (UDF). Know which layer owns which concern and never reach across layer boundaries in either direction.

Quality Gates

Every pull request must pass all three quality gates before review. Run them locally before pushing:
1

Lint

./gradlew lintDebug
Checks Android XML resources and Compose code against the project’s lint.xml configuration. The build is configured with warningsAsErrors = false and abortOnError = false, so lint will report issues without failing the build — but reviewers will flag unaddressed lint warnings.
2

Kotlin formatting (ktlint)

./gradlew ktlintCheck
Enforces consistent Kotlin code style across the entire codebase. To auto-fix formatting issues before checking, run ./gradlew ktlintFormat first. Commits with ktlint failures will not be merged.
3

Unit tests

./gradlew testDebugUnitTest
Runs all unit tests targeting architectural logic. Tests use Turbine for Flow assertions. New logic added to ViewModels, repositories, or utilities should be accompanied by corresponding unit tests.

Kotlin Code Style

Follow the official Kotlin coding conventions in addition to the ktlint rules enforced automatically.
Prefer data class for all model and entity types. This applies to database entities, network response models, and domain models alike. Data classes give you equals, hashCode, toString, and copy for free, which matters when comparing states in ViewModels.
data class ImageDiskCacheConfig(
    val policy: CachePolicy,
    val maxSizeBytes: Long,
)

Architecture Rules

These rules enforce the layer boundaries described in the Architecture guide. Violations will be requested for change during code review.

No direct DB access from UI

The UI layer must never import or call MusicDatabase or DatabaseDao directly. All database reads and writes go through a ViewModel, which coordinates with the repository layer. PlayerConnection exposes database-backed state like currentSong and currentLyrics as Flow properties — the UI collects those, it does not query the DB.

No Android classes in Domain

Domain-layer classes (ViewModels, use cases, repository interfaces) must not import android.* framework classes. Use Context-free abstractions and inject Android dependencies at the boundary via Hilt.

DataStore via typed keys only

All DataStore preference reads and writes must use the typed PreferenceKey constants defined in com.tamed.music.constants. Never hardcode preference key strings. Wildcard-import com.tamed.music.constants.* to access the full set of keys.

Player communication via PlayerConnection

The UI must communicate with the player exclusively through PlayerConnection. Do not bind to MusicService from Activities, Fragments, or Composables. PlayerConnection is provided as a ViewModel-scoped dependency and exposes all required player commands and state streams.

Pull Request Process

1

Fork and branch

Fork the repository on GitHub and create a feature branch with a descriptive name. Use the format feature/short-description for new features and fix/short-description for bug fixes. Never commit directly to main.
2

Run all quality gates

Before pushing, run lint, ktlint check, and unit tests locally:
./gradlew lintDebug ktlintCheck testDebugUnitTest
Fix all reported issues before opening the PR.
3

Write a clear PR description

Include what changed, why it changed, and reference the relevant GitHub issue number (e.g. Closes #42). If the change affects the UI, attach before/after screenshots. If the change modifies database schema, describe the migration strategy.
4

Preserve GPL-3.0 copyright notices

Tamed is licensed under GPL-3.0. Every source file carries the copyright header:
/*
 * Tamed Project (2026)
 * Original project contributors
 * Licensed Under GPL-3.0 | see git history for contributors
 */
Do not remove or modify this header in files you edit. New files you create must include it.

Internationalisation

Tamed ships with support for more than 40 locales. The LanguageCodeToName map in com.tamed.music.constants defines every supported language tag. Translations are managed via Crowdin, not manually.
A crowdin.yml configuration file is present in the repository root. It defines the source and target file paths for Crowdin synchronisation. Only edit the two English base string files: app/src/main/res/values/strings.xml and app/src/main/res/values/tamed_strings.xml. All other locale files (values-es/strings.xml, values-zh-CN/strings.xml, etc.) are automatically synchronised by Crowdin and should not be edited by hand.
Pull requests that include manual edits to non-English string resource files will be rejected. If you are a native speaker who wants to contribute a translation, use the Crowdin project instead of submitting a PR with string file changes.
The generateLocaleConfig = true setting in app/build.gradle.kts tells the Android build system to auto-generate the LocaleConfig XML from the available values-* resource directories, so newly synced locales are automatically registered with the system locale picker.

Build docs developers (and LLMs) love