mirror of
https://github.com/nacabaro/vbhelper.git
synced 2026-01-28 00:15:32 +00:00
Data export working
During export the database will be closed, otherwise the WAL will ruin the export. data will be saved as a .vbhelper file, then the app will be closed to avoid making any changes to the DB For import, we check if the extension is .vbhelper, otherwise we can corrupt the database, IMPORTANT. Next we delete everything related to RoomDB and we swap it with the new files. Finally the app will be closed to ensure RoomDB is running on the new DB. Finally, I still have to reintegrate the importApk functionality with the NewSettingsScreenController and get rid of the old one.
This commit is contained in:
parent
88163684ca
commit
42dd87f0c4
@ -21,12 +21,12 @@ import com.github.nacabaro.vbhelper.di.VBHelper
|
||||
import com.github.nacabaro.vbhelper.domain.characters.Card
|
||||
import com.github.nacabaro.vbhelper.domain.Sprites
|
||||
import com.github.nacabaro.vbhelper.domain.characters.Character
|
||||
import com.github.nacabaro.vbhelper.domain.characters.Dex
|
||||
import com.github.nacabaro.vbhelper.domain.device_data.BECharacterData
|
||||
import com.github.nacabaro.vbhelper.domain.device_data.UserCharacter
|
||||
import com.github.nacabaro.vbhelper.navigation.AppNavigationHandlers
|
||||
import com.github.nacabaro.vbhelper.screens.scanScreen.ScanScreenControllerImpl
|
||||
import com.github.nacabaro.vbhelper.screens.SettingsScreenController
|
||||
import com.github.nacabaro.vbhelper.screens.settingsScreen.NewSettingsScreenControllerImpl
|
||||
import com.github.nacabaro.vbhelper.screens.settingsScreen.SettingsScreenController
|
||||
import com.github.nacabaro.vbhelper.source.ApkSecretsImporter
|
||||
import com.github.nacabaro.vbhelper.ui.theme.VBHelperTheme
|
||||
import com.github.nacabaro.vbhelper.utils.DeviceType
|
||||
@ -64,14 +64,15 @@ class MainActivity : ComponentActivity() {
|
||||
this::handleReceivedNfcCharacter,
|
||||
this,
|
||||
this::registerActivityLifecycleListener,
|
||||
this::unregisterActivityLifecycleListener)
|
||||
|
||||
this::unregisterActivityLifecycleListener
|
||||
)
|
||||
val newSettingsScreenController = NewSettingsScreenControllerImpl(this)
|
||||
|
||||
super.onCreate(savedInstanceState)
|
||||
enableEdgeToEdge()
|
||||
setContent {
|
||||
VBHelperTheme {
|
||||
MainApplication(settingsScreenController, scanScreenController)
|
||||
MainApplication(settingsScreenController, scanScreenController, newSettingsScreenController)
|
||||
}
|
||||
}
|
||||
Log.i("MainActivity", "Activity onCreated")
|
||||
@ -190,10 +191,18 @@ class MainActivity : ComponentActivity() {
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun MainApplication(settingsScreenController: SettingsScreenController, scanScreenController: ScanScreenControllerImpl) {
|
||||
private fun MainApplication(
|
||||
settingsScreenController: SettingsScreenController,
|
||||
scanScreenController: ScanScreenControllerImpl,
|
||||
newSettingsScreenController: NewSettingsScreenControllerImpl
|
||||
) {
|
||||
|
||||
AppNavigation(
|
||||
applicationNavigationHandlers = AppNavigationHandlers(settingsScreenController, scanScreenController),
|
||||
applicationNavigationHandlers = AppNavigationHandlers(
|
||||
settingsScreenController,
|
||||
scanScreenController,
|
||||
newSettingsScreenController
|
||||
),
|
||||
onClickImportCard = {
|
||||
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
|
||||
addCategory(Intent.CATEGORY_OPENABLE)
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
package com.github.nacabaro.vbhelper.navigation
|
||||
|
||||
import android.util.Log
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.runtime.Composable
|
||||
@ -15,12 +14,17 @@ import com.github.nacabaro.vbhelper.screens.homeScreens.HomeScreen
|
||||
import com.github.nacabaro.vbhelper.screens.ItemsScreen
|
||||
import com.github.nacabaro.vbhelper.screens.scanScreen.ScanScreen
|
||||
import com.github.nacabaro.vbhelper.screens.scanScreen.ScanScreenControllerImpl
|
||||
import com.github.nacabaro.vbhelper.screens.SettingsScreen
|
||||
import com.github.nacabaro.vbhelper.screens.SettingsScreenController
|
||||
import com.github.nacabaro.vbhelper.screens.settingsScreen.SettingsScreen
|
||||
import com.github.nacabaro.vbhelper.screens.settingsScreen.SettingsScreenController
|
||||
import com.github.nacabaro.vbhelper.screens.SpriteViewer
|
||||
import com.github.nacabaro.vbhelper.screens.StorageScreen
|
||||
import com.github.nacabaro.vbhelper.screens.settingsScreen.NewSettingsScreenControllerImpl
|
||||
|
||||
data class AppNavigationHandlers(val settingsScreenController: SettingsScreenController, val scanScreenController: ScanScreenControllerImpl)
|
||||
data class AppNavigationHandlers(
|
||||
val settingsScreenController: SettingsScreenController,
|
||||
val scanScreenController: ScanScreenControllerImpl,
|
||||
val newSettingsScreenController: NewSettingsScreenControllerImpl
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun AppNavigation(
|
||||
@ -72,7 +76,8 @@ fun AppNavigation(
|
||||
SettingsScreen(
|
||||
navController = navController,
|
||||
settingsScreenController = applicationNavigationHandlers.settingsScreenController,
|
||||
onClickImportCard = onClickImportCard
|
||||
newSettingsScreenController = applicationNavigationHandlers.newSettingsScreenController,
|
||||
onClickImportCard = onClickImportCard,
|
||||
)
|
||||
}
|
||||
composable(NavigationItems.Viewer.route) {
|
||||
|
||||
@ -0,0 +1,8 @@
|
||||
package com.github.nacabaro.vbhelper.screens.settingsScreen
|
||||
|
||||
import android.net.Uri
|
||||
|
||||
interface NewSettingsScreenController {
|
||||
fun onClickOpenDirectory()
|
||||
fun onClickImportDatabase()
|
||||
}
|
||||
@ -0,0 +1,152 @@
|
||||
package com.github.nacabaro.vbhelper.screens.settingsScreen
|
||||
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import kotlinx.coroutines.launch
|
||||
import android.net.Uri
|
||||
import android.provider.OpenableColumns
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import com.github.nacabaro.vbhelper.di.VBHelper
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import java.io.File
|
||||
import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
|
||||
|
||||
class NewSettingsScreenControllerImpl(
|
||||
private val context: ComponentActivity,
|
||||
): NewSettingsScreenController {
|
||||
private val filePickerLauncher: ActivityResultLauncher<String>
|
||||
private val filePickerOpenerLauncher: ActivityResultLauncher<Array<String>>
|
||||
|
||||
init {
|
||||
filePickerLauncher = context.registerForActivityResult(
|
||||
ActivityResultContracts.CreateDocument("application/octet-stream")
|
||||
) { uri ->
|
||||
if (uri != null) {
|
||||
exportDatabase("internalDb", uri)
|
||||
} else {
|
||||
context.runOnUiThread {
|
||||
Toast.makeText(context, "No destination selected", Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
filePickerOpenerLauncher = context.registerForActivityResult(
|
||||
ActivityResultContracts.OpenDocument()
|
||||
) { uri ->
|
||||
if (uri != null) {
|
||||
importDatabase("internalDb", uri)
|
||||
} else {
|
||||
context.runOnUiThread {
|
||||
Toast.makeText(context, "No source selected", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onClickOpenDirectory() {
|
||||
filePickerLauncher.launch("My application data.vbhelper")
|
||||
}
|
||||
|
||||
override fun onClickImportDatabase() {
|
||||
filePickerOpenerLauncher.launch(arrayOf("application/octet-stream"))
|
||||
}
|
||||
|
||||
private fun exportDatabase(roomDbName: String, destinationUri: Uri) {
|
||||
context.lifecycleScope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
val application = context.applicationContext as VBHelper
|
||||
val dbFile = File(context.getDatabasePath(roomDbName).absolutePath)
|
||||
if (!dbFile.exists()) {
|
||||
throw IllegalStateException("Database file does not exist!")
|
||||
}
|
||||
|
||||
application.container.db.close()
|
||||
|
||||
context.contentResolver.openOutputStream(destinationUri)?.use { outputStream ->
|
||||
dbFile.inputStream().use { inputStream ->
|
||||
copyFile(inputStream, outputStream)
|
||||
}
|
||||
} ?: throw IllegalArgumentException("Unable to open destination Uri for writing")
|
||||
|
||||
context.runOnUiThread {
|
||||
Toast.makeText(context, "Database exported successfully!", Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(context, "Closing application to avoid changes.", Toast.LENGTH_LONG).show()
|
||||
context.finishAffinity()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e("ScanScreenController", "Error exporting database $e")
|
||||
context.runOnUiThread {
|
||||
Toast.makeText(context, "Error exporting database: ${e.message}", Toast.LENGTH_LONG).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun importDatabase(roomDbName: String, sourceUri: Uri) {
|
||||
context.lifecycleScope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
if (!getFileNameFromUri(sourceUri)!!.endsWith(".vbhelper")) {
|
||||
context.runOnUiThread {
|
||||
Toast.makeText(context, "Invalid file format", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
return@launch
|
||||
}
|
||||
|
||||
val dbPath = context.getDatabasePath(roomDbName)
|
||||
val shmFile = File(dbPath.parent, "$roomDbName-shm")
|
||||
val walFile = File(dbPath.parent, "$roomDbName-wal")
|
||||
|
||||
// Delete existing database files
|
||||
if (dbPath.exists()) dbPath.delete()
|
||||
if (shmFile.exists()) shmFile.delete()
|
||||
if (walFile.exists()) walFile.delete()
|
||||
|
||||
val dbFile = File(dbPath.absolutePath)
|
||||
|
||||
context.contentResolver.openInputStream(sourceUri)?.use { inputStream ->
|
||||
dbFile.outputStream().use { outputStream ->
|
||||
copyFile(inputStream, outputStream)
|
||||
}
|
||||
} ?: throw IllegalArgumentException("Unable to open source Uri for reading")
|
||||
|
||||
context.runOnUiThread {
|
||||
Toast.makeText(context, "Database imported successfully!", Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(context, "Reopen the app to finish import process!", Toast.LENGTH_LONG).show()
|
||||
context.finishAffinity()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e("ScanScreenController", "Error importing database $e")
|
||||
context.runOnUiThread {
|
||||
Toast.makeText(context, "Error importing database: ${e.message}", Toast.LENGTH_LONG).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getFileNameFromUri(uri: Uri): String? {
|
||||
var fileName: String? = null
|
||||
val cursor = context.contentResolver.query(uri, null, null, null, null)
|
||||
cursor?.use {
|
||||
if (it.moveToFirst()) {
|
||||
val nameIndex = it.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME)
|
||||
fileName = it.getString(nameIndex)
|
||||
}
|
||||
}
|
||||
return fileName
|
||||
}
|
||||
|
||||
private fun copyFile(inputStream: InputStream, outputStream: OutputStream) {
|
||||
val buffer = ByteArray(1024)
|
||||
var bytesRead: Int
|
||||
while (inputStream.read(buffer).also { bytesRead = it } != -1) {
|
||||
outputStream.write(buffer, 0, bytesRead)
|
||||
}
|
||||
outputStream.flush()
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
package com.github.nacabaro.vbhelper.screens
|
||||
package com.github.nacabaro.vbhelper.screens.settingsScreen
|
||||
|
||||
import android.net.Uri
|
||||
import androidx.activity.ComponentActivity
|
||||
@ -22,11 +22,14 @@ import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.navigation.NavController
|
||||
import com.github.nacabaro.vbhelper.components.TopBanner
|
||||
import com.github.nacabaro.vbhelper.navigation.AppNavigation
|
||||
import com.github.nacabaro.vbhelper.navigation.NavigationItems
|
||||
|
||||
@Composable
|
||||
fun SettingsScreen(
|
||||
navController: NavController,
|
||||
settingsScreenController: SettingsScreenController,
|
||||
newSettingsScreenController: NewSettingsScreenControllerImpl,
|
||||
onClickImportCard: () -> Unit
|
||||
) {
|
||||
Scaffold (
|
||||
@ -51,6 +54,13 @@ fun SettingsScreen(
|
||||
SettingsEntry(title = "Import APK", description = "Import Secrets From Vital Arean 2.1.0 APK") {
|
||||
settingsScreenController.apkFilePickLauncher.launch(arrayOf("*/*"))
|
||||
}
|
||||
SettingsSection("Data management")
|
||||
SettingsEntry(title = "Export data", description = "Export application database") {
|
||||
newSettingsScreenController.onClickOpenDirectory()
|
||||
}
|
||||
SettingsEntry(title = "Import data", description = "Import application database") {
|
||||
newSettingsScreenController.onClickImportDatabase()
|
||||
}
|
||||
SettingsSection("DiM/BEm management")
|
||||
SettingsEntry(title = "Import DiM card", description = "Import DiM/BEm card file", onClick = onClickImportCard)
|
||||
SettingsEntry(title = "Rename DiM/BEm", description = "Set card name") { }
|
||||
@ -1,4 +1,4 @@
|
||||
package com.github.nacabaro.vbhelper.screens
|
||||
package com.github.nacabaro.vbhelper.screens.settingsScreen
|
||||
|
||||
import android.net.Uri
|
||||
import android.widget.Toast
|
||||
Loading…
x
Reference in New Issue
Block a user