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.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.
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:Lint
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.Kotlin formatting (ktlint)
./gradlew ktlintFormat first. Commits with ktlint failures will not be merged.Unit tests
Kotlin Code Style
Follow the official Kotlin coding conventions in addition to the ktlint rules enforced automatically.- Data Classes
- Sealed Interfaces
- Coroutines
- State Management
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.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
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.Run all quality gates
Before pushing, run lint, ktlint check, and unit tests locally:Fix all reported issues before opening the PR.
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.Internationalisation
Tamed ships with support for more than 40 locales. TheLanguageCodeToName 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.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.