Install dev and production side by side, in the hunting sett

A dev build now installs under its own applicationId, org.prolibertate.games.dev,
so it is a separate app to Android and sits beside the production copy instead
of replacing it. Each channel still updates itself normally, because an update
within a channel keeps the same package.

Three things move together off one isDevBuild flag, because a dev package
wearing the production name and colours is exactly the mix-up that having both
installed is meant to prevent:

  - applicationId gets a .dev suffix;
  - the launcher label becomes "Pro Libertate Games DEV";
  - the cloth becomes Wallace Hunting, the same threadcount with green where
    the clan sett has red.

The name and the colours reach the resources through resValue, so app_name,
tartan_field, splash_background and ic_launcher_background are generated by the
build rather than declared in res/values. The icon vectors reference
@color/tartan_field instead of a literal, which is how one set of vectors serves
both colourways without a second copy of the paths. BuildConfig.DEV_BUILD
carries the same flag into Kotlin, where AppSett picks the Compose sett for the
splash, and WallaceHuntingSett is derived from the same threadcount builder as
WallaceSett so the two cannot drift.

versionName now tracks isDevBuild rather than the CI stamp alone, so an
unstamped local build cannot claim a channel its applicationId contradicts.

Separate packages also mean a channel switch can no longer be refused as a
downgrade; the settings copy and the README said it could, and now say what
actually happens.

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01JDuAayWXLRoUNTsfE79Mg1
This commit is contained in:
Claude
2026-08-06 22:07:32 +00:00
parent 742add4d07
commit 7939395c3c
11 changed files with 303 additions and 137 deletions
+49 -4
View File
@@ -51,12 +51,20 @@ them.
- **Production** — stable builds, published from `main`.
- **Dev** — preview builds, published from `dev` on every green CI run.
The two install as separate apps, so both can be kept on one device — see
[Both channels on one device](#both-channels-on-one-device).
## Branding
The launcher icon is a single square of the Wallace tartan set on the bias, and
the app opens on the same cloth carrying the name in a Celtic uncial hand for two
seconds before fading into the menu.
A dev build wears **Wallace Hunting** instead — the same threadcount with green
where the clan sett has red — and installs as *Pro Libertate Games DEV*. That is
what tells the two copies apart when both are on the phone; see
[Both channels on one device](#both-channels-on-one-device).
The tartan is drawn, not an image: `ui/theme/Tartan.kt` lays the sett down as
warp and then as weft at half opacity, which is what produces the blended
squares instead of a flat grid. The icon is the same construction as a vector
@@ -72,7 +80,9 @@ Two honest caveats:
and the narrow black guard sits *between two red blocks*. The icon is
generated from the same numbers, so the icon and the splash cannot drift.
Weave scale is derived from the sett rather than fixed, so changing the
threadcount rescales both instead of silently zooming in.
threadcount rescales both instead of silently zooming in. The hunting
colourway is the same threadcount passed a different field colour, not a
second copy of the numbers, so it cannot fall out of step either.
- **The typeface is bundled under a licence.** Uncial Antiqua, © 2011 Brian J.
Bonislawsky DBA Astigmatic, Reserved Font Name "Uncial Antiqua", used under
the SIL Open Font License 1.1. The full licence is at
@@ -101,6 +111,11 @@ smaller than a fingertip.
Requires JDK 17 and the Android SDK (compileSdk 34).
A local build is a dev build: it installs as `org.prolibertate.games.dev` under
the name *Pro Libertate Games DEV* in the hunting sett, so it goes on beside an
installed release rather than over it. Set `PLG_CHANNEL=production` to build the
production package instead.
## CI
- `.github/workflows/ci.yml` — builds, tests and lints on `dev` and on pull
@@ -161,9 +176,39 @@ is the run number of the workflow that produced it, and the two workflows count
separately. Versions are therefore only ever compared *within* a channel; the app
reads the `-dev` suffix in its own `versionName` to know which channel it is on,
and treats a change of channel as an explicit switch rather than an upgrade.
One consequence: switching channels can be a downgrade as far as Android is
concerned, in which case the install is refused until the current copy is
uninstalled. The settings screen says so when it applies.
### Both channels on one device
A dev build installs under its own `applicationId`, `org.prolibertate.games.dev`,
so it is a separate app as far as Android is concerned and can sit beside the
production copy rather than replacing it. Each channel still updates itself
normally, because an update within a channel keeps the same package.
Three things move together, all off `isDevBuild` in `app/build.gradle.kts`:
| | Production | Dev |
| --- | --- | --- |
| `applicationId` | `org.prolibertate.games` | `org.prolibertate.games.dev` |
| Launcher name | Pro Libertate Games | Pro Libertate Games DEV |
| Sett | Wallace (red) | Wallace Hunting (green) |
Anything that is not an explicit `PLG_CHANNEL=production` build is a dev build,
so a local `assembleDebug` is a dev build too and installs alongside a release
without uninstalling anything.
They have to move together. A dev package wearing the production name and
colours is precisely the mix-up that having both installed is meant to prevent,
which is why one flag drives all three rather than each being set by hand. The
name and the colours reach the resources through `resValue`, so `app_name`,
`tartan_field`, `splash_background` and `ic_launcher_background` are generated
by the build and are deliberately *not* in `res/values/` — the icon vectors
reference `@color/tartan_field` rather than a literal, which is how one set of
vectors serves both colourways. `BuildConfig.DEV_BUILD` carries the same flag
into Kotlin, where `AppSett` picks the matching Compose sett for the splash.
Switching channels in Settings therefore installs the other channel's app
alongside this one and leaves this one in place; the settings screen says so
when the selected channel is not the installed one.
The debug keystore is committed on purpose: every build is signed with the same
key, so an OTA update installs over the previous one instead of being rejected