diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 497108e..5d1bee1 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -50,6 +50,8 @@ dependencies { implementation("io.github.chochanaresh:filepicker:0.6.0") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.10.2") + implementation(libs.androidx.core.ktx) implementation(libs.androidx.lifecycle.runtime.ktx) implementation(libs.androidx.activity.compose) diff --git a/app/src/main/java/com/mirenkov/ktheightmap/MainActivity.kt b/app/src/main/java/com/mirenkov/ktheightmap/MainActivity.kt index eaf5142..639fdd8 100644 --- a/app/src/main/java/com/mirenkov/ktheightmap/MainActivity.kt +++ b/app/src/main/java/com/mirenkov/ktheightmap/MainActivity.kt @@ -40,10 +40,14 @@ import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewmodel.compose.LocalViewModelStoreOwner import androidx.lifecycle.viewmodel.compose.viewModel import com.mirenkov.ktheightmap.ui.theme.KtHeightMapTheme +import java.util.Base64 const val TAG = "KtHeightMap" const val TILE_SIZE: Float = 256F +fun ByteArray.toBase64(): String = + String(Base64.getEncoder().encode(this)) + class TileViewModelFactory(val application: Application): ViewModelProvider.Factory { override fun create(modelClass: Class): T { @@ -58,7 +62,6 @@ class MainActivity : ComponentActivity() { enableEdgeToEdge() setContent { val owner = LocalViewModelStoreOwner.current - owner?.let { val viewModel: TileViewModel = viewModel( it, diff --git a/app/src/main/java/com/mirenkov/ktheightmap/SettingsActivity.kt b/app/src/main/java/com/mirenkov/ktheightmap/SettingsActivity.kt index 4728b6c..df37348 100644 --- a/app/src/main/java/com/mirenkov/ktheightmap/SettingsActivity.kt +++ b/app/src/main/java/com/mirenkov/ktheightmap/SettingsActivity.kt @@ -1,65 +1,97 @@ package com.mirenkov.ktheightmap +import android.graphics.BitmapFactory import android.os.Bundle import android.util.Log import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge -import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.safeDrawingPadding import androidx.compose.material3.Button import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier import com.mirenkov.ktheightmap.ui.theme.KtHeightMapTheme import com.nareshchocha.filepickerlibrary.FilePickerResultContracts -import com.nareshchocha.filepickerlibrary.models.PickerData -import com.nareshchocha.filepickerlibrary.models.PopUpConfig +import com.nareshchocha.filepickerlibrary.models.FilePickerResult +import kotlinx.coroutines.CoroutineScope +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() { - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - val launcher = registerForActivityResult(FilePickerResultContracts.PickDocumentFile()) { result -> - if (result.errorMessage != null) { - Log.e("Picker", result.errorMessage ?: "") - } else { - val filePath = result.selectedFilePath - filePath?.let { - if (!it.endsWith(".zip")) - return@let - ZipInputStream(FileInputStream(it)).use { zipInputStream -> - var entry = zipInputStream.nextEntry - while (entry != null) { - with(entry.name) { - if (count { it == '/' } == 4 && endsWith(".jpg")) - processEntry(entry) - } - entry = zipInputStream.nextEntry - } - } - } + + var filePath: String? = 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")) + return filePath } + return null } + } + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + val launcher = registerForActivityResult(FilePickerResultContracts.PickDocumentFile(), + { result -> filePath = filePickerResult(result) } + ) + enableEdgeToEdge() setContent { + val coroutineScope = rememberCoroutineScope() + KtHeightMapTheme { Column(Modifier.fillMaxSize() .safeDrawingPadding()) { - Button(onClick = { - launcher.launch(null) - }) { Text(text = "Load database") } + Button(onClick = { + launcher.launch(null) + }) { Text(text = "Select database") } + Button(onClick = { coroutineScope.launch { processZip(filePath) } }) { + Text(text = "Load database") + } } } } } } -fun processEntry(entry: ZipEntry) { - Log.i(TAG, entry.name) +suspend fun processZip(filePath: String?) = coroutineScope { + 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) + } + entry = zipInputStream.nextEntry + if (entry == null) + break + } + } + } +} + +fun processEntry(zis: ZipInputStream, entry: ZipEntry) { + val ba = ByteArray(entry.size.toInt()) + var offset = 0 + var size = zis.read(ba, 0, ba.size) + 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) } \ No newline at end of file