Build and Release APK / Build APK and publish release (push) Canceled after 0s
The bootstrap rebranded the app to Pilgrims of Katan (WALA-118 review); the decision is to keep the upstream's own identity instead. - Title and splash: the upstream originals restored exactly — the Wallace tartan splash under the "Pro Libertate Games" uncial wordmark (SplashScreen.kt, Tartan.kt), the tartan launcher icon, and the Theme.ProLibertateGames window theme. The game screens go back to the upstream WallaceGold/WallaceRed accents the tartan is woven from. - Names and colours: launcher names back to "Pro Libertate Games" / "Pro Libertate Games DEV"; the channel colours back to the clan red / hunting green setts (resValue tartan_field and friends). The installed identity stays ca.wallacenet.pilgrimsofkatan (+ .dev) — that is the package, not the brand. - Catalogue: Katan is listed under Board Games (3-4 players, original blurb, no third-party marks) with available = false, so dev builds show it as "Coming soon" and a production build hides it until its engine and screen land. - Docs: README/CLAUDE.md/docs/CI.md updated to the restored identity; CatalogTest's invariant now names Katan as the one deliberate exception rather than asserting nothing is unfinished. Structural bootstrap untouched: applicationId, OTA repointed at the Gitea releases, CI workflows, version stamping. Verified: ./gradlew assembleDebug and testDebugUnitTest green — 628 tests, 0 failures. Co-Authored-By: Paperclip <[email protected]>
186 lines
8.3 KiB
Kotlin
186 lines
8.3 KiB
Kotlin
plugins {
|
|
id("com.android.application")
|
|
id("org.jetbrains.kotlin.android")
|
|
id("org.jetbrains.kotlin.plugin.serialization")
|
|
}
|
|
|
|
// Which update channel this build belongs to, set by whichever workflow built
|
|
// it: release.yml stamps "production" from main, ci.yml stamps "dev" from the
|
|
// dev branch. Unset for a local build, which counts as dev.
|
|
//
|
|
// GITHUB_RUN_NUMBER alone is not enough to key off, because it counts per
|
|
// workflow — the two pipelines have entirely independent sequences. That is
|
|
// also why versionCode is only ever compared *within* a channel; see
|
|
// UpdateChannel for how switching between them is handled.
|
|
val buildChannel = System.getenv("PLG_CHANNEL").orEmpty()
|
|
val runNumber = (System.getenv("GITHUB_RUN_NUMBER") ?: "").toIntOrNull() ?: 0
|
|
val stampVersion = buildChannel.isNotEmpty() && runNumber > 0
|
|
|
|
// The fixed floor every stamped build number is measured from, in
|
|
// gradle.properties. Gitea Actions restarts its run numbers at 1 for each
|
|
// workflow, so an unstamped build (versionCode 1) and the first release of a
|
|
// fresh instance would otherwise collide — and once a phone has installed
|
|
// versionCode N, Android refuses anything smaller as a downgrade. Adding a
|
|
// permanent offset is what makes "the counter never resets" true by
|
|
// construction: it is read from one place, it never changes, and the tag,
|
|
// versionName and versionCode all get the same number (see docs/CI.md).
|
|
val runOffset = (project.findProperty("plgVersionRunOffset") as String?)?.toIntOrNull() ?: 0
|
|
|
|
// The stamped build number: what the release tag, the versionName and the
|
|
// versionCode are all named after.
|
|
val buildNumber = runNumber + runOffset
|
|
|
|
// The version series, from gradle.properties. Both workflows read the same
|
|
// property when they name a release tag, so the tag and the APK inside it
|
|
// cannot disagree about which series they belong to.
|
|
val versionSeries = (project.findProperty("plgVersionSeries") as String?) ?: "1.0"
|
|
|
|
// Anything that is not an explicit production build is a dev build: ci.yml
|
|
// stamps "dev", and a local build with nothing set counts as dev too.
|
|
//
|
|
// This one flag drives everything that makes a dev build a separate app rather
|
|
// than a replacement for the production one — its own applicationId, its own
|
|
// launcher name, and the hunting sett in place of the clan sett. They have to
|
|
// move together: a dev package wearing the production name and colours is
|
|
// exactly the confusion having both installed is meant to avoid.
|
|
val isDevBuild = buildChannel != "production"
|
|
|
|
android {
|
|
// Deliberately left at the upstream source package: R and BuildConfig stay
|
|
// in org.prolibertate.games for both channels, so no source file has to
|
|
// care which applicationId it ended up installed as. The *installed*
|
|
// identity is the applicationId below — that is what the rebrand changes
|
|
// (WALA-118), not the JVM package names.
|
|
namespace = "org.prolibertate.games"
|
|
compileSdk = 34
|
|
|
|
defaultConfig {
|
|
// A dev build installs under its own package, so a dev copy and a
|
|
// production copy can sit side by side on one device instead of one
|
|
// replacing the other.
|
|
applicationId = if (isDevBuild) "ca.wallacenet.pilgrimsofkatan.dev" else "ca.wallacenet.pilgrimsofkatan"
|
|
minSdk = 24
|
|
targetSdk = 34
|
|
// The -dev suffix in versionName is what the installed app reads to
|
|
// know its own channel, so it must survive into the shipped APK. It
|
|
// tracks isDevBuild rather than the stamp alone, so an unstamped local
|
|
// build cannot end up claiming a channel its applicationId contradicts.
|
|
// versionCode is the stamped build number and nothing else, so it keeps
|
|
// climbing across a change of series: bumping 1.0 to 1.1 must not make
|
|
// an update look like a downgrade to Android. An unstamped build is 1,
|
|
// below every release, which is the honest answer for a build nobody
|
|
// stamped.
|
|
versionCode = if (stampVersion) buildNumber else 1
|
|
versionName = when {
|
|
stampVersion && buildChannel == "production" -> "$versionSeries.$buildNumber"
|
|
stampVersion -> "$versionSeries.$buildNumber-dev"
|
|
isDevBuild -> "$versionSeries.0-dev"
|
|
else -> "$versionSeries.0"
|
|
}
|
|
|
|
// The launcher label. With both copies on the phone the app drawer shows
|
|
// two entries, and this is what names them apart.
|
|
resValue(
|
|
"string",
|
|
"app_name",
|
|
if (isDevBuild) "Pro Libertate Games DEV" else "Pro Libertate Games",
|
|
)
|
|
|
|
// The one colour that separates the two setts: Wallace red for
|
|
// production, Wallace Hunting green for dev. The icon vectors reference
|
|
// this rather than a literal, and Tartan.kt picks the matching Compose
|
|
// colour off DEV_BUILD below — the two have to stay in step or the
|
|
// launcher icon and the splash it opens on would be different cloth.
|
|
resValue("color", "tartan_field", if (isDevBuild) "#1E7A3C" else "#E01B24")
|
|
// Shades of the same field, so a launch does not flash the wrong colour
|
|
// before the tartan is drawn: splash_background is held behind the
|
|
// window for that instant, ic_launcher_background is the icon's flat
|
|
// fallback.
|
|
resValue("color", "splash_background", if (isDevBuild) "#0D2E17" else "#4A0B10")
|
|
resValue("color", "ic_launcher_background", if (isDevBuild) "#145C2D" else "#B3121B")
|
|
|
|
buildConfigField("boolean", "DEV_BUILD", isDevBuild.toString())
|
|
|
|
// In-app OTA updates, pointed at this project's own Gitea releases
|
|
// (docs/CI.md). True in this build — the channel exists. Flipping it
|
|
// false is the kill switch: every check then reports that and fetches
|
|
// nothing, with the machinery and its tests still compiled.
|
|
buildConfigField("boolean", "OTA_ENABLED", "true")
|
|
}
|
|
|
|
signingConfigs {
|
|
// A fixed, checked-in debug keystore (standard "android" password) so
|
|
// every build — local or CI — is signed with the same key. Without this,
|
|
// each CI runner would generate a random debug key and OTA updates would
|
|
// fail to install with a signature mismatch.
|
|
getByName("debug") {
|
|
storeFile = file("debug.keystore")
|
|
storePassword = "android"
|
|
keyAlias = "androiddebugkey"
|
|
keyPassword = "android"
|
|
}
|
|
}
|
|
|
|
buildTypes {
|
|
release {
|
|
isMinifyEnabled = false
|
|
proguardFiles(
|
|
getDefaultProguardFile("proguard-android-optimize.txt"),
|
|
"proguard-rules.pro"
|
|
)
|
|
}
|
|
}
|
|
|
|
buildFeatures {
|
|
compose = true
|
|
// For DEV_BUILD, which is how the Compose tartan picks its sett.
|
|
buildConfig = true
|
|
}
|
|
|
|
composeOptions {
|
|
// Must stay in step with the Kotlin version declared in the root build file.
|
|
kotlinCompilerExtensionVersion = "1.5.14"
|
|
}
|
|
|
|
compileOptions {
|
|
sourceCompatibility = JavaVersion.VERSION_17
|
|
targetCompatibility = JavaVersion.VERSION_17
|
|
}
|
|
|
|
kotlinOptions {
|
|
jvmTarget = "17"
|
|
}
|
|
|
|
packaging {
|
|
resources.excludes += "/META-INF/{AL2.0,LGPL2.1}"
|
|
}
|
|
|
|
testOptions {
|
|
unitTests.isReturnDefaultValues = true
|
|
}
|
|
}
|
|
|
|
dependencies {
|
|
implementation("androidx.core:core-ktx:1.13.1")
|
|
implementation("androidx.activity:activity-compose:1.9.0")
|
|
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.8.2")
|
|
implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.8.2")
|
|
|
|
val composeBom = platform("androidx.compose:compose-bom:2024.06.00")
|
|
implementation(composeBom)
|
|
implementation("androidx.compose.ui:ui")
|
|
implementation("androidx.compose.ui:ui-graphics")
|
|
implementation("androidx.compose.ui:ui-tooling-preview")
|
|
implementation("androidx.compose.material3:material3")
|
|
debugImplementation("androidx.compose.ui:ui-tooling")
|
|
|
|
// Settings persistence for sound / animation speed / update preferences.
|
|
implementation("androidx.datastore:datastore-preferences:1.1.1")
|
|
|
|
// Wire format for the LAN game protocol.
|
|
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3")
|
|
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.1")
|
|
|
|
testImplementation("junit:junit:4.13.2")
|
|
}
|