diff --git a/apps/flipcash/app/build.gradle.kts b/apps/flipcash/app/build.gradle.kts
index 7080336941..ba301ebe12 100644
--- a/apps/flipcash/app/build.gradle.kts
+++ b/apps/flipcash/app/build.gradle.kts
@@ -37,6 +37,14 @@ val bugsnagBuildId: String = providers.exec {
commandLine("git", "rev-parse", "HEAD")
}.standardOutput.asText.get().trim()
+// Tracked changes only: CI writes google-services.json, local.properties and the signing key
+// into the checkout, all gitignored, so a release build still reads as clean. Reading just the
+// exit code keeps the configuration cache from invalidating on every edit to a dirty tree.
+val gitTreeDirty: Boolean = providers.exec {
+ commandLine("git", "diff", "--quiet", "HEAD")
+ isIgnoreExitValue = true
+}.result.get().exitValue != 0
+
val contributorsSigningConfig = ContributorsSignatory(rootDir)
val appNamespace = "${Gradle.flipcashNamespace}.app.android"
@@ -61,6 +69,8 @@ android {
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
buildConfigField("String", "VERSION_NAME", "\"${Packaging.Flipcash.versionName}\"")
+ buildConfigField("String", "GIT_SHA", "\"$bugsnagBuildId\"")
+ buildConfigField("Boolean", "GIT_DIRTY", "$gitTreeDirty")
buildConfigField("String", "MIXPANEL_API_KEY", "\"${tryReadProperty(rootProject.rootDir, "MIXPANEL_API_KEY")}\"")
buildConfigField("Boolean", "NOTIFY_ERRORS", "false")
diff --git a/apps/flipcash/app/src/main/kotlin/com/flipcash/app/inject/AppModule.kt b/apps/flipcash/app/src/main/kotlin/com/flipcash/app/inject/AppModule.kt
index cbc65b893a..074c4f9f30 100644
--- a/apps/flipcash/app/src/main/kotlin/com/flipcash/app/inject/AppModule.kt
+++ b/apps/flipcash/app/src/main/kotlin/com/flipcash/app/inject/AppModule.kt
@@ -30,7 +30,9 @@ object AppModule {
@Singleton
fun providesAppVersion(): VersionInfo = VersionInfo(
versionName = BuildConfig.VERSION_NAME,
- versionCode = BuildConfig.VERSION_CODE
+ versionCode = BuildConfig.VERSION_CODE,
+ commitSha = BuildConfig.GIT_SHA,
+ isDirty = BuildConfig.GIT_DIRTY,
)
@Provides
diff --git a/apps/flipcash/core/src/main/kotlin/com/flipcash/app/core/android/VersionInfo.kt b/apps/flipcash/core/src/main/kotlin/com/flipcash/app/core/android/VersionInfo.kt
index 0d9b16f3ed..69aedc9c9a 100644
--- a/apps/flipcash/core/src/main/kotlin/com/flipcash/app/core/android/VersionInfo.kt
+++ b/apps/flipcash/core/src/main/kotlin/com/flipcash/app/core/android/VersionInfo.kt
@@ -5,4 +5,24 @@ import javax.inject.Inject
class VersionInfo @Inject constructor(
val versionName: String = "",
val versionCode: Int = 0,
-)
\ No newline at end of file
+ /** The full commit SHA the build was made from; empty when the build carries none. */
+ val commitSha: String = "",
+ /** Whether the build was made with uncommitted changes to tracked files. */
+ val isDirty: Boolean = false,
+) {
+ /**
+ * The commit as the version footer shows it: the first [COMMIT_LABEL_LENGTH] characters of
+ * [commitSha], with `*` appended for a build with uncommitted changes. Null when the
+ * build carries no commit. The iOS footer uses the same format.
+ */
+ val commitLabel: String?
+ get() {
+ if (commitSha.isBlank()) return null
+ val short = commitSha.trim().take(COMMIT_LABEL_LENGTH)
+ return if (isDirty) "$short*" else short
+ }
+
+ companion object {
+ const val COMMIT_LABEL_LENGTH = 10
+ }
+}
diff --git a/apps/flipcash/core/src/main/res/values/strings.xml b/apps/flipcash/core/src/main/res/values/strings.xml
index c981d0eff9..6daf7f2aca 100644
--- a/apps/flipcash/core/src/main/res/values/strings.xml
+++ b/apps/flipcash/core/src/main/res/values/strings.xml
@@ -136,7 +136,9 @@
Something went wrong. The cash could not be collected
- Version %1$s • Build %2$s%3$s
+ Version %1$s • Build %2$s\n%3$s%4$s
+
+ Version %1$s • Build %2$s%3$s
No Beta Features
There are no beta features available right now.
diff --git a/apps/flipcash/core/src/test/kotlin/com/flipcash/app/core/android/VersionInfoTest.kt b/apps/flipcash/core/src/test/kotlin/com/flipcash/app/core/android/VersionInfoTest.kt
new file mode 100644
index 0000000000..ac705be3f6
--- /dev/null
+++ b/apps/flipcash/core/src/test/kotlin/com/flipcash/app/core/android/VersionInfoTest.kt
@@ -0,0 +1,40 @@
+package com.flipcash.app.core.android
+
+import kotlin.test.Test
+import kotlin.test.assertEquals
+import kotlin.test.assertNull
+
+class VersionInfoTest {
+
+ private val sha = "2acfecffeb1c1e7b8d64b0e3c6b7a1f2d9e0c4a5"
+
+ @Test
+ fun `the label is the first ten characters of the SHA`() {
+ assertEquals("2acfecffeb", VersionInfo(commitSha = sha).commitLabel)
+ }
+
+ @Test
+ fun `a dirty build appends an asterisk`() {
+ assertEquals("2acfecffeb*", VersionInfo(commitSha = sha, isDirty = true).commitLabel)
+ }
+
+ @Test
+ fun `a SHA shorter than ten characters is shown whole`() {
+ assertEquals("2acfecf", VersionInfo(commitSha = "2acfecf").commitLabel)
+ }
+
+ @Test
+ fun `surrounding whitespace is not counted toward the ten characters`() {
+ assertEquals("2acfecffeb", VersionInfo(commitSha = " $sha\n").commitLabel)
+ }
+
+ @Test
+ fun `an empty SHA has no label`() {
+ assertNull(VersionInfo().commitLabel)
+ }
+
+ @Test
+ fun `a blank SHA has no label, even when dirty`() {
+ assertNull(VersionInfo(commitSha = " ", isDirty = true).commitLabel)
+ }
+}
diff --git a/apps/flipcash/features/menu/src/main/kotlin/com/flipcash/app/menu/internal/MenuScreenContent.kt b/apps/flipcash/features/menu/src/main/kotlin/com/flipcash/app/menu/internal/MenuScreenContent.kt
index 598b803edc..0b5a479598 100644
--- a/apps/flipcash/features/menu/src/main/kotlin/com/flipcash/app/menu/internal/MenuScreenContent.kt
+++ b/apps/flipcash/features/menu/src/main/kotlin/com/flipcash/app/menu/internal/MenuScreenContent.kt
@@ -930,7 +930,10 @@ private fun ShareTile(
}
}
-/** The "Version … • Build …" footer; its repeated tap toggles beta access (see the ViewModel). */
+/**
+ * The two-line version footer, "Version … • Build …" over " •