Split from #414
The iOS portion of #414 is implemented and simulator-verified. At the requester’s direction, Android completion is tracked independently here rather than holding the iOS issue open.
Current implementation
Android already has the kept-image loader on main in 1e866bb. This is not a request to implement it again. MainActivityNative.kt.template checks CraftNativeMedia.local(this, uri) before the public image cache/network, while retaining the existing image-source scheme policy.
Commit 2107ffe strengthens keptPrivateImageLoadsOfflineWithoutEnteringPublicCache in NativeNavigationTest.kt: an explicit protocol node is updated through updateNode, and assertions verify that the same mounted ImageView handles source replacement.
Remaining verification
Earlier API 34 and API 35 runs failed at "kept bytes must not allow arbitrary file URLs" after successfully displaying the kept image. The fixture retained an ImageView reference across full-document renders; a stale reference was suspected but has NOT been established as the root cause. The revised fixture must pass native instrumentation before Android is considered complete. Source-contract tests alone are not sufficient.
The latest main Mobile E2E run was still pending when this issue was filed: https://github.com/craft-native/craft/actions/runs/40326490959
Acceptance
- Run the kept-private-image instrumentation regression on Android API 34 and 35; investigate and fix any failure rather than relaxing assertions.
- A kept image keyed by an HTTPS .invalid address loads offline into the mounted ImageView with the expected pixels.
- Private bytes do not enter the public image cache.
- Arbitrary file URLs remain rejected and clear the previous image.
- Source mutations retain the mounted view; late decode results cannot replace a newer source.
- A corrupt kept copy reports an error without silently fetching a network replacement.
- Publish the Android package containing the verified implementation and name the actual published version in the closing comment.
As checked on 26 October 2026, the published @craft-native/android version is still 0.0.358 and does not contain this fix. The application should keep the response with craft.media.keep(url, { headers }) and use the original HTTPS URL as the image source, not a picker/download filesystem path.