Make tongue-out stage 3 actually reach users: ship its model, stop latching failed init - #36
Merged
Merged
Conversation
Found on a new test device (nubia NX809J, Android 16) running the
released v0.3.1: the logs showed a failure warning and a success line
at the same millisecond.
W/MainViewModel: Échec d'initialisation de TongueEmbeddingHelper : …
I/MainViewModel: TongueEmbeddingHelper initialisé (délégué GPU=false)
Two defects behind that, not just a cosmetic log problem:
- setup() only signalled failure through the onError callback, so the
caller had no way to know. It now returns a Boolean, which is
unambiguous. It has a single caller and no test depends on its
signature.
- ensureTongueEmbeddingHelper() assigned the instance to the field
whatever happened, then logged "initialisé" unconditionally. Since
the function's own entry guard is `if (tongueEmbeddingHelper != null)
return`, a failed setup was latched for the whole process lifetime:
toggling the experimental setting off and on again never retried. The
field is now only set on success, and the failed instance is closed,
so a later attempt is possible again -- worthwhile for a
circumstantial failure (memory, GPU delegate), even though the most
common one, a missing model asset, will fail identically.
The success log stays unconditional on TONGUE_DIAGNOSTIC_LOGGING as
before, but now sits after the success check, so it can no longer
contradict the warning printed a moment earlier.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Stage 3 of tongue-out detection (revue technique points 15/56) has never worked in any published build. Both model files are gitignored, and all three workflows only ever downloaded face_landmarker.task, so image_embedder.tflite was simply absent from every release APK. The feature only ever ran on locally built APKs, where the model sits in app/src/main/assets from the manual download the README documents. Confirmed on device (nubia NX809J, Android 16) against the published v0.3.1: "Impossible d'initialiser le modèle de détection de langue tirée (CPU et GPU en échec)" -- both delegates failing is exactly what a missing asset looks like. The feature is opt-in and degrades without crashing, which is why this went unnoticed: point 56 recorded it as shipped and device-confirmed, which was true of local debug builds but never of the artifact users install. release.yml now downloads both models, since that is the APK that gets published. ci.yml does too, so the CI build stays identical to the published one and a future packaging problem surfaces on a PR rather than at release time. codeql.yml deliberately keeps downloading only the first: it just compiles for analysis, where a runtime asset changes nothing. That asymmetry is commented in both files so it is not "fixed" later by mistake. The URL was checked rather than copied on faith: HTTP 200 and 4 117 670 bytes, byte-for-byte the size of the local model already known to work. Expect the published APK to grow by about 4 MB. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
3 tasks
guyiome
added a commit
that referenced
this pull request
Sep 16, 2026
Patch release whose point is to get the tongue-out stage 3 packaging fix (#36) into an artifact users actually install: image_embedder.tflite was downloaded by no workflow, so the feature could never initialise in any published APK. Also carries the failed-init latch fix from the same PR. As for v0.3.1, bumping versionName is a prerequisite rather than bookkeeping: the in-app update checker compares BuildConfig.VERSION_NAME against the latest release tag, so tagging v0.3.2 on a build still reporting 0.3.1 would leave a permanent "update available" badge on devices already running the newest build. Verified the built APK reports the new version rather than assuming the edit took (aapt2 dump badging: versionCode='5' versionName='0.3.2'). Unit tests, debug build and lint pass. Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two separate defects, both found by plugging in a new test device (nubia NX809J, Android 16) running the published v0.3.1.
1. The model was in no published APK
Both models are gitignored, and all three workflows only downloaded
face_landmarker.task.image_embedder.tflitewas therefore absent from every release, so stage 3 of tongue-out detection (points 15/56) could never initialise for anyone installing from GitHub Releases. It only ever worked on locally built APKs, where the model is present from the manual download the README documents.Confirmed on device against v0.3.1: "Impossible d'initialiser le modèle de détection de langue tirée (CPU et GPU en échec)" — both delegates failing is exactly what a missing asset looks like.
Why it went unnoticed: the feature is opt-in and its failure is caught cleanly (logged, no crash). Point 56 recorded it as shipped and device-confirmed, which was true of local debug builds but never of the artifact users install.
release.ymlandci.ymlnow download both models — CI too, so the CI build stays identical to the published one and a future packaging problem surfaces on a PR rather than at release time.codeql.ymldeliberately keeps fetching only the first: it compiles for analysis, where a runtime asset changes nothing. That asymmetry is commented in both files so it isn't "fixed" later by mistake.The URL was checked rather than copied on faith: HTTP 200, 4 117 670 bytes — byte-for-byte the size of the local model already known to work. Expect the published APK to grow by ~4 MB.
2. A failed init was latched and reported as a success
The device logs showed a failure warning and
TongueEmbeddingHelper initialiséat the same millisecond.ensureTongueEmbeddingHelper()assigned the instance whatever happened and logged success unconditionally — and since its own entry guard isif (tongueEmbeddingHelper != null) return, a failed setup was latched for the whole process lifetime: toggling the experimental setting off and on never retried.setup()now returns a Boolean (single caller, no test depended on its signature; signalling failure only throughonErrorleft the caller unable to tell). The field is set only on success, the failed instance is closed, and the success log moved after the check.Test plan
testDebugUnitTest,assembleDebug,lintDebuggreen. The 3 extra lint warnings versus two weeks ago are all "a newer version is available" (CameraX 1.6.2, compose-bom 2026.09.00, ARCore 1.56.0, Kotlin plugins 2.4.20), none from this change — the only warning on the touched files,EmptySuperCall, predates it.🤖 Generated with Claude Code