Skip to content

martiege/task-champ

Repository files navigation

task-champ

A native Android client for TaskChampion, the storage and sync engine behind TaskWarrior 3.

Built on a shared Rust core with a Jetpack Compose UI. Syncs against any taskchampion-sync-server, so the same task list works on your phone and your terminal.

Task list Edit task Home-screen widget

More screenshots in fastlane/metadata/android/en-US/images/phoneScreenshots/.

Features

  • Read & write tasks — list, add, edit, complete, and delete with swipes.
  • Filter & search — by status, project, and tag from the top bar; free-text search across descriptions.
  • Urgency scoring — TaskWarrior-style urgency (priority + age + due-date proximity + tag count) shown next to each task; the list sorts by it.
  • Sync — talks to any taskchampion-sync-server over HTTP(S); use a TLS reverse proxy for any non-local deployment.
  • Background sync — opt-in periodic sync; minimum 15 min (Android's lower bound).
  • Home-screen widget — top pending tasks by urgency; tap a row to open, tap the circle to complete without opening the app.
  • Material You theming — dynamic colour on Android 12+, with a manual light / dark / system override.
  • Offline-first — local SQLite replica; sync is incremental and resumable.

Status

Early but usable. The code shape and FFI surface are stable; UI features land round-by-round. Tested on Android 14 (API 34) emulator and a personal device. Min SDK 26 (Android 8.0).

Install

No prebuilt APK or store listing yet. Build from source — see below.

F-Droid metadata for an eventual listing lives under fastlane/metadata/android/en-US/. Submission to the F-Droid official repository is pending: the build recipe (metadata/dev.taskchamp.app.yml in the fdroiddata repository) needs to be added there, not here.

Getting started

The fastest path is via the Nix dev shell, which pins the entire toolchain (Rust, Android SDK 34, NDK 26, JDK 17, Gradle, the sync server, the emulator, and a taskchamp-emulator wrapper for the GL/Qt env you need on NixOS). On NixOS or with the Nix package manager (flakes enabled):

git clone https://github.com/<you>/task-champ
cd task-champ
nix develop                # one-shot toolchain bootstrap
just sync-server &         # local TaskChampion sync server on :8080
just seed-tasks            # push a few demo tasks
just build-apk             # cross-compiles Rust core + builds debug APK
just emulator              # boot a windowed AVD (see "Emulator" below)
just install               # adb installDebug
adb shell am start -n dev.taskchamp.app/.MainActivity

That's it — open the app, tap the gear icon, paste the dev sync creds from the Dev creds table below, and the seeded tasks should appear after a manual sync.

Emulator

There are four ways to get a device, depending on your host:

Recipe When to use
just emulator x86_64 host with KVM (/dev/kvm exists). Curated AVD, fast.
just emulator-headless Same as above, no window. CI / scrcpy mirroring over SSH.
just emulator-tcg No KVM available (cloud VM, BIOS-locked). arm64 + TCG, slow.
just emulator-avd Use your own AVD created with avdmanager. AVD=<name>.

If just emulator complains x86_64 emulation currently requires hardware acceleration, your host has no KVM — use just emulator-tcg or plug in a real device and skip the emulator entirely. See DEVELOPMENT.md for the full story.

Without Nix

You'll need: a recent Rust stable toolchain, cargo-ndk, the Android SDK with platform 34 + NDK 26, and JDK 17. The Gradle build invokes cargo ndk for x86_64 and arm64-v8a by default; override with -PrustTargets=arm64-v8a.

Sync server

You need a running taskchampion-sync-server somewhere reachable. The Nix dev shell ships one:

just sync-server           # runs on 0.0.0.0:8080 with SQLite backend

For a real deployment, run it behind a reverse proxy with TLS. The Android app accepts cleartext HTTP for development convenience; switch to HTTPS in production.

In the app, open Settings (gear icon) and configure:

Field Value
Server URL https://your.server (or http://10.0.2.2:8080 for the emulator)
Client ID a UUID — same on every device that should share state
Encryption secret a passphrase — same on every device

The client ID and secret form your replica identity. Any device with the same pair will sync the same tasks. The secret is used by the TaskChampion sync protocol to derive the on-the-wire encryption key; the server stores only what the protocol sends it. Treat HTTPS-via-reverse-proxy as a hard requirement for any non-local deployment.

Architecture

┌────────────────────────┐
│  Jetpack Compose UI    │
│  (TasksViewModel,      │
│   Compose screens)     │
└──────────┬─────────────┘
           │ Kotlin
           │ ↓ UniFFI-generated bindings
┌──────────┴─────────────┐
│  taskchamp_core (Rust) │ ← libtaskchamp_core.so per ABI
│  - replica wrapper     │
│  - urgency scoring     │
│  - sync (blocking FFI) │
└──────────┬─────────────┘
           │ depends on
           ↓
   GothenburgBitFactory/taskchampion crate

The Rust core wraps taskchampion::Replica and exposes a small typed FFI surface through UniFFI. The Compose layer talks to it via the generated Replica Kotlin class. All FFI methods are blocking on the Rust side and dispatched from Dispatchers.IO in the view-model — see DEVELOPMENT.md for why.

Dev creds for the bundled seed tool

just seed-tasks pushes a small fixed task list to a local sync server using these defaults; paste them into the Android app to see the seeded tasks immediately:

Field Value
Server URL http://10.0.2.2:8080 (emulator → host loopback)
Client ID 966626b2-73df-4030-8595-b984aff7a3df
Secret test

Override via SYNC_URL, CLIENT_ID, SECRET, DATA_DIR. Re-running seed-tasks is idempotent: it pulls current state first and only pushes descriptions that aren't already present.

License

MIT — see LICENSE.

About

Android client for TaskChampion / TaskWarrior 3

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors