SettingsActivity refactor
This commit is contained in:
parent
516455209e
commit
2b821d8204
6 changed files with 117 additions and 96 deletions
4
TODO
4
TODO
|
|
@ -1,8 +1,8 @@
|
||||||
functionality:
|
functionality:
|
||||||
(done) sasplanet zipped sqlitedbs parser
|
(done) sasplanet zipped sqlitedbs parser
|
||||||
refactor:
|
refactor:
|
||||||
extract file opener from settings activity
|
(done) extract file opener from settings activity
|
||||||
extract zip parser from settings activity
|
(done) extract zip parser from settings activity
|
||||||
make use of Config.kt (maybe move consts here?)
|
make use of Config.kt (maybe move consts here?)
|
||||||
fix:
|
fix:
|
||||||
app rotation with placed point
|
app rotation with placed point
|
||||||
|
|
|
||||||
31
app/src/main/java/com/mirenkov/ktheightmap/FileLoader.kt
Normal file
31
app/src/main/java/com/mirenkov/ktheightmap/FileLoader.kt
Normal 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) }
|
||||||
|
}
|
||||||
|
|
@ -27,6 +27,7 @@ import androidx.compose.ui.text.drawText
|
||||||
import androidx.compose.ui.text.rememberTextMeasurer
|
import androidx.compose.ui.text.rememberTextMeasurer
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.text.withStyle
|
import androidx.compose.ui.text.withStyle
|
||||||
|
import com.mirenkov.ktheightmap.parser.KhmParser
|
||||||
import kotlin.math.absoluteValue
|
import kotlin.math.absoluteValue
|
||||||
import kotlin.math.floor
|
import kotlin.math.floor
|
||||||
import kotlin.math.pow
|
import kotlin.math.pow
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ package com.mirenkov.ktheightmap
|
||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.Log
|
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
|
|
@ -14,50 +13,26 @@ import androidx.compose.foundation.layout.safeDrawingPadding
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.MutableState
|
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.lifecycle.viewmodel.compose.LocalViewModelStoreOwner
|
import androidx.lifecycle.viewmodel.compose.LocalViewModelStoreOwner
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
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.parser.SasSQLiteParser
|
||||||
import com.mirenkov.ktheightmap.ui.theme.KtHeightMapTheme
|
import com.mirenkov.ktheightmap.ui.theme.KtHeightMapTheme
|
||||||
import com.nareshchocha.filepickerlibrary.FilePickerResultContracts
|
|
||||||
import com.nareshchocha.filepickerlibrary.models.DocumentFilePickerConfig
|
import com.nareshchocha.filepickerlibrary.models.DocumentFilePickerConfig
|
||||||
import com.nareshchocha.filepickerlibrary.models.FilePickerResult
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.coroutineScope
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import java.io.FileInputStream
|
|
||||||
import java.util.zip.ZipEntry
|
|
||||||
import java.util.zip.ZipInputStream
|
|
||||||
|
|
||||||
class SettingsActivity : ComponentActivity() {
|
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?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
val launcher = registerForActivityResult(FilePickerResultContracts.PickDocumentFile())
|
|
||||||
{ result -> filePath.value = filePickerResult(result) }
|
val launcher = registerFileLoader()
|
||||||
|
|
||||||
enableEdgeToEdge()
|
enableEdgeToEdge()
|
||||||
setContent {
|
setContent {
|
||||||
|
|
@ -77,79 +52,38 @@ class SettingsActivity : ComponentActivity() {
|
||||||
@Composable
|
@Composable
|
||||||
fun SettingsMain(vm: TileViewModel, launcher: ActivityResultLauncher<DocumentFilePickerConfig?>) {
|
fun SettingsMain(vm: TileViewModel, launcher: ActivityResultLauncher<DocumentFilePickerConfig?>) {
|
||||||
val coroutineScope = rememberCoroutineScope()
|
val coroutineScope = rememberCoroutineScope()
|
||||||
val filePath by remember { SettingsActivity.filePath }
|
val filePath by remember { FileLoader.filePath }
|
||||||
val ctx = LocalContext.current
|
val ctx = LocalContext.current
|
||||||
|
val toast = Toast.makeText(ctx, "File parsed.", Toast.LENGTH_LONG)
|
||||||
KtHeightMapTheme {
|
KtHeightMapTheme {
|
||||||
Column(Modifier.fillMaxSize()
|
Column(Modifier.fillMaxSize()
|
||||||
.safeDrawingPadding()) {
|
.safeDrawingPadding()) {
|
||||||
Button(
|
Button({ launcher.launch(null) }) { Text("Select file") }
|
||||||
onClick = { launcher.launch(null) }
|
Button({
|
||||||
) { 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 = {
|
|
||||||
filePath?.let {
|
filePath?.let {
|
||||||
coroutineScope.launch(Dispatchers.IO) {
|
coroutineScope.launch(Dispatchers.IO) {
|
||||||
SasSQLiteParser.processZip(it, vm.repository, ctx)
|
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 SQLite") }
|
||||||
) { Text("Load SAS") }
|
Button({
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun processZip(filePath: String?): List<Tile> = coroutineScope {
|
|
||||||
val list: MutableList<Tile> = mutableListOf()
|
|
||||||
filePath?.let {
|
filePath?.let {
|
||||||
ZipInputStream(FileInputStream(it)).use { zipInputStream ->
|
coroutineScope.launch(Dispatchers.IO) {
|
||||||
var entry = zipInputStream.nextEntry
|
SasJPEGParser.processZip(it, vm.repository)
|
||||||
while (true) {
|
coroutineScope.launch(Dispatchers.Main) { toast.show() }
|
||||||
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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
) { 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)
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package com.mirenkov.ktheightmap
|
package com.mirenkov.ktheightmap.parser
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import java.io.DataInputStream
|
import java.io.DataInputStream
|
||||||
|
|
@ -32,6 +32,12 @@ class KhmParser {
|
||||||
inp.close()
|
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 {
|
private fun getOffset(header: HeightInfo, x: Int, y: Int): Int {
|
||||||
return (x * header.width + y) * 2
|
return (x * header.width + y) * 2
|
||||||
}
|
}
|
||||||
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue