Merge pull request #23 from nacabaro/cleanup

Cleanup MainActivity
This commit is contained in:
nacabaro 2025-01-21 13:12:38 +01:00 committed by GitHub
commit 1a38cefb92
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 243 additions and 275 deletions

View File

@ -1,44 +1,21 @@
package com.github.nacabaro.vbhelper package com.github.nacabaro.vbhelper
import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.util.Log import android.util.Log
import android.widget.Toast
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge import androidx.activity.enableEdgeToEdge
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.lifecycle.lifecycleScope
import com.github.cfogrady.vb.dim.card.BemCard
import com.github.cfogrady.vb.dim.card.DimReader
import com.github.nacabaro.vbhelper.navigation.AppNavigation import com.github.nacabaro.vbhelper.navigation.AppNavigation
import com.github.cfogrady.vbnfc.be.BENfcCharacter
import com.github.cfogrady.vbnfc.data.NfcCharacter
import com.github.cfogrady.vbnfc.vb.VBNfcCharacter
import com.github.nacabaro.vbhelper.di.VBHelper 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.device_data.BECharacterData
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.itemsScreen.ItemsScreenControllerImpl import com.github.nacabaro.vbhelper.screens.itemsScreen.ItemsScreenControllerImpl
import com.github.nacabaro.vbhelper.screens.scanScreen.ScanScreenControllerImpl import com.github.nacabaro.vbhelper.screens.scanScreen.ScanScreenControllerImpl
import com.github.nacabaro.vbhelper.screens.settingsScreen.SettingsScreenControllerImpl import com.github.nacabaro.vbhelper.screens.settingsScreen.SettingsScreenControllerImpl
import com.github.nacabaro.vbhelper.ui.theme.VBHelperTheme import com.github.nacabaro.vbhelper.ui.theme.VBHelperTheme
import com.github.nacabaro.vbhelper.utils.DeviceType
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
import java.util.GregorianCalendar
class MainActivity : ComponentActivity() { class MainActivity : ComponentActivity() {
private var nfcCharacter = MutableStateFlow<NfcCharacter?>(null)
private lateinit var activityResultLauncher: ActivityResultLauncher<Intent>
private val onActivityLifecycleListeners = HashMap<String, ActivityLifecycleListener>() private val onActivityLifecycleListeners = HashMap<String, ActivityLifecycleListener>()
private fun registerActivityLifecycleListener(key: String, activityLifecycleListener: ActivityLifecycleListener) { private fun registerActivityLifecycleListener(key: String, activityLifecycleListener: ActivityLifecycleListener) {
@ -53,12 +30,9 @@ class MainActivity : ComponentActivity() {
} }
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
registerFileActivityResult()
val application = applicationContext as VBHelper val application = applicationContext as VBHelper
val scanScreenController = ScanScreenControllerImpl( val scanScreenController = ScanScreenControllerImpl(
application.container.dataStoreSecretsRepository.secretsFlow, application.container.dataStoreSecretsRepository.secretsFlow,
this::handleReceivedNfcCharacter,
this, this,
this::registerActivityLifecycleListener, this::registerActivityLifecycleListener,
this::unregisterActivityLifecycleListener this::unregisterActivityLifecycleListener
@ -67,7 +41,9 @@ class MainActivity : ComponentActivity() {
val itemsScreenController = ItemsScreenControllerImpl(this) val itemsScreenController = ItemsScreenControllerImpl(this)
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
enableEdgeToEdge() enableEdgeToEdge()
setContent { setContent {
VBHelperTheme { VBHelperTheme {
MainApplication( MainApplication(
@ -77,6 +53,7 @@ class MainActivity : ComponentActivity() {
) )
} }
} }
Log.i("MainActivity", "Activity onCreated") Log.i("MainActivity", "Activity onCreated")
} }
@ -96,240 +73,18 @@ class MainActivity : ComponentActivity() {
} }
} }
private fun registerFileActivityResult() {
activityResultLauncher = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) {
lifecycleScope.launch {
val application = applicationContext as VBHelper
val storageRepository = application.container.db
if (it.resultCode != RESULT_OK) {
Toast.makeText(applicationContext, "Import operation cancelled.", Toast.LENGTH_SHORT).show()
}
val contentResolver = applicationContext.contentResolver
val inputStream = contentResolver.openInputStream(it.data!!.data!!)
inputStream.use { fileReader ->
val dimReader = DimReader()
val card = dimReader.readCard(fileReader, false)
Log.i("MainActivity", "Card name: ${card is BemCard}")
val cardModel = Card(
dimId = card.header.dimId,
logo = card.spriteData.sprites[0].pixelData,
name = card.spriteData.text, // TODO Make user write card name
stageCount = card.adventureLevels.levels.size,
logoHeight = card.spriteData.sprites[0].height,
logoWidth = card.spriteData.sprites[0].width,
isBEm = card is BemCard
)
val dimId = storageRepository
.dimDao()
.insertNewDim(cardModel)
val characters = card.characterStats.characterEntries
var spriteCounter = when (card is BemCard) {
true -> 55
false -> 10
}
val domainCharacters = mutableListOf<Character>()
for (index in 0 until characters.size) {
domainCharacters.add(
Character(
dimId = dimId,
monIndex = index,
name = card.spriteData.sprites[spriteCounter].pixelData,
stage = characters[index].stage,
attribute = characters[index].attribute,
baseHp = characters[index].hp,
baseBp = characters[index].dp,
baseAp = characters[index].ap,
sprite1 = card.spriteData.sprites[spriteCounter + 1].pixelData,
sprite2 = card.spriteData.sprites[spriteCounter + 2].pixelData,
nameWidth = card.spriteData.sprites[spriteCounter].width,
nameHeight = card.spriteData.sprites[spriteCounter].height,
spritesWidth = card.spriteData.sprites[spriteCounter + 1].width,
spritesHeight = card.spriteData.sprites[spriteCounter + 1].height
)
)
// TODO: Improve this
if (card is BemCard) {
spriteCounter += 14
} else {
when (index) {
0 -> spriteCounter += 6
1 -> spriteCounter += 7
else -> spriteCounter += 14
}
}
}
storageRepository
.characterDao()
.insertCharacter(*domainCharacters.toTypedArray())
val sprites = card.spriteData.sprites.map { sprite ->
Sprites(
id = 0,
sprite = sprite.pixelData,
width = sprite.width,
height = sprite.height
)
}
storageRepository
.characterDao()
.insertSprite(*sprites.toTypedArray())
}
inputStream?.close()
Toast.makeText(applicationContext, "Import successful!", Toast.LENGTH_SHORT).show()
}
}
}
@Composable @Composable
private fun MainApplication( private fun MainApplication(
scanScreenController: ScanScreenControllerImpl, scanScreenController: ScanScreenControllerImpl,
settingsScreenController: SettingsScreenControllerImpl, settingsScreenController: SettingsScreenControllerImpl,
itemsScreenController: ItemsScreenControllerImpl itemsScreenController: ItemsScreenControllerImpl
) { ) {
AppNavigation( AppNavigation(
applicationNavigationHandlers = AppNavigationHandlers( applicationNavigationHandlers = AppNavigationHandlers(
settingsScreenController, settingsScreenController,
scanScreenController, scanScreenController,
itemsScreenController itemsScreenController
), )
onClickImportCard = {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = "*/*"
}
activityResultLauncher.launch(intent)
}
) )
} }
private fun handleReceivedNfcCharacter(character: NfcCharacter): String {
nfcCharacter.value = character
val importStatus = addCharacterScannedIntoDatabase()
return importStatus
}
//
/*
TODO:
- Support for regular VB
The good news is that the theory behind inserting to the database should be working
now, it's a matter of implementing the functionality to parse dim/bem cards and use my
domain model.
*/
private fun addCharacterScannedIntoDatabase(): String {
val application = applicationContext as VBHelper
val storageRepository = application.container.db
val dimData = storageRepository
.dimDao()
.getDimById(nfcCharacter.value!!.dimId.toInt())
if (dimData == null) return "Card not found"
val cardCharData = storageRepository
.characterDao()
.getCharacterByMonIndex(nfcCharacter.value!!.charIndex.toInt(), dimData.id)
val characterData = UserCharacter(
charId = cardCharData.id,
stage = nfcCharacter.value!!.stage.toInt(),
attribute = nfcCharacter.value!!.attribute,
ageInDays = nfcCharacter.value!!.ageInDays.toInt(),
nextAdventureMissionStage = nfcCharacter.value!!.nextAdventureMissionStage.toInt(),
mood = nfcCharacter.value!!.mood.toInt(),
vitalPoints = nfcCharacter.value!!.vitalPoints.toInt(),
transformationCountdown = nfcCharacter.value!!.transformationCountdownInMinutes.toInt(),
injuryStatus = nfcCharacter.value!!.injuryStatus,
trophies = nfcCharacter.value!!.trophies.toInt(),
currentPhaseBattlesWon = nfcCharacter.value!!.currentPhaseBattlesWon.toInt(),
currentPhaseBattlesLost = nfcCharacter.value!!.currentPhaseBattlesLost.toInt(),
totalBattlesWon = nfcCharacter.value!!.totalBattlesWon.toInt(),
totalBattlesLost = nfcCharacter.value!!.totalBattlesLost.toInt(),
activityLevel = nfcCharacter.value!!.activityLevel.toInt(),
heartRateCurrent = nfcCharacter.value!!.heartRateCurrent.toInt(),
characterType = when (nfcCharacter.value) {
is BENfcCharacter -> DeviceType.BEDevice
else -> DeviceType.VBDevice
},
isActive = true
)
storageRepository
.userCharacterDao()
.clearActiveCharacter()
val characterId: Long = storageRepository
.userCharacterDao()
.insertCharacterData(characterData)
if (nfcCharacter.value is BENfcCharacter) {
val beCharacter = nfcCharacter.value as BENfcCharacter
val extraCharacterData = BECharacterData(
id = characterId,
trainingHp = beCharacter.trainingHp.toInt(),
trainingAp = beCharacter.trainingAp.toInt(),
trainingBp = beCharacter.trainingBp.toInt(),
remainingTrainingTimeInMinutes = beCharacter.remainingTrainingTimeInMinutes.toInt(),
itemEffectActivityLevelValue = beCharacter.itemEffectActivityLevelValue.toInt(),
itemEffectMentalStateValue = beCharacter.itemEffectMentalStateValue.toInt(),
itemEffectMentalStateMinutesRemaining = beCharacter.itemEffectMentalStateMinutesRemaining.toInt(),
itemEffectActivityLevelMinutesRemaining = beCharacter.itemEffectActivityLevelMinutesRemaining.toInt(),
itemEffectVitalPointsChangeValue = beCharacter.itemEffectVitalPointsChangeValue.toInt(),
itemEffectVitalPointsChangeMinutesRemaining = beCharacter.itemEffectVitalPointsChangeMinutesRemaining.toInt(),
abilityRarity = beCharacter.abilityRarity,
abilityType = beCharacter.abilityType.toInt(),
abilityBranch = beCharacter.abilityBranch.toInt(),
abilityReset = beCharacter.abilityReset.toInt(),
rank = beCharacter.abilityReset.toInt(),
itemType = beCharacter.itemType.toInt(),
itemMultiplier = beCharacter.itemMultiplier.toInt(),
itemRemainingTime = beCharacter.itemRemainingTime.toInt(),
otp0 = "", //beCharacter.value!!.otp0.toString(),
otp1 = "", //beCharacter.value!!.otp1.toString(),
minorVersion = beCharacter.characterCreationFirmwareVersion.minorVersion.toInt(),
majorVersion = beCharacter.characterCreationFirmwareVersion.majorVersion.toInt(),
)
storageRepository
.userCharacterDao()
.insertBECharacterData(extraCharacterData)
val transformationHistoryWatch = beCharacter.transformationHistory
transformationHistoryWatch.map { item ->
if (item.toCharIndex.toInt() != 255) {
val date = GregorianCalendar(item.year.toInt(), item.month.toInt(), item.day.toInt())
.time
.time
storageRepository
.characterDao()
.insertTransformation(characterId, item.toCharIndex.toInt(), dimData.id, date)
storageRepository
.dexDao()
.insertCharacter(item.toCharIndex.toInt(), dimData.id, date)
}
}
} else if (nfcCharacter.value is VBNfcCharacter) {
return "Not implemented yet"
}
return "Done reading character!"
}
} }

View File

@ -30,7 +30,6 @@ data class AppNavigationHandlers(
@Composable @Composable
fun AppNavigation( fun AppNavigation(
applicationNavigationHandlers: AppNavigationHandlers, applicationNavigationHandlers: AppNavigationHandlers,
onClickImportCard: () -> Unit,
) { ) {
val navController = rememberNavController() val navController = rememberNavController()
@ -76,8 +75,7 @@ fun AppNavigation(
composable(NavigationItems.Settings.route) { composable(NavigationItems.Settings.route) {
SettingsScreen( SettingsScreen(
navController = navController, navController = navController,
settingsScreenController = applicationNavigationHandlers.settingsScreenController, settingsScreenController = applicationNavigationHandlers.settingsScreenController
onClickImportCard = onClickImportCard,
) )
} }
composable(NavigationItems.Viewer.route) { composable(NavigationItems.Viewer.route) {

View File

@ -1,6 +1,5 @@
package com.github.nacabaro.vbhelper.screens.homeScreens package com.github.nacabaro.vbhelper.screens.homeScreens
import android.util.Log
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
@ -72,7 +71,6 @@ fun HomeScreen(
} }
} else { } else {
if (activeMon.value!!.isBemCard) { if (activeMon.value!!.isBemCard) {
Log.d("HomeScreen", "BEDeviceBEm")
BEBEmHomeScreen( BEBEmHomeScreen(
activeMon = activeMon.value!!, activeMon = activeMon.value!!,
beData = beData.value!!, beData = beData.value!!,
@ -80,7 +78,6 @@ fun HomeScreen(
contentPadding = contentPadding contentPadding = contentPadding
) )
} else if (!activeMon.value!!.isBemCard && activeMon.value!!.characterType == DeviceType.BEDevice) { } else if (!activeMon.value!!.isBemCard && activeMon.value!!.characterType == DeviceType.BEDevice) {
Log.d("HomeScreen", "BEDevice")
BEDiMHomeScreen( BEDiMHomeScreen(
activeMon = activeMon.value!!, activeMon = activeMon.value!!,
beData = beData.value!!, beData = beData.value!!,

View File

@ -11,20 +11,26 @@ import android.widget.Toast
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.github.cfogrady.vbnfc.TagCommunicator import com.github.cfogrady.vbnfc.TagCommunicator
import com.github.cfogrady.vbnfc.be.BENfcCharacter
import com.github.cfogrady.vbnfc.data.NfcCharacter import com.github.cfogrady.vbnfc.data.NfcCharacter
import com.github.cfogrady.vbnfc.vb.VBNfcCharacter
import com.github.nacabaro.vbhelper.ActivityLifecycleListener import com.github.nacabaro.vbhelper.ActivityLifecycleListener
import com.github.nacabaro.vbhelper.di.VBHelper
import com.github.nacabaro.vbhelper.domain.device_data.BECharacterData
import com.github.nacabaro.vbhelper.domain.device_data.UserCharacter
import com.github.nacabaro.vbhelper.source.getCryptographicTransformerMap import com.github.nacabaro.vbhelper.source.getCryptographicTransformerMap
import com.github.nacabaro.vbhelper.source.isMissingSecrets import com.github.nacabaro.vbhelper.source.isMissingSecrets
import com.github.nacabaro.vbhelper.source.proto.Secrets import com.github.nacabaro.vbhelper.source.proto.Secrets
import com.github.nacabaro.vbhelper.utils.DeviceType
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.util.GregorianCalendar
class ScanScreenControllerImpl( class ScanScreenControllerImpl(
override val secretsFlow: Flow<Secrets>, override val secretsFlow: Flow<Secrets>,
private val nfcHandler: (NfcCharacter)->String, private val componentActivity: ComponentActivity,
private val context: ComponentActivity,
private val registerActivityLifecycleListener: (String, ActivityLifecycleListener)->Unit, private val registerActivityLifecycleListener: (String, ActivityLifecycleListener)->Unit,
private val unregisterActivityLifecycleListener: (String)->Unit, private val unregisterActivityLifecycleListener: (String)->Unit,
): ScanScreenController { ): ScanScreenController {
@ -32,9 +38,9 @@ class ScanScreenControllerImpl(
private val nfcAdapter: NfcAdapter private val nfcAdapter: NfcAdapter
init { init {
val maybeNfcAdapter = NfcAdapter.getDefaultAdapter(context) val maybeNfcAdapter = NfcAdapter.getDefaultAdapter(componentActivity)
if (maybeNfcAdapter == null) { if (maybeNfcAdapter == null) {
Toast.makeText(context, "No NFC on device!", Toast.LENGTH_SHORT).show() Toast.makeText(componentActivity, "No NFC on device!", Toast.LENGTH_SHORT).show()
} }
nfcAdapter = maybeNfcAdapter nfcAdapter = maybeNfcAdapter
checkSecrets() checkSecrets()
@ -43,7 +49,7 @@ class ScanScreenControllerImpl(
override fun onClickRead(secrets: Secrets, onComplete: ()->Unit) { override fun onClickRead(secrets: Secrets, onComplete: ()->Unit) {
handleTag(secrets) { tagCommunicator -> handleTag(secrets) { tagCommunicator ->
val character = tagCommunicator.receiveCharacter() val character = tagCommunicator.receiveCharacter()
val resultMessage = nfcHandler(character) val resultMessage = addCharacterScannedIntoDatabase(character)
onComplete.invoke() onComplete.invoke()
resultMessage resultMessage
} }
@ -51,7 +57,7 @@ class ScanScreenControllerImpl(
override fun cancelRead() { override fun cancelRead() {
if(nfcAdapter.isEnabled) { if(nfcAdapter.isEnabled) {
nfcAdapter.disableReaderMode(context) nfcAdapter.disableReaderMode(componentActivity)
} }
} }
@ -74,7 +80,7 @@ class ScanScreenControllerImpl(
val options = Bundle() val options = Bundle()
// Work around for some broken Nfc firmware implementations that poll the card too fast // Work around for some broken Nfc firmware implementations that poll the card too fast
options.putInt(NfcAdapter.EXTRA_READER_PRESENCE_CHECK_DELAY, 250) options.putInt(NfcAdapter.EXTRA_READER_PRESENCE_CHECK_DELAY, 250)
nfcAdapter.enableReaderMode(context, buildOnReadTag(secrets, handlerFunc), NfcAdapter.FLAG_READER_NFC_A or NfcAdapter.FLAG_READER_SKIP_NDEF_CHECK, nfcAdapter.enableReaderMode(componentActivity, buildOnReadTag(secrets, handlerFunc), NfcAdapter.FLAG_READER_NFC_A or NfcAdapter.FLAG_READER_SKIP_NDEF_CHECK,
options options
) )
} }
@ -85,26 +91,26 @@ class ScanScreenControllerImpl(
return { tag-> return { tag->
val nfcData = NfcA.get(tag) val nfcData = NfcA.get(tag)
if (nfcData == null) { if (nfcData == null) {
context.runOnUiThread { componentActivity.runOnUiThread {
Toast.makeText(context, "Tag detected is not VB", Toast.LENGTH_SHORT).show() Toast.makeText(componentActivity, "Tag detected is not VB", Toast.LENGTH_SHORT).show()
} }
} }
nfcData.connect() nfcData.connect()
nfcData.use { nfcData.use {
val tagCommunicator = TagCommunicator.getInstance(nfcData, secrets.getCryptographicTransformerMap()) val tagCommunicator = TagCommunicator.getInstance(nfcData, secrets.getCryptographicTransformerMap())
val successText = handlerFunc(tagCommunicator) val successText = handlerFunc(tagCommunicator)
context.runOnUiThread { componentActivity.runOnUiThread {
Toast.makeText(context, successText, Toast.LENGTH_SHORT).show() Toast.makeText(componentActivity, successText, Toast.LENGTH_SHORT).show()
} }
} }
} }
} }
private fun checkSecrets() { private fun checkSecrets() {
context.lifecycleScope.launch(Dispatchers.IO) { componentActivity.lifecycleScope.launch(Dispatchers.IO) {
if(secretsFlow.stateIn(context.lifecycleScope).value.isMissingSecrets()) { if(secretsFlow.stateIn(componentActivity.lifecycleScope).value.isMissingSecrets()) {
context.runOnUiThread { componentActivity.runOnUiThread {
Toast.makeText(context, "Missing Secrets. Go to settings and import Vital Arena APK", Toast.LENGTH_SHORT).show() Toast.makeText(componentActivity, "Missing Secrets. Go to settings and import Vital Arena APK", Toast.LENGTH_SHORT).show()
} }
} }
} }
@ -141,7 +147,107 @@ class ScanScreenControllerImpl(
// EXTRACTED DIRECTLY FROM EXAMPLE APP // EXTRACTED DIRECTLY FROM EXAMPLE APP
private fun showWirelessSettings() { private fun showWirelessSettings() {
Toast.makeText(context, "NFC must be enabled", Toast.LENGTH_SHORT).show() Toast.makeText(componentActivity, "NFC must be enabled", Toast.LENGTH_SHORT).show()
context.startActivity(Intent(Settings.ACTION_WIRELESS_SETTINGS)) componentActivity.startActivity(Intent(Settings.ACTION_WIRELESS_SETTINGS))
}
private fun addCharacterScannedIntoDatabase(nfcCharacter: NfcCharacter): String {
val application = componentActivity.applicationContext as VBHelper
val storageRepository = application.container.db
val dimData = storageRepository
.dimDao()
.getDimById(nfcCharacter.dimId.toInt())
if (dimData == null) return "Card not found"
val cardCharData = storageRepository
.characterDao()
.getCharacterByMonIndex(nfcCharacter.charIndex.toInt(), dimData.id)
val characterData = UserCharacter(
charId = cardCharData.id,
stage = nfcCharacter.stage.toInt(),
attribute = nfcCharacter.attribute,
ageInDays = nfcCharacter.ageInDays.toInt(),
nextAdventureMissionStage = nfcCharacter.nextAdventureMissionStage.toInt(),
mood = nfcCharacter.mood.toInt(),
vitalPoints = nfcCharacter.vitalPoints.toInt(),
transformationCountdown = nfcCharacter.transformationCountdownInMinutes.toInt(),
injuryStatus = nfcCharacter.injuryStatus,
trophies = nfcCharacter.trophies.toInt(),
currentPhaseBattlesWon = nfcCharacter.currentPhaseBattlesWon.toInt(),
currentPhaseBattlesLost = nfcCharacter.currentPhaseBattlesLost.toInt(),
totalBattlesWon = nfcCharacter.totalBattlesWon.toInt(),
totalBattlesLost = nfcCharacter.totalBattlesLost.toInt(),
activityLevel = nfcCharacter.activityLevel.toInt(),
heartRateCurrent = nfcCharacter.heartRateCurrent.toInt(),
characterType = when (nfcCharacter) {
is BENfcCharacter -> DeviceType.BEDevice
else -> DeviceType.VBDevice
},
isActive = true
)
storageRepository
.userCharacterDao()
.clearActiveCharacter()
val characterId: Long = storageRepository
.userCharacterDao()
.insertCharacterData(characterData)
if (nfcCharacter is BENfcCharacter) {
val extraCharacterData = BECharacterData(
id = characterId,
trainingHp = nfcCharacter.trainingHp.toInt(),
trainingAp = nfcCharacter.trainingAp.toInt(),
trainingBp = nfcCharacter.trainingBp.toInt(),
remainingTrainingTimeInMinutes = nfcCharacter.remainingTrainingTimeInMinutes.toInt(),
itemEffectActivityLevelValue = nfcCharacter.itemEffectActivityLevelValue.toInt(),
itemEffectMentalStateValue = nfcCharacter.itemEffectMentalStateValue.toInt(),
itemEffectMentalStateMinutesRemaining = nfcCharacter.itemEffectMentalStateMinutesRemaining.toInt(),
itemEffectActivityLevelMinutesRemaining = nfcCharacter.itemEffectActivityLevelMinutesRemaining.toInt(),
itemEffectVitalPointsChangeValue = nfcCharacter.itemEffectVitalPointsChangeValue.toInt(),
itemEffectVitalPointsChangeMinutesRemaining = nfcCharacter.itemEffectVitalPointsChangeMinutesRemaining.toInt(),
abilityRarity = nfcCharacter.abilityRarity,
abilityType = nfcCharacter.abilityType.toInt(),
abilityBranch = nfcCharacter.abilityBranch.toInt(),
abilityReset = nfcCharacter.abilityReset.toInt(),
rank = nfcCharacter.abilityReset.toInt(),
itemType = nfcCharacter.itemType.toInt(),
itemMultiplier = nfcCharacter.itemMultiplier.toInt(),
itemRemainingTime = nfcCharacter.itemRemainingTime.toInt(),
otp0 = "", //nfcCharacter.value!!.otp0.toString(),
otp1 = "", //nfcCharacter.value!!.otp1.toString(),
minorVersion = nfcCharacter.characterCreationFirmwareVersion.minorVersion.toInt(),
majorVersion = nfcCharacter.characterCreationFirmwareVersion.majorVersion.toInt(),
)
storageRepository
.userCharacterDao()
.insertBECharacterData(extraCharacterData)
val transformationHistoryWatch = nfcCharacter.transformationHistory
transformationHistoryWatch.map { item ->
if (item.toCharIndex.toInt() != 255) {
val date = GregorianCalendar(item.year.toInt(), item.month.toInt(), item.day.toInt())
.time
.time
storageRepository
.characterDao()
.insertTransformation(characterId, item.toCharIndex.toInt(), dimData.id, date)
storageRepository
.dexDao()
.insertCharacter(item.toCharIndex.toInt(), dimData.id, date)
}
}
} else if (nfcCharacter is VBNfcCharacter) {
return "Not implemented yet"
}
return "Done reading character!"
} }
} }

View File

@ -23,7 +23,6 @@ import com.github.nacabaro.vbhelper.components.TopBanner
fun SettingsScreen( fun SettingsScreen(
navController: NavController, navController: NavController,
settingsScreenController: SettingsScreenControllerImpl, settingsScreenController: SettingsScreenControllerImpl,
onClickImportCard: () -> Unit
) { ) {
Scaffold ( Scaffold (
topBar = { topBar = {
@ -55,7 +54,9 @@ fun SettingsScreen(
settingsScreenController.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") {
settingsScreenController.onClickImportCard()
}
SettingsEntry(title = "Rename DiM/BEm", description = "Set card name") { } SettingsEntry(title = "Rename DiM/BEm", description = "Set card name") { }
SettingsSection("About and credits") SettingsSection("About and credits")
SettingsEntry(title = "Credits", description = "Credits") { } SettingsEntry(title = "Credits", description = "Credits") { }

View File

@ -4,4 +4,5 @@ interface SettingsScreenController {
fun onClickOpenDirectory() fun onClickOpenDirectory()
fun onClickImportDatabase() fun onClickImportDatabase()
fun onClickImportApk() fun onClickImportApk()
fun onClickImportCard()
} }

View File

@ -9,7 +9,13 @@ import android.widget.Toast
import androidx.activity.ComponentActivity 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.cfogrady.vb.dim.card.BemCard
import com.github.cfogrady.vb.dim.card.DimReader
import com.github.nacabaro.vbhelper.database.AppDatabase
import com.github.nacabaro.vbhelper.di.VBHelper import com.github.nacabaro.vbhelper.di.VBHelper
import com.github.nacabaro.vbhelper.domain.Sprites
import com.github.nacabaro.vbhelper.domain.characters.Card
import com.github.nacabaro.vbhelper.domain.characters.Character
import com.github.nacabaro.vbhelper.source.ApkSecretsImporter import com.github.nacabaro.vbhelper.source.ApkSecretsImporter
import com.github.nacabaro.vbhelper.source.SecretsImporter import com.github.nacabaro.vbhelper.source.SecretsImporter
import com.github.nacabaro.vbhelper.source.SecretsRepository import com.github.nacabaro.vbhelper.source.SecretsRepository
@ -27,9 +33,11 @@ class SettingsScreenControllerImpl(
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 filePickerApk: ActivityResultLauncher<Array<String>>
private val filePickerCard: ActivityResultLauncher<Array<String>>
private val secretsImporter: SecretsImporter = ApkSecretsImporter() private val secretsImporter: SecretsImporter = ApkSecretsImporter()
private val application = context.applicationContext as VBHelper private val application = context.applicationContext as VBHelper
private val secretsRepository: SecretsRepository = application.container.dataStoreSecretsRepository private val secretsRepository: SecretsRepository = application.container.dataStoreSecretsRepository
private val database: AppDatabase = application.container.db
init { init {
filePickerLauncher = context.registerForActivityResult( filePickerLauncher = context.registerForActivityResult(
@ -68,6 +76,18 @@ class SettingsScreenControllerImpl(
} }
} }
} }
filePickerCard = context.registerForActivityResult(
ActivityResultContracts.OpenDocument()
) { uri ->
if (uri != null) {
importCard(uri)
} else {
context.runOnUiThread {
Toast.makeText(context, "Card import cancelled", Toast.LENGTH_SHORT).show()
}
}
}
} }
override fun onClickOpenDirectory() { override fun onClickOpenDirectory() {
@ -82,6 +102,96 @@ class SettingsScreenControllerImpl(
filePickerApk.launch(arrayOf("*/*")) filePickerApk.launch(arrayOf("*/*"))
} }
override fun onClickImportCard() {
filePickerCard.launch(arrayOf("*/*"))
}
private fun importCard(uri: Uri) {
context.lifecycleScope.launch(Dispatchers.IO) {
val contentResolver = context.contentResolver
val inputStream = contentResolver.openInputStream(uri)
inputStream.use { fileReader ->
val dimReader = DimReader()
val card = dimReader.readCard(fileReader, false)
val cardModel = Card(
dimId = card.header.dimId,
logo = card.spriteData.sprites[0].pixelData,
name = card.spriteData.text, // TODO Make user write card name// TODO Make user write card name
stageCount = card.adventureLevels.levels.size,
logoHeight = card.spriteData.sprites[0].height,
logoWidth = card.spriteData.sprites[0].width,
isBEm = card is BemCard
)
val dimId = database
.dimDao()
.insertNewDim(cardModel)
val characters = card.characterStats.characterEntries
var spriteCounter = when (card is BemCard) {
true -> 55
false -> 10
}
val domainCharacters = mutableListOf<Character>()
for (index in 0 until characters.size) {
domainCharacters.add(
Character(
dimId = dimId,
monIndex = index,
name = card.spriteData.sprites[spriteCounter].pixelData,
stage = characters[index].stage,
attribute = characters[index].attribute,
baseHp = characters[index].hp,
baseBp = characters[index].dp,
baseAp = characters[index].ap,
sprite1 = card.spriteData.sprites[spriteCounter + 1].pixelData,
sprite2 = card.spriteData.sprites[spriteCounter + 2].pixelData,
nameWidth = card.spriteData.sprites[spriteCounter].width,
nameHeight = card.spriteData.sprites[spriteCounter].height,
spritesWidth = card.spriteData.sprites[spriteCounter + 1].width,
spritesHeight = card.spriteData.sprites[spriteCounter + 1].height
)
)
spriteCounter += if (card is BemCard) {
14
} else {
when (index) {
0 -> 6
1 -> 7
else -> 14
}
}
}
database
.characterDao()
.insertCharacter(*domainCharacters.toTypedArray())
val sprites = card.spriteData.sprites.map { sprite ->
Sprites(
id = 0,
sprite = sprite.pixelData,
width = sprite.width,
height = sprite.height
)
}
database
.characterDao()
.insertSprite(*sprites.toTypedArray())
}
inputStream?.close()
context.runOnUiThread {
Toast.makeText(context, "Import successful!", Toast.LENGTH_SHORT).show()
}
}
}
private fun exportDatabase(destinationUri: Uri) { private fun exportDatabase(destinationUri: Uri) {
context.lifecycleScope.launch(Dispatchers.IO) { context.lifecycleScope.launch(Dispatchers.IO) {
try { try {