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:
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")
}