YAML Metadata Warning:empty or missing yaml metadata in repo card

Check out the documentation for more information.

Mouture

Kotlin Jetpack Compose AGP API JDK License

Mouture is a modern Android launcher (home screen replacement) built with Kotlin and Jetpack Compose.

It is a minimal home screen with a 100% on-device AI and verifiable privacy: no account, no telemetry by default, and nothing β€” content or derived insight β€” ever leaves the phone. The assistant runs a real language model locally (Gemma 3 1B via LiteRT-LM, or Gemma 4 E2B for Pro users who opt in), so it works in airplane mode.

The launcher is free, including everything it computes on its own: smart reminders, smart folders, usage patterns, wellbeing stats, widgets, and the full theming palette. Mouture AI (all LLM features, Pro only) unlocks what the language model writes β€” chat, narrations, AI names and labels β€” plus the premium surface styles and home transitions. The underlying entitlement is Mouture Pro via RevenueCat.

Table of contents

Module dependency graph

42 Gradle modules. :app wires together nine features, each split into an api module (the navigation entry point and public contract) and an impl module (screens, view models, DI), which in turn build on 19 :core:* modules β€” :core:designsystem for theming, :core:launcher for the installed-apps data layer, :core:data/:core:database/:core:datastore for persistence, :core:llm for the on-device model.

The graph below is :app's dependency closure, so it omits what the shipped app does not compile against: the four test-support modules (:core:testing, :core:launcher-test, :core:datastore-test, :core:screenshot-testing, all pulled in via test configurations), :app-mouture-catalog (the design-system catalog app), :lint (custom lint checks, wired via lintPublish), :ui-test-hilt-manifest, and the build-logic included build.

The graph below is generated β€” do not edit it by hand. Regenerate every module's graph with ./gradlew graphUpdate; CI runs the same task and auto-commits any drift. Each module's own README carries its own scoped graph.

---
config:
  layout: elk
  elk:
    nodePlacementStrategy: SIMPLE
---
graph TB
  subgraph :feature
    direction TB
    subgraph :feature:assistant
      direction TB
      :feature:assistant:api[api]:::android-library
      :feature:assistant:impl[impl]:::android-library
    end
    subgraph :feature:calendar
      direction TB
      :feature:calendar:api[api]:::android-library
      :feature:calendar:impl[impl]:::android-library
    end
    subgraph :feature:email
      direction TB
      :feature:email:api[api]:::android-library
      :feature:email:impl[impl]:::android-library
    end
    subgraph :feature:launcher
      direction TB
      :feature:launcher:api[api]:::android-library
      :feature:launcher:impl[impl]:::android-library
    end
    subgraph :feature:notes
      direction TB
      :feature:notes:api[api]:::android-library
      :feature:notes:impl[impl]:::android-library
    end
    subgraph :feature:onboarding
      direction TB
      :feature:onboarding:api[api]:::android-library
      :feature:onboarding:impl[impl]:::android-library
    end
    subgraph :feature:search
      direction TB
      :feature:search:api[api]:::android-library
      :feature:search:impl[impl]:::android-library
    end
    subgraph :feature:settings
      direction TB
      :feature:settings:api[api]:::android-library
      :feature:settings:impl[impl]:::android-library
    end
    subgraph :feature:wellbeing
      direction TB
      :feature:wellbeing:api[api]:::android-library
      :feature:wellbeing:impl[impl]:::android-library
    end
  end
  subgraph :core
    direction TB
    :core:analytics[analytics]:::android-library
    :core:appfunctions[appfunctions]:::android-library
    :core:common[common]:::jvm-library
    :core:data[data]:::android-library
    :core:database[database]:::android-library
    :core:datastore[datastore]:::android-library
    :core:datastore-proto[datastore-proto]:::jvm-library
    :core:designsystem-components[designsystem-components]:::android-library
    :core:designsystem-theme[designsystem-theme]:::android-library
    :core:email[email]:::android-library
    :core:launcher[launcher]:::android-library
    :core:launcherimport[launcherimport]:::android-library
    :core:llm[llm]:::android-library
    :core:model[model]:::jvm-library
    :core:navigation[navigation]:::android-library
    :core:premiumui[premiumui]:::android-library
    :core:ui[ui]:::android-library
  end
  :benchmarks[benchmarks]:::android-test
  :app[app]:::android-application

  :app -.->|baselineProfile| :benchmarks
  :app -.-> :core:analytics
  :app -.-> :core:appfunctions
  :app -.-> :core:common
  :app -.-> :core:data
  :app -.-> :core:designsystem-components
  :app -.-> :core:launcher
  :app -.-> :core:model
  :app -.-> :core:ui
  :app -.-> :feature:assistant:api
  :app -.-> :feature:assistant:impl
  :app -.-> :feature:calendar:api
  :app -.-> :feature:calendar:impl
  :app -.-> :feature:email:api
  :app -.-> :feature:email:impl
  :app -.-> :feature:launcher:api
  :app -.-> :feature:launcher:impl
  :app -.-> :feature:notes:api
  :app -.-> :feature:notes:impl
  :app -.-> :feature:onboarding:api
  :app -.-> :feature:onboarding:impl
  :app -.-> :feature:search:api
  :app -.-> :feature:search:impl
  :app -.-> :feature:settings:api
  :app -.-> :feature:settings:impl
  :app -.-> :feature:wellbeing:api
  :app -.-> :feature:wellbeing:impl
  :benchmarks -.->|testedApks| :app
  :core:appfunctions -.-> :core:data
  :core:appfunctions -.-> :core:model
  :core:data -.-> :core:analytics
  :core:data --> :core:common
  :core:data -.-> :core:database
  :core:data --> :core:datastore
  :core:data --> :core:email
  :core:data -.-> :core:launcher
  :core:data -.-> :core:llm
  :core:database --> :core:model
  :core:datastore -.-> :core:common
  :core:datastore --> :core:datastore-proto
  :core:datastore --> :core:model
  :core:designsystem-components --> :core:designsystem-theme
  :core:designsystem-theme --> :core:model
  :core:email --> :core:common
  :core:email -.-> :core:model
  :core:launcher --> :core:common
  :core:launcher --> :core:model
  :core:launcherimport --> :core:common
  :core:launcherimport --> :core:model
  :core:llm --> :core:common
  :core:llm -.-> :core:datastore
  :core:premiumui --> :core:designsystem-components
  :core:premiumui --> :core:model
  :core:ui --> :core:analytics
  :core:ui --> :core:designsystem-components
  :core:ui --> :core:launcher
  :core:ui --> :core:model
  :feature:assistant:api --> :core:navigation
  :feature:assistant:impl -.-> :core:data
  :feature:assistant:impl -.-> :core:designsystem-components
  :feature:assistant:impl -.-> :core:launcher
  :feature:assistant:impl -.-> :core:llm
  :feature:assistant:impl -.-> :core:model
  :feature:assistant:impl -.-> :core:premiumui
  :feature:assistant:impl -.-> :core:ui
  :feature:assistant:impl -.-> :feature:assistant:api
  :feature:calendar:api --> :core:navigation
  :feature:calendar:impl -.-> :core:data
  :feature:calendar:impl -.-> :core:designsystem-components
  :feature:calendar:impl -.-> :core:premiumui
  :feature:calendar:impl -.-> :core:ui
  :feature:calendar:impl -.-> :feature:calendar:api
  :feature:email:api --> :core:navigation
  :feature:email:impl -.-> :core:data
  :feature:email:impl -.-> :core:designsystem-components
  :feature:email:impl -.-> :core:model
  :feature:email:impl -.-> :core:ui
  :feature:email:impl -.-> :feature:email:api
  :feature:launcher:api --> :core:navigation
  :feature:launcher:impl -.-> :core:data
  :feature:launcher:impl -.-> :core:designsystem-components
  :feature:launcher:impl -.-> :core:launcher
  :feature:launcher:impl -.-> :core:ui
  :feature:launcher:impl -.-> :feature:launcher:api
  :feature:notes:api --> :core:navigation
  :feature:notes:impl -.-> :core:data
  :feature:notes:impl -.-> :core:designsystem-components
  :feature:notes:impl -.-> :core:premiumui
  :feature:notes:impl -.-> :core:ui
  :feature:notes:impl -.-> :feature:notes:api
  :feature:onboarding:api --> :core:navigation
  :feature:onboarding:impl -.-> :core:common
  :feature:onboarding:impl -.-> :core:data
  :feature:onboarding:impl -.-> :core:designsystem-components
  :feature:onboarding:impl -.-> :core:launcher
  :feature:onboarding:impl -.-> :core:model
  :feature:onboarding:impl -.-> :core:ui
  :feature:onboarding:impl -.-> :feature:onboarding:api
  :feature:search:api --> :core:navigation
  :feature:search:impl -.-> :core:data
  :feature:search:impl -.-> :core:designsystem-components
  :feature:search:impl -.-> :core:launcher
  :feature:search:impl -.-> :core:ui
  :feature:search:impl -.-> :feature:search:api
  :feature:settings:api --> :core:navigation
  :feature:settings:impl -.-> :core:data
  :feature:settings:impl -.-> :core:designsystem-components
  :feature:settings:impl -.-> :core:launcher
  :feature:settings:impl -.-> :core:launcherimport
  :feature:settings:impl -.-> :core:model
  :feature:settings:impl -.-> :core:premiumui
  :feature:settings:impl -.-> :core:ui
  :feature:settings:impl -.-> :feature:settings:api
  :feature:wellbeing:api --> :core:navigation
  :feature:wellbeing:impl -.-> :core:data
  :feature:wellbeing:impl -.-> :core:designsystem-components
  :feature:wellbeing:impl -.-> :core:launcher
  :feature:wellbeing:impl -.-> :core:model
  :feature:wellbeing:impl -.-> :core:ui
  :feature:wellbeing:impl -.-> :feature:wellbeing:api

classDef android-application fill:#CAFFBF,stroke:#000,stroke-width:2px,color:#000;
classDef android-feature fill:#FFD6A5,stroke:#000,stroke-width:2px,color:#000;
classDef android-library fill:#9BF6FF,stroke:#000,stroke-width:2px,color:#000;
classDef android-test fill:#A0C4FF,stroke:#000,stroke-width:2px,color:#000;
classDef jvm-library fill:#BDB2FF,stroke:#000,stroke-width:2px,color:#000;
classDef unknown fill:#FFADAD,stroke:#000,stroke-width:2px,color:#000;
πŸ“‹ Graph legend
graph TB
  application[application]:::android-application
  feature[feature]:::android-feature
  library[library]:::android-library
  jvm[jvm]:::jvm-library

  application -.-> feature
  library --> jvm

classDef android-application fill:#CAFFBF,stroke:#000,stroke-width:2px,color:#000;
classDef android-feature fill:#FFD6A5,stroke:#000,stroke-width:2px,color:#000;
classDef android-library fill:#9BF6FF,stroke:#000,stroke-width:2px,color:#000;
classDef android-test fill:#A0C4FF,stroke:#000,stroke-width:2px,color:#000;
classDef jvm-library fill:#BDB2FF,stroke:#000,stroke-width:2px,color:#000;
classDef unknown fill:#FFADAD,stroke:#000,stroke-width:2px,color:#000;

Install

You need JDK 17+ (CI builds on Zulu 21), the Android SDK platform 37, and Android Studio (latest stable) if you want the IDE.

git clone ssh://git@github.com/doppio-lab/mouture.git
cd mouture
./gradlew :app:installDebug

No secrets are required for a fresh clone. app/google-services.json is committed, the RevenueCat keys in MoutureApplication.kt are public SDK keys, and release builds fall back to the debug signing key when keystore.properties is absent β€” copy keystore.properties.template only if you need to sign with the real upload key.

To use Mouture as your launcher, press the home button (Android will ask which home app to use), or set it directly:

adb shell cmd package set-home-activity \
  com.doppio.android.mouture.debug/com.doppio.android.mouture.MainActivity

Enable the Git hooks once per clone so commits are auto-formatted (Spotless) and never fail the CI spotlessCheck gate:

git config core.hooksPath .githooks

And hide the repo-wide ktfmt reformat from git blame, so it does not sit on top of every line (GitHub's blame view applies this file on its own):

git config blame.ignoreRevsFile .git-blame-ignore-revs

One runtime note: Mouture AI downloads a 557 MB Gemma 3 1B model on first use. Pro users can swap it for Gemma 4 E2B (2.6 GB, confirmed in a dialog first) from the model manager; one model is on disk at a time. Debug builds ship an aiTestMode toggle (Settings β†’ developer tools) that swaps in a fake LLM and skips the download.

Test

Layer Command
Unit (JVM + Robolectric) ./gradlew testDebug
A single test ./gradlew testDebug --tests "com.doppio.android.mouture.MyTest"
Screenshot (Roborazzi) ./gradlew verifyRoborazziDebug
Instrumented (Gradle-managed device) ./gradlew pixel6api31aospDebugAndroidTest
End-to-end (Maestro) maestro test .maestro

Screenshot goldens are recorded by CI, not from a workstation β€” if verifyRoborazziDebug fails on a PR, CI runs recordRoborazziDebug and commits the new goldens for you. A screenshot failure you cannot reproduce locally is usually a rendering difference between your machine and the Linux runner: replay the job through act (see Deploy) and you get CI's exact pixels, plus the diff PNGs in /tmp/act-artifacts/. See .maestro/README.md for the E2E flows and their tags.

Quality gates, in the order CI runs them:

./gradlew spotlessCheck        # ./gradlew spotlessApply to fix
./gradlew detekt               # ./gradlew detektBaseline after a large refactor
./gradlew dependencyGuard      # ./gradlew dependencyGuardBaseline to regenerate
./gradlew graphUpdate          # module dependency graphs
./gradlew :app:lintRelease :app-mouture-catalog:lintRelease :lint:lint
./gradlew :app:checkReleaseBadging
./gradlew koverXmlReportMerged   # 40% overall, 60% on changed files

Deploy

Five GitHub Actions workflows, tuned for the free runner tier (no emulator on the hot path).

Workflow Trigger Outcome
ci.yml PR, push to main/dev Quality gates + JVM tests. On push: signed release APK β†’ Firebase App Distribution (testers group)
release.yml tag v*, manual Signed AAB β†’ Google Play internal track, draft + a draft GitHub Release with the AAB and R8 mapping.txt
nightly-baseline.yml cron 42 4 * * * Regenerates the baseline profile on an emulator, auto-commits it to dev
landing-deploy.yml changes under landing/** Firebase Hosting β€” preview channel on PRs, live on main/dev
emulator-debug.yml branch ci/emulator-debug Throwaway diagnostic, not part of the pipeline

Cutting a release: push a tag and the pipeline does the rest.

git tag v1.2.3 && git push origin v1.2.3

Both the Play upload and the GitHub Release land as drafts β€” publishing stays a manual decision in the Play Console. A manual workflow_dispatch run has dry_run defaulting to true: it resolves a version and stops before building.

Versioning is read from Play, not from the repo. The resolve-version composite action queries the Play Developer API and computes versionCode = max(bundles, apks, tracks, floor) + 1. Because Play is the authority that enforces uniqueness, re-running a release is safe and a run that fails before uploading consumes no code. release.yml calls it in strict mode (any API failure fails the run); ci.yml calls it non-strict, so Play being unreachable does not turn every push red.

Expect πŸ€– commits on your PR. CI auto-commits regenerated dependency graphs, screenshot goldens, dependency-guard baselines, and nightly baseline profiles.

You can run the workflows locally before pushing. nektos/act replays them on your machine, in a container that carries the Android SDK, against the real secrets:

./scripts/act.sh pull_request -W .github/workflows/ci.yml -e .github/act/events/pull_request.json

Three things to remember. The event payload under .github/act/events/ is what decides which if: branches fire β€” pull_request.json and push-main.json are mirror images of each other. Adding -n lists the steps a payload selects in two seconds without running anything, which is worth doing before paying for a full Gradle run. And artifacts, including the Roborazzi diff PNGs, land under /tmp/act-artifacts/. First-time setup β€” Rosetta, the runner image, .secrets β€” plus the recipes for the other three workflows and the handful of act quirks worth knowing are in .github/CICD.md.

Required secrets, one-time Play/Firebase setup, and how signing works in CI are documented in the same file.

Documentation

Document What it covers
AGENTS.md Architecture, module layout, testing conventions
.github/CICD.md CI/CD in depth: secrets, signing, versioning, R8, and running the workflows locally with act
docs/mouture-ai-rules.md Invariants RG1–RG6 that every AI feature must honor
docs/appfunctions.md Driving Mouture's agent surface from a phone: discover, execute, read errors
docs/audit-mouture-ai-smart-reminders.md Β§2 is the authoritative Free/Pro inventory πŸ‡«πŸ‡·
docs/book/ Engineer onboarding and beta-tester guide πŸ‡«πŸ‡·
.maestro/README.md End-to-end flows and tags
*/README.md Every module documents its own dependency graph

πŸ‡«πŸ‡· marks documents written in French.

License

Mouture is distributed under the terms of the Apache License (Version 2.0). See LICENSE for more information.

Downloads last month

-

Downloads are not tracked for this model. How to track
Inference Providers NEW
This model isn't deployed by any Inference Provider. πŸ™‹ Ask for provider support