No description
  • Kotlin 99.7%
  • Shell 0.3%
Find a file
Rune Jensen ca42dbcd75
Some checks failed
ci/woodpecker/push/build Pipeline was canceled
ci/woodpecker/tag/build Pipeline was successful
Bump version to 0.4.1
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01STsrVABBMYp3a9cXfrqJ41
2026-07-17 16:52:44 +00:00
.woodpecker A4: release signing, Woodpecker tag release pipeline, app icon, docs 2026-07-14 16:02:53 +00:00
app Bump version to 0.4.1 2026-07-17 16:52:44 +00:00
gradle Print class QR labels on a Niimbot B1 over BLE 2026-07-16 13:40:36 +00:00
.gitignore A0 foundation: gradle project, typed API client, session store, nav scaffold, test harness 2026-07-14 04:00:30 +00:00
build.gradle.kts A0 foundation: gradle project, typed API client, session store, nav scaffold, test harness 2026-07-14 04:00:30 +00:00
build.sh A0 foundation: gradle project, typed API client, session store, nav scaffold, test harness 2026-07-14 04:00:30 +00:00
gradle.properties A0 foundation: gradle project, typed API client, session store, nav scaffold, test harness 2026-07-14 04:00:30 +00:00
gradlew A0 foundation: gradle project, typed API client, session store, nav scaffold, test harness 2026-07-14 04:00:30 +00:00
gradlew.bat A0 foundation: gradle project, typed API client, session store, nav scaffold, test harness 2026-07-14 04:00:30 +00:00
PLAN.md Add pantryapp plan: native Kotlin/Compose kitchen companion for pantryapi 2026-07-13 17:42:27 +00:00
README.md Consume in whole packages everywhere 2026-07-17 10:42:01 +00:00
settings.gradle.kts A0 foundation: gradle project, typed API client, session store, nav scaffold, test harness 2026-07-14 04:00:30 +00:00
SMOKE.md Consume in whole packages everywhere 2026-07-17 10:42:01 +00:00

pantryapp

Android companion app for pantryapi — the kitchen tool: scan a barcode, book packages in, quick-consume, check stock levels, see what's short. Booking and consuming both move whole packages of a barcode — a 330 ml can goes out as 1 can, never 150 ml — with the package size coming from the product, so the scan sheet and the quick-consume dialog just count packages (typed quantities remain only as the fallback for classes with no sized product). The Manage tab covers the everyday additions — new locations, product groups and product classes — and when a scanned product isn't assigned to a class yet, you can create the class and set the package size right in the scan sheet. Class QR labels print directly on a Niimbot B1 (Bluetooth, 50×30 mm labels) — select the printer once in Settings, then "Print label" on any class detail page; the QR encodes the class UUID, the same payload the web UI's printable labels use, so either kind scans back to the class. Heavier surgery stays on the pantryapi web UI (pantryweb), reachable from any browser on the same server: renames and deletes, class splits and user administration. Think of the app as the thing you keep in the kitchen for the five-second interactions, and the web UI as the thing you open at a desk once in a while to reorganize.

Native Kotlin + Jetpack Compose, minSdk 31 (Android 12+). See PLAN.md for the full plan, screen list and work packages.

Building

Everything runs in Docker; no local Android toolchain needed.

./build.sh                  # unit tests + debug APK (default)
./build.sh test             # just the unit tests
./build.sh assembleDebug    # just the APK -> app/build/outputs/apk/debug/
./build.sh assembleRelease  # R8-minified release APK -> app/build/outputs/apk/release/

Unit tests are plain JVM: the typed API client runs against a MockWebServer fake pantryapi with the same canned fixtures as the web UI's fake.

assembleRelease works locally without any signing secrets — it just produces an unsigned app-release-unsigned.apk, useful for checking that R8 shrinking still builds cleanly. Signed release APKs are built by CI on tags; see "Release/signing" below.

Installing

Signed APKs are attached to Forgejo releases (see the repo's Releases tab) on every version tag. Two ways to install:

  • Direct: download pantryapp-<tag>.apk from the latest release and open it on the device (enable "install unknown apps" for your browser or file manager once, when prompted).
  • Obtainium: add this repo as an app source (it auto-detects Forgejo/Gitea releases) and Obtainium will track new tags and offer updates, the same way it would for an F-Droid or GitHub-hosted app. There is no Play Store listing.

First-run setup

On first launch the app asks for:

  1. Server URL — your pantryapi instance, e.g. http://pantry.local:8084 or https://pantry.example.com. The app probes /healthz before continuing, so a wrong URL or unreachable server fails fast with a clear message rather than a silent hang.
  2. Username / password — the same credentials as the web UI (the bootstrap admin user works out of the box on a fresh pantryapi).

The resulting JWT is stored encrypted on-device (DataStore + Android Keystore) and is valid for 30 days, so re-login is rare; a 401 from the API (expired/revoked token) drops you back to this screen automatically.

HTTPS is recommended if the server is reachable from outside your LAN, but plain cleartext HTTP works fine for a household server on the local network — the app explicitly allows cleartext traffic (usesCleartextTraffic) since that's the common case for a self-hosted pantryapi. Put the server behind a TLS-terminating proxy if you ever expose it beyond the LAN.

Release/signing (for maintainers)

Release builds are signed with a dedicated app-signing keystore (never the debug key) and R8-minified. The signing config in app/build.gradle.kts reads the keystore path and passwords from environment variables and is only applied when they're set — this is what lets local ./build.sh assembleRelease succeed unsigned, and lets CI sign only on tag builds where the secrets are injected.

One-time: generate the keystore

keytool -genkeypair -v \
  -keystore pantryapp-release.jks \
  -alias pantryapp \
  -keyalg RSA -keysize 4096 -validity 10000

Keep the resulting .jks and its passwords somewhere durable and private (a password manager, not this repo) — losing it means future releases can no longer update installs signed with it, and Android will refuse to install an update signed with a different key over an existing install.

Woodpecker secrets

.woodpecker/build.yaml builds and signs the release APK only on tag events, using these repo secrets:

Secret Value
pantryapp_keystore base64 -w0 pantryapp-release.jks output
pantryapp_keystore_password keystore password
pantryapp_key_alias key alias (pantryapp above)
pantryapp_key_password key password
gitea_token Forgejo API token with write access to releases (shared with pantryapi's release step)

Set them in the Woodpecker UI under the repo's Secrets settings. On a tag push, CI decodes the keystore to a temp file, runs assembleRelease with PANTRYAPP_KEYSTORE/PANTRYAPP_KEYSTORE_PASSWORD/PANTRYAPP_KEY_ALIAS/ PANTRYAPP_KEY_PASSWORD set, renames the output to pantryapp-${CI_COMMIT_TAG}.apk, and attaches it to the matching Forgejo release.

Manual on-device verification

There's no automated UI/instrumentation test suite (see PLAN.md — unit tests cover the API client and business logic against a MockWebServer fake). After a release build, run through SMOKE.md against a live pantryapi stack before trusting a tag.