diff options
| author | Claude <noreply@anthropic.com> | 2026-05-15 23:34:48 +0000 |
|---|---|---|
| committer | Claude <noreply@anthropic.com> | 2026-05-15 23:34:48 +0000 |
| commit | bbfcb667d9510e604676ac92cecc69f9bc39e440 (patch) | |
| tree | f1a3a8d02ffff9f1006852dc98711936a11960d8 /android-app/app | |
| parent | 79a175ab56e03b4e8031e48c139060a0f434c0ab (diff) | |
fix: recover tracks after reinstall via MediaScanner; add version to startup log
Android clears OWNER_PACKAGE_NAME for all MediaStore entries when an app is
uninstalled. Files survive on disk in Documents/Nav/ but the ownership query
returns 0 rows after reinstall. Fix: if the first load finds nothing, call
MediaScannerConnection.scanFile() on Documents/Nav/. Because our app triggers
the scan, the MediaScanner re-registers each file with our package as owner,
restoring the query results on the immediate retry.
Also log BuildConfig.VERSION_NAME + VERSION_CODE in the startup message so
the build is identifiable directly from the in-app debug log.
https://claude.ai/code/session_01DNjbYxiC1cco83dArNGW3G
Diffstat (limited to 'android-app/app')
3 files changed, 47 insertions, 3 deletions
diff --git a/android-app/app/src/main/kotlin/org/terst/nav/MainActivity.kt b/android-app/app/src/main/kotlin/org/terst/nav/MainActivity.kt index bfa285d..8609456 100644 --- a/android-app/app/src/main/kotlin/org/terst/nav/MainActivity.kt +++ b/android-app/app/src/main/kotlin/org/terst/nav/MainActivity.kt @@ -223,7 +223,7 @@ class MainActivity : AppCompatActivity(), SafetyFragment.SafetyListener { hudDepth.text = "—" applyUnitLabels() - NavLogger.i("app", "started") + NavLogger.i("app", "started v${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})") setupBottomSheet() setupBottomNavigation() setupHandlers() diff --git a/android-app/app/src/main/kotlin/org/terst/nav/track/TrackRepository.kt b/android-app/app/src/main/kotlin/org/terst/nav/track/TrackRepository.kt index 3c5c9d5..bd79985 100644 --- a/android-app/app/src/main/kotlin/org/terst/nav/track/TrackRepository.kt +++ b/android-app/app/src/main/kotlin/org/terst/nav/track/TrackRepository.kt @@ -107,10 +107,21 @@ class TrackRepository(context: Context) { if (!pastTracksLoaded) { pastTracksLoaded = true NavLogger.i("track", "getPastTracks: loading from storage (first call)") - val stored = storage.loadAllTracks() + var stored = storage.loadAllTracks() NavLogger.i("track", "getPastTracks: storage returned ${stored.size} tracks " + "(points: ${stored.map { it.size }})") - // Merge: put stored tracks behind any in-memory tracks from this session + + if (stored.isEmpty()) { + // Android clears OWNER_PACKAGE_NAME on uninstall; the files survive on + // disk but the MediaStore query finds nothing. Triggering a rescan via + // our app re-registers the files under our package name, restoring ownership. + NavLogger.i("track", "getPastTracks: 0 tracks — triggering MediaScanner rescan") + storage.triggerNavDirRescan() + stored = storage.loadAllTracks() + NavLogger.i("track", "getPastTracks: after rescan: ${stored.size} tracks " + + "(points: ${stored.map { it.size }})") + } + _pastTracks.addAll(stored) NavLogger.i("track", "getPastTracks: total in memory=${_pastTracks.size}") } diff --git a/android-app/app/src/main/kotlin/org/terst/nav/track/TrackStorage.kt b/android-app/app/src/main/kotlin/org/terst/nav/track/TrackStorage.kt index 0951a38..59d7e53 100644 --- a/android-app/app/src/main/kotlin/org/terst/nav/track/TrackStorage.kt +++ b/android-app/app/src/main/kotlin/org/terst/nav/track/TrackStorage.kt @@ -2,11 +2,14 @@ package org.terst.nav.track import android.content.ContentValues import android.content.Context +import android.media.MediaScannerConnection import android.net.Uri import android.os.Build import android.os.Environment import android.provider.MediaStore import android.util.Log +import kotlinx.coroutines.suspendCancellableCoroutine +import kotlinx.coroutines.withTimeoutOrNull import org.terst.nav.ui.NavLogger import java.io.File import java.time.Instant @@ -255,4 +258,34 @@ class TrackStorage(private val context: Context) { ?.takeIf { it.isNotEmpty() } } ?: emptyList() } + + // ── Post-reinstall recovery ─────────────────────────────────────────────── + + /** + * Asks the MediaScanner to re-index Documents/Nav/. When triggered by our + * app, the scanner re-registers each file with OWNER_PACKAGE_NAME set to + * our package, restoring ownership that Android clears on uninstall. + * After this returns, the MediaStore query in [loadAllTracks] will find + * the files again. + */ + suspend fun triggerNavDirRescan() { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) return + val navDir = File( + Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS), + "Nav" + ) + NavLogger.i("track", "triggerNavDirRescan: scanning ${navDir.absolutePath}") + withTimeoutOrNull(10_000L) { + suspendCancellableCoroutine<Unit> { cont -> + MediaScannerConnection.scanFile( + context, + arrayOf(navDir.absolutePath), + null + ) { path, uri -> + NavLogger.i("track", "triggerNavDirRescan: done path=$path uri=$uri") + if (cont.isActive) cont.resume(Unit) + } + } + } ?: NavLogger.e("track", "triggerNavDirRescan: timed out") + } } |
