mirror of
https://github.com/nacabaro/vbhelper.git
synced 2026-01-28 00:15:32 +00:00
Finished refactoring the old SettingsScreenController.kt
This commit is contained in:
parent
542072c238
commit
23e233227a
@ -25,9 +25,7 @@ import com.github.nacabaro.vbhelper.domain.device_data.BECharacterData
|
|||||||
import com.github.nacabaro.vbhelper.domain.device_data.UserCharacter
|
import com.github.nacabaro.vbhelper.domain.device_data.UserCharacter
|
||||||
import com.github.nacabaro.vbhelper.navigation.AppNavigationHandlers
|
import com.github.nacabaro.vbhelper.navigation.AppNavigationHandlers
|
||||||
import com.github.nacabaro.vbhelper.screens.scanScreen.ScanScreenControllerImpl
|
import com.github.nacabaro.vbhelper.screens.scanScreen.ScanScreenControllerImpl
|
||||||
import com.github.nacabaro.vbhelper.screens.settingsScreen.NewSettingsScreenControllerImpl
|
import com.github.nacabaro.vbhelper.screens.settingsScreen.SettingsScreenControllerImpl
|
||||||
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.ui.theme.VBHelperTheme
|
||||||
import com.github.nacabaro.vbhelper.utils.DeviceType
|
import com.github.nacabaro.vbhelper.utils.DeviceType
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
@ -57,8 +55,6 @@ class MainActivity : ComponentActivity() {
|
|||||||
registerFileActivityResult()
|
registerFileActivityResult()
|
||||||
|
|
||||||
val application = applicationContext as VBHelper
|
val application = applicationContext as VBHelper
|
||||||
val settingsScreenController = SettingsScreenController.Factory(this, ApkSecretsImporter(), application.container.dataStoreSecretsRepository)
|
|
||||||
.buildSettingScreenHandlers()
|
|
||||||
val scanScreenController = ScanScreenControllerImpl(
|
val scanScreenController = ScanScreenControllerImpl(
|
||||||
application.container.dataStoreSecretsRepository.secretsFlow,
|
application.container.dataStoreSecretsRepository.secretsFlow,
|
||||||
this::handleReceivedNfcCharacter,
|
this::handleReceivedNfcCharacter,
|
||||||
@ -66,13 +62,13 @@ class MainActivity : ComponentActivity() {
|
|||||||
this::registerActivityLifecycleListener,
|
this::registerActivityLifecycleListener,
|
||||||
this::unregisterActivityLifecycleListener
|
this::unregisterActivityLifecycleListener
|
||||||
)
|
)
|
||||||
val newSettingsScreenController = NewSettingsScreenControllerImpl(this)
|
val settingsScreenController = SettingsScreenControllerImpl(this)
|
||||||
|
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
enableEdgeToEdge()
|
enableEdgeToEdge()
|
||||||
setContent {
|
setContent {
|
||||||
VBHelperTheme {
|
VBHelperTheme {
|
||||||
MainApplication(settingsScreenController, scanScreenController, newSettingsScreenController)
|
MainApplication(scanScreenController, settingsScreenController)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Log.i("MainActivity", "Activity onCreated")
|
Log.i("MainActivity", "Activity onCreated")
|
||||||
@ -192,16 +188,14 @@ class MainActivity : ComponentActivity() {
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun MainApplication(
|
private fun MainApplication(
|
||||||
settingsScreenController: SettingsScreenController,
|
|
||||||
scanScreenController: ScanScreenControllerImpl,
|
scanScreenController: ScanScreenControllerImpl,
|
||||||
newSettingsScreenController: NewSettingsScreenControllerImpl
|
settingsScreenController: SettingsScreenControllerImpl
|
||||||
) {
|
) {
|
||||||
|
|
||||||
AppNavigation(
|
AppNavigation(
|
||||||
applicationNavigationHandlers = AppNavigationHandlers(
|
applicationNavigationHandlers = AppNavigationHandlers(
|
||||||
settingsScreenController,
|
settingsScreenController,
|
||||||
scanScreenController,
|
scanScreenController,
|
||||||
newSettingsScreenController
|
|
||||||
),
|
),
|
||||||
onClickImportCard = {
|
onClickImportCard = {
|
||||||
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
|
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
|
||||||
|
|||||||
@ -15,15 +15,13 @@ import com.github.nacabaro.vbhelper.screens.ItemsScreen
|
|||||||
import com.github.nacabaro.vbhelper.screens.scanScreen.ScanScreen
|
import com.github.nacabaro.vbhelper.screens.scanScreen.ScanScreen
|
||||||
import com.github.nacabaro.vbhelper.screens.scanScreen.ScanScreenControllerImpl
|
import com.github.nacabaro.vbhelper.screens.scanScreen.ScanScreenControllerImpl
|
||||||
import com.github.nacabaro.vbhelper.screens.settingsScreen.SettingsScreen
|
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.SpriteViewer
|
||||||
import com.github.nacabaro.vbhelper.screens.StorageScreen
|
import com.github.nacabaro.vbhelper.screens.StorageScreen
|
||||||
import com.github.nacabaro.vbhelper.screens.settingsScreen.NewSettingsScreenControllerImpl
|
import com.github.nacabaro.vbhelper.screens.settingsScreen.SettingsScreenControllerImpl
|
||||||
|
|
||||||
data class AppNavigationHandlers(
|
data class AppNavigationHandlers(
|
||||||
val settingsScreenController: SettingsScreenController,
|
val settingsScreenController: SettingsScreenControllerImpl,
|
||||||
val scanScreenController: ScanScreenControllerImpl,
|
val scanScreenController: ScanScreenControllerImpl,
|
||||||
val newSettingsScreenController: NewSettingsScreenControllerImpl
|
|
||||||
)
|
)
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@ -76,7 +74,6 @@ fun AppNavigation(
|
|||||||
SettingsScreen(
|
SettingsScreen(
|
||||||
navController = navController,
|
navController = navController,
|
||||||
settingsScreenController = applicationNavigationHandlers.settingsScreenController,
|
settingsScreenController = applicationNavigationHandlers.settingsScreenController,
|
||||||
newSettingsScreenController = applicationNavigationHandlers.newSettingsScreenController,
|
|
||||||
onClickImportCard = onClickImportCard,
|
onClickImportCard = onClickImportCard,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,8 +0,0 @@
|
|||||||
package com.github.nacabaro.vbhelper.screens.settingsScreen
|
|
||||||
|
|
||||||
import android.net.Uri
|
|
||||||
|
|
||||||
interface NewSettingsScreenController {
|
|
||||||
fun onClickOpenDirectory()
|
|
||||||
fun onClickImportDatabase()
|
|
||||||
}
|
|
||||||
@ -1,9 +1,5 @@
|
|||||||
package com.github.nacabaro.vbhelper.screens.settingsScreen
|
package com.github.nacabaro.vbhelper.screens.settingsScreen
|
||||||
|
|
||||||
import android.net.Uri
|
|
||||||
import androidx.activity.ComponentActivity
|
|
||||||
import androidx.activity.result.ActivityResultLauncher
|
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
@ -22,14 +18,11 @@ import androidx.compose.ui.unit.dp
|
|||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
import com.github.nacabaro.vbhelper.components.TopBanner
|
import com.github.nacabaro.vbhelper.components.TopBanner
|
||||||
import com.github.nacabaro.vbhelper.navigation.AppNavigation
|
|
||||||
import com.github.nacabaro.vbhelper.navigation.NavigationItems
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun SettingsScreen(
|
fun SettingsScreen(
|
||||||
navController: NavController,
|
navController: NavController,
|
||||||
settingsScreenController: SettingsScreenController,
|
settingsScreenController: SettingsScreenControllerImpl,
|
||||||
newSettingsScreenController: NewSettingsScreenControllerImpl,
|
|
||||||
onClickImportCard: () -> Unit
|
onClickImportCard: () -> Unit
|
||||||
) {
|
) {
|
||||||
Scaffold (
|
Scaffold (
|
||||||
@ -52,14 +45,14 @@ fun SettingsScreen(
|
|||||||
) {
|
) {
|
||||||
SettingsSection("NFC Communication")
|
SettingsSection("NFC Communication")
|
||||||
SettingsEntry(title = "Import APK", description = "Import Secrets From Vital Arean 2.1.0 APK") {
|
SettingsEntry(title = "Import APK", description = "Import Secrets From Vital Arean 2.1.0 APK") {
|
||||||
settingsScreenController.apkFilePickLauncher.launch(arrayOf("*/*"))
|
settingsScreenController.onClickImportApk()
|
||||||
}
|
}
|
||||||
SettingsSection("Data management")
|
SettingsSection("Data management")
|
||||||
SettingsEntry(title = "Export data", description = "Export application database") {
|
SettingsEntry(title = "Export data", description = "Export application database") {
|
||||||
newSettingsScreenController.onClickOpenDirectory()
|
settingsScreenController.onClickOpenDirectory()
|
||||||
}
|
}
|
||||||
SettingsEntry(title = "Import data", description = "Import application database") {
|
SettingsEntry(title = "Import data", description = "Import application database") {
|
||||||
newSettingsScreenController.onClickImportDatabase()
|
settingsScreenController.onClickImportDatabase()
|
||||||
}
|
}
|
||||||
SettingsSection("DiM/BEm management")
|
SettingsSection("DiM/BEm management")
|
||||||
SettingsEntry(title = "Import DiM card", description = "Import DiM/BEm card file", onClick = onClickImportCard)
|
SettingsEntry(title = "Import DiM card", description = "Import DiM/BEm card file", onClick = onClickImportCard)
|
||||||
@ -71,12 +64,6 @@ fun SettingsScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun buildFilePickLauncher(activity: ComponentActivity, onItemPicked: (Uri?) -> Unit): ActivityResultLauncher<Array<String>> {
|
|
||||||
return activity.registerForActivityResult(ActivityResultContracts.OpenDocument()) {
|
|
||||||
onItemPicked.invoke(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun SettingsEntry(
|
fun SettingsEntry(
|
||||||
title: String,
|
title: String,
|
||||||
|
|||||||
@ -1,71 +0,0 @@
|
|||||||
package com.github.nacabaro.vbhelper.screens.settingsScreen
|
|
||||||
|
|
||||||
import android.net.Uri
|
|
||||||
import android.widget.Toast
|
|
||||||
import androidx.activity.ComponentActivity
|
|
||||||
import androidx.activity.result.ActivityResultLauncher
|
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
|
||||||
import androidx.lifecycle.lifecycleScope
|
|
||||||
import com.github.nacabaro.vbhelper.source.SecretsImporter
|
|
||||||
import com.github.nacabaro.vbhelper.source.SecretsRepository
|
|
||||||
import com.github.nacabaro.vbhelper.source.proto.Secrets
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
|
|
||||||
data class SettingsScreenController(val apkFilePickLauncher: ActivityResultLauncher<Array<String>>) {
|
|
||||||
|
|
||||||
class Factory(private val componentActivity: ComponentActivity, private val secretsImporter: SecretsImporter, private val secretsRepository: SecretsRepository) {
|
|
||||||
|
|
||||||
fun buildSettingScreenHandlers(): SettingsScreenController {
|
|
||||||
return SettingsScreenController(
|
|
||||||
apkFilePickLauncher = buildFilePickerActivityLauncher(this::importApk)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun buildFilePickerActivityLauncher(onResult : (Uri?) ->Unit): ActivityResultLauncher<Array<String>> {
|
|
||||||
return componentActivity.registerForActivityResult(ActivityResultContracts.OpenDocument()) {
|
|
||||||
onResult.invoke(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun importApk(uri: Uri?) {
|
|
||||||
if(uri == null) {
|
|
||||||
componentActivity.runOnUiThread {
|
|
||||||
Toast.makeText(componentActivity, "APK Import Cancelled", Toast.LENGTH_SHORT)
|
|
||||||
.show()
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
componentActivity.lifecycleScope.launch(Dispatchers.IO) {
|
|
||||||
componentActivity.contentResolver.openInputStream(uri).use {
|
|
||||||
if(it == null) {
|
|
||||||
componentActivity.runOnUiThread {
|
|
||||||
Toast.makeText(
|
|
||||||
componentActivity,
|
|
||||||
"Selected file is empty!",
|
|
||||||
Toast.LENGTH_SHORT
|
|
||||||
).show()
|
|
||||||
}
|
|
||||||
return@launch
|
|
||||||
}
|
|
||||||
var secrets: Secrets? = null
|
|
||||||
try {
|
|
||||||
secrets = secretsImporter.importSecrets(it)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
componentActivity.runOnUiThread {
|
|
||||||
Toast.makeText(componentActivity, "Secrets import failed. Please only select the official Vital Arena App 2.1.0 APK.", Toast.LENGTH_SHORT).show()
|
|
||||||
}
|
|
||||||
return@launch
|
|
||||||
}
|
|
||||||
componentActivity.lifecycleScope.launch(Dispatchers.IO) {
|
|
||||||
secretsRepository.updateSecrets(secrets)
|
|
||||||
}.invokeOnCompletion {
|
|
||||||
componentActivity.runOnUiThread {
|
|
||||||
Toast.makeText(componentActivity, "Secrets successfully imported. Connections with devices are now possible.", Toast.LENGTH_SHORT).show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -10,24 +10,33 @@ import androidx.activity.ComponentActivity
|
|||||||
import androidx.activity.result.ActivityResultLauncher
|
import androidx.activity.result.ActivityResultLauncher
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import com.github.nacabaro.vbhelper.di.VBHelper
|
import com.github.nacabaro.vbhelper.di.VBHelper
|
||||||
|
import com.github.nacabaro.vbhelper.source.ApkSecretsImporter
|
||||||
|
import com.github.nacabaro.vbhelper.source.SecretsImporter
|
||||||
|
import com.github.nacabaro.vbhelper.source.SecretsRepository
|
||||||
|
import com.github.nacabaro.vbhelper.source.proto.Secrets
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.io.OutputStream
|
import java.io.OutputStream
|
||||||
|
|
||||||
|
|
||||||
class NewSettingsScreenControllerImpl(
|
class SettingsScreenControllerImpl(
|
||||||
private val context: ComponentActivity,
|
private val context: ComponentActivity,
|
||||||
): NewSettingsScreenController {
|
): SettingsScreenController {
|
||||||
|
private val roomDbName = "internalDb"
|
||||||
private val filePickerLauncher: ActivityResultLauncher<String>
|
private val filePickerLauncher: ActivityResultLauncher<String>
|
||||||
private val filePickerOpenerLauncher: ActivityResultLauncher<Array<String>>
|
private val filePickerOpenerLauncher: ActivityResultLauncher<Array<String>>
|
||||||
|
private val filePickerApk: ActivityResultLauncher<Array<String>>
|
||||||
|
private val secretsImporter: SecretsImporter = ApkSecretsImporter()
|
||||||
|
private val application = context.applicationContext as VBHelper
|
||||||
|
private val secretsRepository: SecretsRepository = application.container.dataStoreSecretsRepository
|
||||||
|
|
||||||
init {
|
init {
|
||||||
filePickerLauncher = context.registerForActivityResult(
|
filePickerLauncher = context.registerForActivityResult(
|
||||||
ActivityResultContracts.CreateDocument("application/octet-stream")
|
ActivityResultContracts.CreateDocument("application/octet-stream")
|
||||||
) { uri ->
|
) { uri ->
|
||||||
if (uri != null) {
|
if (uri != null) {
|
||||||
exportDatabase("internalDb", uri)
|
exportDatabase(uri)
|
||||||
} else {
|
} else {
|
||||||
context.runOnUiThread {
|
context.runOnUiThread {
|
||||||
Toast.makeText(context, "No destination selected", Toast.LENGTH_SHORT)
|
Toast.makeText(context, "No destination selected", Toast.LENGTH_SHORT)
|
||||||
@ -40,13 +49,25 @@ class NewSettingsScreenControllerImpl(
|
|||||||
ActivityResultContracts.OpenDocument()
|
ActivityResultContracts.OpenDocument()
|
||||||
) { uri ->
|
) { uri ->
|
||||||
if (uri != null) {
|
if (uri != null) {
|
||||||
importDatabase("internalDb", uri)
|
importDatabase(uri)
|
||||||
} else {
|
} else {
|
||||||
context.runOnUiThread {
|
context.runOnUiThread {
|
||||||
Toast.makeText(context, "No source selected", Toast.LENGTH_SHORT).show()
|
Toast.makeText(context, "No source selected", Toast.LENGTH_SHORT).show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
filePickerApk = context.registerForActivityResult(
|
||||||
|
ActivityResultContracts.OpenDocument()
|
||||||
|
) { uri ->
|
||||||
|
if (uri != null) {
|
||||||
|
importApk(uri)
|
||||||
|
} else {
|
||||||
|
context.runOnUiThread {
|
||||||
|
Toast.makeText(context, "APK import cancelled", Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onClickOpenDirectory() {
|
override fun onClickOpenDirectory() {
|
||||||
@ -57,10 +78,13 @@ class NewSettingsScreenControllerImpl(
|
|||||||
filePickerOpenerLauncher.launch(arrayOf("application/octet-stream"))
|
filePickerOpenerLauncher.launch(arrayOf("application/octet-stream"))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun exportDatabase(roomDbName: String, destinationUri: Uri) {
|
override fun onClickImportApk() {
|
||||||
|
filePickerApk.launch(arrayOf("*/*"))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun exportDatabase(destinationUri: Uri) {
|
||||||
context.lifecycleScope.launch(Dispatchers.IO) {
|
context.lifecycleScope.launch(Dispatchers.IO) {
|
||||||
try {
|
try {
|
||||||
val application = context.applicationContext as VBHelper
|
|
||||||
val dbFile = File(context.getDatabasePath(roomDbName).absolutePath)
|
val dbFile = File(context.getDatabasePath(roomDbName).absolutePath)
|
||||||
if (!dbFile.exists()) {
|
if (!dbFile.exists()) {
|
||||||
throw IllegalStateException("Database file does not exist!")
|
throw IllegalStateException("Database file does not exist!")
|
||||||
@ -88,11 +112,9 @@ class NewSettingsScreenControllerImpl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun importDatabase(roomDbName: String, sourceUri: Uri) {
|
private fun importDatabase(sourceUri: Uri) {
|
||||||
context.lifecycleScope.launch(Dispatchers.IO) {
|
context.lifecycleScope.launch(Dispatchers.IO) {
|
||||||
try {
|
try {
|
||||||
var application = context.applicationContext as VBHelper
|
|
||||||
|
|
||||||
if (!getFileNameFromUri(sourceUri)!!.endsWith(".vbhelper")) {
|
if (!getFileNameFromUri(sourceUri)!!.endsWith(".vbhelper")) {
|
||||||
context.runOnUiThread {
|
context.runOnUiThread {
|
||||||
Toast.makeText(context, "Invalid file format", Toast.LENGTH_SHORT).show()
|
Toast.makeText(context, "Invalid file format", Toast.LENGTH_SHORT).show()
|
||||||
@ -153,4 +175,37 @@ class NewSettingsScreenControllerImpl(
|
|||||||
}
|
}
|
||||||
outputStream.flush()
|
outputStream.flush()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun importApk(uri: Uri) {
|
||||||
|
context.lifecycleScope.launch(Dispatchers.IO) {
|
||||||
|
context.contentResolver.openInputStream(uri).use {
|
||||||
|
if(it == null) {
|
||||||
|
context.runOnUiThread {
|
||||||
|
Toast.makeText(
|
||||||
|
context,
|
||||||
|
"Selected file is empty!",
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
).show()
|
||||||
|
}
|
||||||
|
return@launch
|
||||||
|
}
|
||||||
|
val secrets: Secrets?
|
||||||
|
try {
|
||||||
|
secrets = secretsImporter.importSecrets(it)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
context.runOnUiThread {
|
||||||
|
Toast.makeText(context, "Secrets import failed. Please only select the official Vital Arena App 2.1.0 APK.", Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
return@launch
|
||||||
|
}
|
||||||
|
context.lifecycleScope.launch(Dispatchers.IO) {
|
||||||
|
secretsRepository.updateSecrets(secrets)
|
||||||
|
}.invokeOnCompletion {
|
||||||
|
context.runOnUiThread {
|
||||||
|
Toast.makeText(context, "Secrets successfully imported. Connections with devices are now possible.", Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user