app

Local-first trade for farms and co-ops
git clone https://radroots.dev/git/app.git
Log | Files | Refs | README | LICENSE

commit 4e47ad1a9b2c119d92f8bb4a53bc29521535b68e
parent afac741ccc895dfc6372fa004732ecc1c48c4892
Author: triesap <tyson@radroots.org>
Date:   Sun, 22 Mar 2026 01:04:11 +0000

android: use stamped offline geocoder revisions

- load the android offline geocoder revision from the stamped asset sidecar instead of package update time
- treat a missing or invalid android geocoder revision sidecar as a build-asset unavailability state
- keep android geocoder staging keyed by the stamped revision so unchanged datasets reuse the same staged path
- validate the stamped revision contract with the existing android crate tests and apk asset packaging build

Diffstat:
Mplatforms/android/app/src/main/kotlin/org/radroots/app/android/RadRootsAndroidAppBridge.kt | 44+++++++++++++++++++++++++++++++++-----------
1 file changed, 33 insertions(+), 11 deletions(-)

diff --git a/platforms/android/app/src/main/kotlin/org/radroots/app/android/RadRootsAndroidAppBridge.kt b/platforms/android/app/src/main/kotlin/org/radroots/app/android/RadRootsAndroidAppBridge.kt @@ -6,6 +6,7 @@ import java.io.FileNotFoundException object RadRootsAndroidAppBridge { private const val GEOCODER_ASSET_PATH = "geocoder/geonames.db" + private const val GEOCODER_REVISION_ASSET_PATH = "geocoder/geonames.revision" private const val GEOCODER_FILE_NAME = "geonames.db" private const val GEOCODER_ERROR_KIND_MISSING_BUILD_ASSET = 1 private const val GEOCODER_ERROR_KIND_INITIALIZATION_FAILED = 2 @@ -33,14 +34,8 @@ object RadRootsAndroidAppBridge { GEOCODER_ERROR_KIND_INTERNAL_ERROR, "android app bridge is not initialized", ) - val targetDir = try { - stagedGeocoderDirectory(context) - } catch (source: Exception) { - return fail( - GEOCODER_ERROR_KIND_INTERNAL_ERROR, - "failed to resolve android geocoder revision: ${source.message ?: source.javaClass.simpleName}", - ) - } + val revision = loadGeocoderRevision(context) ?: return null + val targetDir = stagedGeocoderDirectory(context, revision) if (!targetDir.exists() && !targetDir.mkdirs()) { return fail( GEOCODER_ERROR_KIND_INITIALIZATION_FAILED, @@ -76,9 +71,36 @@ object RadRootsAndroidAppBridge { } } - private fun stagedGeocoderDirectory(context: Context): File { - val packageInfo = context.packageManager.getPackageInfo(context.packageName, 0) - val revision = packageInfo.lastUpdateTime.toString() + private fun loadGeocoderRevision(context: Context): String? { + val revision = try { + context.assets.open(GEOCODER_REVISION_ASSET_PATH).bufferedReader().use { it.readText() } + } catch (_: FileNotFoundException) { + return fail( + GEOCODER_ERROR_KIND_MISSING_BUILD_ASSET, + "android bundled geocoder revision asset missing at assets/$GEOCODER_REVISION_ASSET_PATH", + ) + } catch (source: Exception) { + return fail( + GEOCODER_ERROR_KIND_MISSING_BUILD_ASSET, + "failed to read android geocoder revision asset at assets/$GEOCODER_REVISION_ASSET_PATH: ${source.message ?: source.javaClass.simpleName}", + ) + }.trim() + + if (!isValidRevision(revision)) { + return fail( + GEOCODER_ERROR_KIND_MISSING_BUILD_ASSET, + "android bundled geocoder revision asset invalid at assets/$GEOCODER_REVISION_ASSET_PATH", + ) + } + + return revision + } + + private fun isValidRevision(revision: String): Boolean { + return revision.length == 64 && revision.all { it.isDigit() || it.lowercaseChar() in 'a'..'f' } + } + + private fun stagedGeocoderDirectory(context: Context, revision: String): File { return File(context.noBackupFilesDir, "RadRoots/app/android/geocoder/$revision") }