summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorClaude <noreply@anthropic.com>2026-09-25 07:23:58 +0000
committerClaude <noreply@anthropic.com>2026-09-25 07:23:58 +0000
commite5608851415ec04a8450678e6399441467194ddd (patch)
tree3e9a399d939b8daaef557a827e4fa9533700736d
parent0b45924bfb78557ce207f10ffd3ceccd55df2485 (diff)
Sign debug builds with a shared, checked-in keystore
"App not installed" when updating. Android generates a debug keystore per machine, so tungsten's build and the laptop's build are signed differently and each refuses to install over the other. Reproduced exactly: INSTALL_FAILED_UPDATE_INCOMPATIBLE, "signatures do not match newer version". The published APKs show the collision plainly -- 0930d0 on the 21st, 106ecafe in between, 0930d0 again after my last publish -- so an update had roughly a coin-flip chance of being installable depending on which machine built it. The checked-in key is tungsten's existing debug keystore, stock alias and password, not a freshly generated one. That matters: a new key would have been just as unusable as the mismatch, forcing a second uninstall and a second loss of every playback position, right after Peter had already done one. Verified by installing the currently-published APK and updating in place over it. Safe to commit -- it signs debug builds of a personal sideloaded app. It must never sign a release, where the key is the app's identity. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018AwnALZL6bD4VBtATDVv3B
-rw-r--r--CLAUDE.md8
-rw-r--r--app/build.gradle.kts26
-rw-r--r--keystore/listen-debug.keystorebin0 -> 2618 bytes
3 files changed, 34 insertions, 0 deletions
diff --git a/CLAUDE.md b/CLAUDE.md
index f97e524..8308bb8 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -155,6 +155,14 @@ skipping. `skipIntroMs`/`skipOutroMs` exist on `SubscriptionEntity` and nothing
Twice the better answer was restructuring instead: `Screens.kt` was split per screen and
`ListenViewModel` was split into library and player halves rather than raising a limit again.
+- **Debug builds are signed with the checked-in `keystore/listen-debug.keystore`**, not the
+ per-machine one Android generates. Two machines build this app, and differently-signed APKs
+ refuse to install over each other: "App not installed", or
+ `INSTALL_FAILED_UPDATE_INCOMPATIBLE`, with the only remedy an uninstall that destroys every
+ playback position. The checked-in file is tungsten's original debug keystore, with the stock
+ alias and password, because that is the key already on the phone. Never swap it for a fresh
+ one — that costs another uninstall. Never use it for a release.
+
- **Feeds are big.** A real subscription in testing produced 2753 episodes in one refresh. Any
per-episode work — a query in a loop, an O(n²) match — needs to be considered at that scale.
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index f680a08..a81df9b 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -37,6 +37,32 @@ android {
}
+ signingConfigs {
+ // A checked-in debug key, deliberately.
+ //
+ // Android generates a per-machine debug keystore, so a build from tungsten and a build
+ // from the laptop are signed differently and each refuses to install over the other --
+ // "App not installed", with the only remedy being an uninstall that destroys every
+ // playback position. Two machines build this app, so the key has to be shared.
+ //
+ // This is tungsten's existing debug keystore rather than a fresh one, chosen because
+ // that is the key already installed on the phone -- a new key would have forced a
+ // second uninstall and a second loss of every playback position.
+ //
+ // Safe to commit: it signs debug builds of a personal sideloaded app and nothing else.
+ // It must never be used for a Play release, where the signing key is the app's identity.
+ getByName("debug") {
+ storeFile = file("$rootDir/keystore/listen-debug.keystore")
+ // Android's standard debug credentials: this IS the platform debug keystore,
+ // copied in so both machines sign identically. Keeping the stock alias and
+ // password means an APK built here is byte-compatible with one built by a
+ // default Android toolchain on any other machine.
+ storePassword = "android"
+ keyAlias = "androiddebugkey"
+ keyPassword = "android"
+ }
+ }
+
buildTypes {
debug {
enableUnitTestCoverage = true
diff --git a/keystore/listen-debug.keystore b/keystore/listen-debug.keystore
new file mode 100644
index 0000000..c2b34ce
--- /dev/null
+++ b/keystore/listen-debug.keystore
Binary files differ