# Encore

<img src="fastlane/metadata/android/en-US/images/icon.png" width="120" align="right" alt="Encore app icon">

A minimal, modern Android app: **Kotlin** + **Jetpack Compose** (Material 3), built and
tested with **Nix**.

Jetpack Compose *is* the modern Android UI framework — declarative Kotlin UI that has
replaced the old XML layouts. Material 3 is Google's current design system, wired up here
with dynamic (wallpaper-based) color on Android 12+.

## Screenshots

| Now Playing | Library | Queue | Settings |
|:-----------:|:-------:|:-----:|:--------:|
| <img src="fastlane/metadata/android/en-US/images/phoneScreenshots/1.png" width="180" alt="Now Playing"> | <img src="fastlane/metadata/android/en-US/images/phoneScreenshots/2.png" width="180" alt="Album grid"> | <img src="fastlane/metadata/android/en-US/images/phoneScreenshots/3.png" width="180" alt="Queue"> | <img src="fastlane/metadata/android/en-US/images/phoneScreenshots/4.png" width="180" alt="Settings"> |

## Layout

```
flake.nix                     Nix entrypoint: dev shells, SDK, package, emulator
package.nix                   Reproducible APK build (nixpkgs-style derivation)
deps.json                     Lockfile of Gradle/Maven deps (generated — see below)
settings.gradle.kts           Gradle project + repositories
build.gradle.kts              Root build script (declares plugin versions)
gradle/libs.versions.toml     Version catalog: all dependency/plugin versions
app/
  build.gradle.kts            The :app module (Android + Compose config)
  src/main/
    AndroidManifest.xml       App/activity declaration
    kotlin/ca/ksamad/encore/
      MainActivity.kt         Entry activity; sets the Compose content
      Theme.kt                Material 3 theme (dynamic color)
    res/values/               strings.xml, themes.xml
  src/test/kotlin/...         A plain JVM unit test
```

## Everyday Development (Recommended)

Use the dev shell — it puts a JDK, Gradle, and the Android SDK on your PATH with the
right environment variables set:

```sh
nix develop
gradle assembleDebug        # build the debug APK -> app/build/outputs/apk/debug/
gradle testDebugUnitTest    # run unit tests
gradle tasks                # see everything available
```

The debug APK is unsigned but installable on a device/emulator:

```sh
adb install app/build/outputs/apk/debug/app-debug.apk
```

### Android Studio

For the GUI editor, Compose previews, and device manager:

```sh
nix develop .#studio
android-studio
```

Open this folder as an existing project. (Android Studio manages its own SDK; the
command-line flow above is fully independent of it.)

### Emulator

```sh
nix run .#emulate
```

## Reproducible Build with Nix

```sh
nix build          # -> ./result/encore.apk
```

Unlike `gradle assembleDebug`, this build runs **fully offline**: every Gradle/Maven
dependency is pinned by hash in `deps.json`, and Nix supplies the Android SDK. It also
runs the unit tests (`testDebugUnitTest`) as its check phase, so a green `nix build` means
both "it assembles" and "tests pass". This is what you'd use in CI for a reproducible
artifact.

> Note: `package.nix` and `app/build.gradle.kts` contain a few small workarounds, active
> **only** during dependency capture (gated on the `IN_GRADLE_UPDATE_DEPS` env var), that
> stop Nix's "resolve every configuration" sweep from tripping over internal AGP/Kotlin
> configurations. They don't affect the dev shell or the actual build. See the comments in
> `app/build.gradle.kts` for the details.

### Regenerating `deps.json`

Any time you change a dependency or plugin version (i.e. edit
`gradle/libs.versions.toml` or a `build.gradle.kts`), regenerate the lockfile:

```sh
nix build .#default.mitmCache.updateScript
./result                    # runs the build under a proxy, rewrites ./deps.json
```

Then commit the updated `deps.json`. (It works by recording Gradle's dependency
downloads through a man-in-the-middle proxy and storing their hashes — see
`package.nix`.)

## Docs

- [`docs/device-testing.md`](docs/device-testing.md) — build & install onto a
  real Android device/DAP over USB (and the NixOS `adb` notes).
- [`docs/TODO.md`](docs/TODO.md) — near-term roadmap / known bugs.

## License

Encore is licensed under the [Apache License 2.0](LICENSE).

## Version notes

Pinned in `gradle/libs.versions.toml`: AGP 8.7.3, Kotlin 2.0.21, Compose BOM 2024.10.01,
compiled against `compileSdk` 35, `minSdk` 24. Nix supplies Gradle 8.14. If you bump
these, keep AGP/Kotlin/Gradle mutually compatible and regenerate `deps.json`.
