Moved read character from watch to its corresponding controller

This commit is contained in:
Nacho 2025-01-21 13:02:45 +01:00
parent bb5f66d167
commit a974bd366e
3 changed files with 124 additions and 150 deletions

View File

@ -7,25 +7,15 @@ import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.runtime.Composable
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.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.itemsScreen.ItemsScreenControllerImpl
import com.github.nacabaro.vbhelper.screens.scanScreen.ScanScreenControllerImpl
import com.github.nacabaro.vbhelper.screens.settingsScreen.SettingsScreenControllerImpl
import com.github.nacabaro.vbhelper.ui.theme.VBHelperTheme
import com.github.nacabaro.vbhelper.utils.DeviceType
import kotlinx.coroutines.flow.MutableStateFlow
import java.util.GregorianCalendar
class MainActivity : ComponentActivity() {
private var nfcCharacter = MutableStateFlow<NfcCharacter?>(null)
private val onActivityLifecycleListeners = HashMap<String, ActivityLifecycleListener>()
private fun registerActivityLifecycleListener(key: String, activityLifecycleListener: ActivityLifecycleListener) {
@ -43,7 +33,6 @@ class MainActivity : ComponentActivity() {
val application = applicationContext as VBHelper
val scanScreenController = ScanScreenControllerImpl(
application.container.dataStoreSecretsRepository.secretsFlow,
this::handleReceivedNfcCharacter,
this,
this::registerActivityLifecycleListener,
this::unregisterActivityLifecycleListener
@ -98,122 +87,4 @@ class MainActivity : ComponentActivity() {
)
)
}
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

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

View File

@ -11,20 +11,26 @@ import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.lifecycle.lifecycleScope
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.vb.VBNfcCharacter
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.isMissingSecrets
import com.github.nacabaro.vbhelper.source.proto.Secrets
import com.github.nacabaro.vbhelper.utils.DeviceType
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import java.util.GregorianCalendar
class ScanScreenControllerImpl(
override val secretsFlow: Flow<Secrets>,
private val nfcHandler: (NfcCharacter)->String,
private val context: ComponentActivity,
private val componentActivity: ComponentActivity,
private val registerActivityLifecycleListener: (String, ActivityLifecycleListener)->Unit,
private val unregisterActivityLifecycleListener: (String)->Unit,
): ScanScreenController {
@ -32,9 +38,9 @@ class ScanScreenControllerImpl(
private val nfcAdapter: NfcAdapter
init {
val maybeNfcAdapter = NfcAdapter.getDefaultAdapter(context)
val maybeNfcAdapter = NfcAdapter.getDefaultAdapter(componentActivity)
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
checkSecrets()
@ -43,7 +49,7 @@ class ScanScreenControllerImpl(
override fun onClickRead(secrets: Secrets, onComplete: ()->Unit) {
handleTag(secrets) { tagCommunicator ->
val character = tagCommunicator.receiveCharacter()
val resultMessage = nfcHandler(character)
val resultMessage = addCharacterScannedIntoDatabase(character)
onComplete.invoke()
resultMessage
}
@ -51,7 +57,7 @@ class ScanScreenControllerImpl(
override fun cancelRead() {
if(nfcAdapter.isEnabled) {
nfcAdapter.disableReaderMode(context)
nfcAdapter.disableReaderMode(componentActivity)
}
}
@ -74,7 +80,7 @@ class ScanScreenControllerImpl(
val options = Bundle()
// Work around for some broken Nfc firmware implementations that poll the card too fast
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
)
}
@ -85,26 +91,26 @@ class ScanScreenControllerImpl(
return { tag->
val nfcData = NfcA.get(tag)
if (nfcData == null) {
context.runOnUiThread {
Toast.makeText(context, "Tag detected is not VB", Toast.LENGTH_SHORT).show()
componentActivity.runOnUiThread {
Toast.makeText(componentActivity, "Tag detected is not VB", Toast.LENGTH_SHORT).show()
}
}
nfcData.connect()
nfcData.use {
val tagCommunicator = TagCommunicator.getInstance(nfcData, secrets.getCryptographicTransformerMap())
val successText = handlerFunc(tagCommunicator)
context.runOnUiThread {
Toast.makeText(context, successText, Toast.LENGTH_SHORT).show()
componentActivity.runOnUiThread {
Toast.makeText(componentActivity, successText, Toast.LENGTH_SHORT).show()
}
}
}
}
private fun checkSecrets() {
context.lifecycleScope.launch(Dispatchers.IO) {
if(secretsFlow.stateIn(context.lifecycleScope).value.isMissingSecrets()) {
context.runOnUiThread {
Toast.makeText(context, "Missing Secrets. Go to settings and import Vital Arena APK", Toast.LENGTH_SHORT).show()
componentActivity.lifecycleScope.launch(Dispatchers.IO) {
if(secretsFlow.stateIn(componentActivity.lifecycleScope).value.isMissingSecrets()) {
componentActivity.runOnUiThread {
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
private fun showWirelessSettings() {
Toast.makeText(context, "NFC must be enabled", Toast.LENGTH_SHORT).show()
context.startActivity(Intent(Settings.ACTION_WIRELESS_SETTINGS))
Toast.makeText(componentActivity, "NFC must be enabled", Toast.LENGTH_SHORT).show()
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!"
}
}