Android: always refresh seed DB from assets when size differs
Old logic: copy only if file doesn't exist. App updates with newer seed DB were ignored because the old file was already there. Now compares file size with asset size and overwrites on mismatch.
This commit is contained in:
parent
049f4d6546
commit
c5bf6da79e
1 changed files with 151 additions and 0 deletions
|
|
@ -0,0 +1,151 @@
|
|||
package dev.baldav.app
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.FileObserver
|
||||
import android.util.Log
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import java.io.File
|
||||
|
||||
class MainActivity : TauriActivity() {
|
||||
private var commandObserver: FileObserver? = null
|
||||
private var pendingSaveContent: String? = null
|
||||
|
||||
private val saveFileLauncher = registerForActivityResult(
|
||||
ActivityResultContracts.CreateDocument("application/gpx+xml")
|
||||
) { uri: Uri? ->
|
||||
if (uri != null && pendingSaveContent != null) {
|
||||
try {
|
||||
contentResolver.openOutputStream(uri)?.use { it.write(pendingSaveContent!!.toByteArray()) }
|
||||
Log.d("BalDav", "File saved to $uri")
|
||||
} catch (e: Exception) {
|
||||
Log.e("BalDav", "Failed to save file", e)
|
||||
}
|
||||
}
|
||||
pendingSaveContent = null
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
enableEdgeToEdge()
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
// Rust app_data_dir() on Android = app's root data dir (filesDir.parentFile)
|
||||
val appDataDir = filesDir.parentFile ?: filesDir
|
||||
Log.d("BalDav", "App data dir: ${appDataDir.absolutePath}")
|
||||
|
||||
// Create command file if not exists (FileObserver needs it)
|
||||
val cmdFile = File(appDataDir, "gps_command.txt")
|
||||
if (!cmdFile.exists()) cmdFile.createNewFile()
|
||||
|
||||
setupCommandWatcher(appDataDir)
|
||||
processGpsCommand(appDataDir)
|
||||
Log.d("BalDav", "Command watcher started on ${appDataDir.absolutePath}")
|
||||
|
||||
// Copy seed DB from assets to filesDir (Rust can't read Android assets directly).
|
||||
// Always overwrite — APK updates may contain a newer seed DB.
|
||||
try {
|
||||
val seedDest = File(filesDir, "products_seed.db")
|
||||
val assetSize = assets.open("frontend/data/products_seed.db").use { it.available().toLong() }
|
||||
if (!seedDest.exists() || seedDest.length() != assetSize) {
|
||||
assets.open("frontend/data/products_seed.db").use { input ->
|
||||
seedDest.outputStream().use { output ->
|
||||
input.copyTo(output)
|
||||
}
|
||||
}
|
||||
Log.d("BalDav", "Seed DB copied to ${seedDest.absolutePath} (${seedDest.length()} bytes)")
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e("BalDav", "Failed to copy seed DB", e)
|
||||
}
|
||||
|
||||
val decorView = window.decorView
|
||||
ViewCompat.setOnApplyWindowInsetsListener(decorView) { view, insets ->
|
||||
val statusBar = insets.getInsets(WindowInsetsCompat.Type.statusBars())
|
||||
val navBar = insets.getInsets(WindowInsetsCompat.Type.navigationBars())
|
||||
val density = resources.displayMetrics.density
|
||||
val top = (statusBar.top / density).toInt()
|
||||
val bottom = (navBar.bottom / density).toInt()
|
||||
val left = (navBar.left / density).toInt()
|
||||
val right = (navBar.right / density).toInt()
|
||||
try {
|
||||
val insetsFile = File(filesDir, "safe_insets.json")
|
||||
insetsFile.writeText("""{"top":$top,"bottom":$bottom,"left":$left,"right":$right}""")
|
||||
Log.d("BalDav", "Safe insets: top=$top bottom=$bottom left=$left right=$right")
|
||||
} catch (e: Exception) {
|
||||
Log.e("BalDav", "Failed to write safe insets", e)
|
||||
}
|
||||
ViewCompat.onApplyWindowInsets(view, insets)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupCommandWatcher(appDataDir: File) {
|
||||
commandObserver = object : FileObserver(appDataDir.absolutePath, CREATE or CLOSE_WRITE or MODIFY or MOVED_TO) {
|
||||
override fun onEvent(event: Int, path: String?) {
|
||||
if (path == "gps_command.txt") {
|
||||
Log.d("BalDav", "Command file event: $event")
|
||||
runOnUiThread { processGpsCommand(appDataDir) }
|
||||
}
|
||||
}
|
||||
}
|
||||
commandObserver?.startWatching()
|
||||
Log.d("BalDav", "FileObserver started on ${appDataDir.absolutePath}")
|
||||
}
|
||||
|
||||
private fun processGpsCommand(appDataDir: File) {
|
||||
val cmdFile = File(appDataDir, "gps_command.txt")
|
||||
if (!cmdFile.exists()) return
|
||||
val cmd = cmdFile.readText().trim()
|
||||
if (cmd.isEmpty()) return
|
||||
cmdFile.writeText("") // Clear but don't delete (FileObserver needs the file)
|
||||
|
||||
Log.d("BalDav", "Processing GPS command: $cmd")
|
||||
|
||||
when {
|
||||
cmd.startsWith("start") -> {
|
||||
val parts = cmd.split(":")
|
||||
val minSpeed = parts.getOrNull(1)?.toFloatOrNull() ?: 0.42f
|
||||
val maxAcc = parts.getOrNull(2)?.toFloatOrNull() ?: 20f
|
||||
val minDist = parts.getOrNull(3)?.toFloatOrNull() ?: 1f
|
||||
Log.d("BalDav", "Starting GPS service: speed=$minSpeed acc=$maxAcc dist=$minDist")
|
||||
val intent = Intent(this, GpsRecorderService::class.java).apply {
|
||||
putExtra(GpsRecorderService.EXTRA_MIN_SPEED, minSpeed)
|
||||
putExtra(GpsRecorderService.EXTRA_MAX_ACC, maxAcc)
|
||||
putExtra(GpsRecorderService.EXTRA_MIN_DIST, minDist)
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
startForegroundService(intent)
|
||||
} else {
|
||||
startService(intent)
|
||||
}
|
||||
}
|
||||
cmd == "stop" -> {
|
||||
Log.d("BalDav", "Stopping GPS service")
|
||||
val intent = Intent(this, GpsRecorderService::class.java)
|
||||
intent.action = GpsRecorderService.ACTION_STOP
|
||||
startService(intent)
|
||||
}
|
||||
cmd.startsWith("save:") -> {
|
||||
// save:filename.gpx — read content from save_content.txt, show SAF picker
|
||||
val filename = cmd.substringAfter("save:")
|
||||
val contentFile = File(appDataDir, "save_content.txt")
|
||||
if (contentFile.exists()) {
|
||||
pendingSaveContent = contentFile.readText()
|
||||
contentFile.delete()
|
||||
Log.d("BalDav", "Opening save dialog for $filename (${pendingSaveContent?.length} bytes)")
|
||||
saveFileLauncher.launch(filename)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
commandObserver?.stopWatching()
|
||||
super.onDestroy()
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue