mirror of
https://github.com/nacabaro/vbhelper.git
synced 2026-01-28 00:15:32 +00:00
commit
1a38cefb92
@ -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!"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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) {
|
||||||
|
|||||||
@ -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!!,
|
||||||
|
|||||||
@ -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!"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -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") { }
|
||||||
|
|||||||
@ -4,4 +4,5 @@ interface SettingsScreenController {
|
|||||||
fun onClickOpenDirectory()
|
fun onClickOpenDirectory()
|
||||||
fun onClickImportDatabase()
|
fun onClickImportDatabase()
|
||||||
fun onClickImportApk()
|
fun onClickImportApk()
|
||||||
|
fun onClickImportCard()
|
||||||
}
|
}
|
||||||
@ -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 {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user