SettingsActivity refactor

This commit is contained in:
Alexey 2025-11-07 12:50:34 +03:00
commit 2b821d8204
6 changed files with 117 additions and 96 deletions

4
TODO
View file

@ -1,8 +1,8 @@
functionality:
(done) sasplanet zipped sqlitedbs parser
refactor:
extract file opener from settings activity
extract zip parser from settings activity
(done) extract file opener from settings activity
(done) extract zip parser from settings activity
make use of Config.kt (maybe move consts here?)
fix:
app rotation with placed point

View file

@ -0,0 +1,31 @@
package com.mirenkov.ktheightmap
import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.result.ActivityResultLauncher
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import com.mirenkov.ktheightmap.FileLoader.Companion.filePickerResult
import com.nareshchocha.filepickerlibrary.FilePickerResultContracts
import com.nareshchocha.filepickerlibrary.models.DocumentFilePickerConfig
import com.nareshchocha.filepickerlibrary.models.FilePickerResult
class FileLoader {
companion object {
var filePath: MutableState<String?> = mutableStateOf(null)
fun filePickerResult(result: FilePickerResult): String? {
if (result.errorMessage != null) {
Log.e(TAG, result.errorMessage!!)
return null
} else {
val filePath = result.selectedFilePath
return filePath
}
}
}
}
fun ComponentActivity.registerFileLoader(): ActivityResultLauncher<DocumentFilePickerConfig?> {
return registerForActivityResult(FilePickerResultContracts.PickDocumentFile())
{ result -> FileLoader.filePath.value = filePickerResult(result) }
}

View file

@ -27,6 +27,7 @@ import androidx.compose.ui.text.drawText
import androidx.compose.ui.text.rememberTextMeasurer
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.withStyle
import com.mirenkov.ktheightmap.parser.KhmParser
import kotlin.math.absoluteValue
import kotlin.math.floor
import kotlin.math.pow

View file

@ -2,7 +2,6 @@ package com.mirenkov.ktheightmap
import android.app.Application
import android.os.Bundle
import android.util.Log
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
@ -14,50 +13,26 @@ import androidx.compose.foundation.layout.safeDrawingPadding
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.lifecycle.viewmodel.compose.LocalViewModelStoreOwner
import androidx.lifecycle.viewmodel.compose.viewModel
import com.mirenkov.ktheightmap.parser.KhmParser
import com.mirenkov.ktheightmap.parser.SasJPEGParser
import com.mirenkov.ktheightmap.parser.SasSQLiteParser
import com.mirenkov.ktheightmap.ui.theme.KtHeightMapTheme
import com.nareshchocha.filepickerlibrary.FilePickerResultContracts
import com.nareshchocha.filepickerlibrary.models.DocumentFilePickerConfig
import com.nareshchocha.filepickerlibrary.models.FilePickerResult
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch
import java.io.FileInputStream
import java.util.zip.ZipEntry
import java.util.zip.ZipInputStream
class SettingsActivity : ComponentActivity() {
companion object {
var filePath: MutableState<String?> = mutableStateOf(null)
}
fun filePickerResult(result: FilePickerResult): String? {
if (result.errorMessage != null) {
Log.e(TAG, result.errorMessage ?: "")
return null
} else {
val filePath = result.selectedFilePath
filePath?.let {
if (it.endsWith(".zip") || it.endsWith(".khm"))
return filePath
}
return null
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val launcher = registerForActivityResult(FilePickerResultContracts.PickDocumentFile())
{ result -> filePath.value = filePickerResult(result) }
val launcher = registerFileLoader()
enableEdgeToEdge()
setContent {
@ -77,79 +52,38 @@ class SettingsActivity : ComponentActivity() {
@Composable
fun SettingsMain(vm: TileViewModel, launcher: ActivityResultLauncher<DocumentFilePickerConfig?>) {
val coroutineScope = rememberCoroutineScope()
val filePath by remember { SettingsActivity.filePath }
val filePath by remember { FileLoader.filePath }
val ctx = LocalContext.current
val toast = Toast.makeText(ctx, "File parsed.", Toast.LENGTH_LONG)
KtHeightMapTheme {
Column(Modifier.fillMaxSize()
.safeDrawingPadding()) {
Button(
onClick = { launcher.launch(null) }
) { Text(text = "Select database") }
Button(
onClick = { coroutineScope.launch {
processZip(filePath).forEach {
with(vm.repository) {
val t = getTile(it.x, it.y, it.level)
if (t == null) {
pushTile(it)
}
}
}
} },
) { Text(text = "Load .zip") }
Button(
onClick = { coroutineScope.launch { vm.repository.clearTiles() } },
) { Text(text = "Clear database") }
Button(
onClick = { filePath?.let {
KhmParser.load(it, ctx)
} },
) { Text(text = "Load .khm") }
Button(
onClick = {
Button({ launcher.launch(null) }) { Text("Select file") }
Button({
filePath?.let {
coroutineScope.launch(Dispatchers.IO) {
SasSQLiteParser.processZip(it, vm.repository, ctx)
coroutineScope.launch(Dispatchers.Main) { Toast.makeText(ctx, "File parsed.", Toast.LENGTH_LONG).show() }
coroutineScope.launch(Dispatchers.Main) { toast.show() }
}
}
}
) { Text("Load SAS") }
}
}
}
suspend fun processZip(filePath: String?): List<Tile> = coroutineScope {
val list: MutableList<Tile> = mutableListOf()
} }
) { Text("Load SQLite") }
Button({
filePath?.let {
ZipInputStream(FileInputStream(it)).use { zipInputStream ->
var entry = zipInputStream.nextEntry
while (true) {
if (entry.name.endsWith(".jpg")) {
Log.i(TAG, entry.name)
val tile = processEntry(zipInputStream, entry)
list.add(tile)
}
entry = zipInputStream.nextEntry
if (entry == null)
break
coroutineScope.launch(Dispatchers.IO) {
SasJPEGParser.processZip(it, vm.repository)
coroutineScope.launch(Dispatchers.Main) { toast.show() }
}
}
},
) { Text("Load JPEG") }
Button({
filePath?.let {
KhmParser.load(it, ctx)
toast.show()
} },
) { Text("Load KHM") }
Button({ coroutineScope.launch { vm.repository.clearTiles() } }) { Text("Clear tiles") }
Button({ KhmParser.clear(ctx) }) { Text("Clear KHM") }
}
}
return@coroutineScope list
}
fun processEntry(zis: ZipInputStream, entry: ZipEntry): Tile {
val ba = ByteArray(entry.size.toInt())
var offset = 0
var size = zis.read(ba)
while (size > 0) {
offset += size
size = zis.read(ba, offset, ba.size - offset)
}
val regex = Regex("(?<=z)(\\d+)|(?<=x)(\\d+)|(?<=y)(\\d+)")
val ( level, x, y ) = regex.findAll(entry.name).map { it.value.toInt() }.toList()
val base64 = ba.toBase64()
return Tile(x, y, level, base64)
}

View file

@ -1,4 +1,4 @@
package com.mirenkov.ktheightmap
package com.mirenkov.ktheightmap.parser
import android.content.Context
import java.io.DataInputStream
@ -32,6 +32,12 @@ class KhmParser {
inp.close()
}
fun clear(ctx: Context) {
if (ctx.getFileStreamPath(HEIGHT_FILE).exists()) {
assert(ctx.deleteFile(HEIGHT_FILE))
}
}
private fun getOffset(header: HeightInfo, x: Int, y: Int): Int {
return (x * header.width + y) * 2
}

View file

@ -0,0 +1,49 @@
package com.mirenkov.ktheightmap.parser
import android.util.Log
import com.mirenkov.ktheightmap.TAG
import com.mirenkov.ktheightmap.Tile
import com.mirenkov.ktheightmap.TileRepository
import com.mirenkov.ktheightmap.toBase64
import java.io.FileInputStream
import java.util.zip.ZipEntry
import java.util.zip.ZipInputStream
class SasJPEGParser {
companion object {
fun processZip(filePath: String?, repo: TileRepository) {
filePath?.let {
ZipInputStream(FileInputStream(it)).use { zipInputStream ->
var entry = zipInputStream.nextEntry
while (true) {
if (entry.name.endsWith(".jpg")) {
Log.i(TAG, entry.name)
processEntry(zipInputStream, entry, repo)
}
entry = zipInputStream.nextEntry
if (entry == null)
break
}
}
}
}
private fun processEntry(zis: ZipInputStream, entry: ZipEntry, repo: TileRepository) {
val ba = ByteArray(entry.size.toInt())
var offset = 0
var size = zis.read(ba)
while (size > 0) {
offset += size
size = zis.read(ba, offset, ba.size - offset)
}
val regex = Regex("(?<=z)(\\d+)|(?<=x)(\\d+)|(?<=y)(\\d+)")
val ( level, x, y ) = regex.findAll(entry.name).map { it.value.toInt() }.toList()
val base64 = ba.toBase64()
val tile = Tile(x, y, level, base64)
val checkedTile = repo.getTile(tile.x, tile.y, tile.level)
if (checkedTile == null)
repo.pushTile(tile)
}
}
}