summaryrefslogtreecommitdiff
path: root/android-app/app/src
diff options
context:
space:
mode:
Diffstat (limited to 'android-app/app/src')
-rw-r--r--android-app/app/src/main/kotlin/org/terst/nav/MainActivity.kt2
-rw-r--r--android-app/app/src/main/kotlin/org/terst/nav/track/TrackRepository.kt15
-rw-r--r--android-app/app/src/main/kotlin/org/terst/nav/track/TrackStorage.kt33
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")
+ }
}