Bug fixes

- Fixed an issue with the calendar, now I store the date as year/month/day in the database directly.
- VB expects to have a year between 2021 and 2032, for some reason it gives me back a 2000 year when reading an empty history. I just patched it in the application to return as year 0 in those cases.
- VitalsHistory works as intended now. Also verified functionality of the new classes.
- Moved a few SQL queries too, since they didn't belong where they were.

Also added a small feature, now the application knows if you entered the scan screen through a character in the storage, or the home screen. If there is an active character and you opened the home screen, the application will let you send the active character, while if it was opened through the storage screen, the application will send the character from the storage screen.

What's now missing is to create a VBSCanScreen, and depending on which device we are using, make use of one screen or the other.
This commit is contained in:
Nacho 2025-07-29 10:36:07 +02:00
parent d847f600f1
commit 220a61a553
9 changed files with 114 additions and 180 deletions

View File

@ -28,27 +28,8 @@ interface CharacterDao {
FROM Character c FROM Character c
JOIN UserCharacter uc ON c.id = uc.charId JOIN UserCharacter uc ON c.id = uc.charId
JOIN Card d ON c.dimId = d.id JOIN Card d ON c.dimId = d.id
WHERE uc.id = :charId WHERE c.id = :charId
""" """
) )
suspend fun getCharacterInfo(charId: Long): CharacterDtos.CardCharacterInfo suspend fun getCharacterInfo(charId: Long): CharacterDtos.CardCharacterInfo
@Query("""
INSERT INTO TransformationHistory(monId, stageId, transformationDate)
VALUES
(:monId,
(SELECT id FROM Character WHERE monIndex = :stage AND dimId = :dimId),
:transformationDate)
""")
fun insertTransformation(monId: Long, stage: Int, dimId: Long, transformationDate: Long)
@Query("""
INSERT INTO VitalsHistory(charId, date, vitalPoints)
VALUES
(:charId,
(:date),
:vitalPoints)
""")
fun insertVitals(charId: Long, date: Long, vitalPoints: Int)
} }

View File

@ -156,4 +156,20 @@ interface UserCharacterDao {
""" """
) )
suspend fun getCharacterInfo(charId: Long): Character suspend fun getCharacterInfo(charId: Long): Character
@Query("""
INSERT INTO TransformationHistory(monId, stageId, transformationDate)
VALUES
(:monId,
(SELECT id FROM Character WHERE monIndex = :stage AND dimId = :dimId),
:transformationDate)
""")
fun insertTransformation(monId: Long, stage: Int, dimId: Long, transformationDate: Long)
@Upsert
fun insertVitals(vararg vitalsHistory: VitalsHistory)
@Query("""SELECT * FROM VitalsHistory WHERE charId = :charId ORDER BY id ASC""")
suspend fun getVitalsHistory(charId: Long): List<VitalsHistory>
} }

View File

@ -15,7 +15,7 @@ import androidx.room.PrimaryKey
] ]
) )
data class CardProgress( data class CardProgress(
@PrimaryKey val cardId: Int, @PrimaryKey val cardId: Long,
val currentStage: Int, val currentStage: Int,
val unlocked: Boolean val unlocked: Boolean
) )

View File

@ -17,6 +17,8 @@ import androidx.room.PrimaryKey
data class VitalsHistory ( data class VitalsHistory (
@PrimaryKey(autoGenerate = true) val id: Long = 0, @PrimaryKey(autoGenerate = true) val id: Long = 0,
val charId: Long, val charId: Long,
val date: Long, val year: Int,
val month: Int,
val day: Int,
val vitalPoints: Int val vitalPoints: Int
) )

View File

@ -57,11 +57,33 @@ fun ScanScreen(
val storageRepository = StorageRepository(application.container.db) val storageRepository = StorageRepository(application.container.db)
var nfcCharacter by remember { mutableStateOf<NfcCharacter?>(null) } var nfcCharacter by remember { mutableStateOf<NfcCharacter?>(null) }
/*
This is in the case there is an active character,
that way active characters are quicker to send.
*/
var selectedCharacterId by remember { mutableStateOf<Long?>(null) }
selectedCharacterId = characterId
val context = LocalContext.current val context = LocalContext.current
LaunchedEffect(storageRepository) { LaunchedEffect(storageRepository) {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
if(characterId != null && nfcCharacter == null) { /*
nfcCharacter = scanScreenController.characterToNfc(characterId) First check if there is a character sent through the navigation system
If there is not, that means we got here through the home screen nfc button
If we got here through the home screen, it does not hurt to check if there is
an active character.
*/
if (characterId != null && nfcCharacter == null) {
selectedCharacterId = characterId
nfcCharacter = scanScreenController.characterToNfc(selectedCharacterId!!)
}
else if (characterId == null && nfcCharacter == null) {
val activeCharacter = storageRepository.getActiveCharacter()
if (activeCharacter != null) {
selectedCharacterId = activeCharacter.id
nfcCharacter = scanScreenController.characterToNfc(selectedCharacterId!!)
}
} }
} }
} }
@ -120,13 +142,15 @@ fun ScanScreen(
) )
} }
if (!isDoneSendingCard) { if (secrets != null && nfcCharacter != null) {
scanScreenController.onClickCheckCard(secrets!!, nfcCharacter!!) { if (!isDoneSendingCard) {
isDoneSendingCard = true scanScreenController.onClickCheckCard(secrets!!, nfcCharacter!!) {
} isDoneSendingCard = true
} else if (!isDoneWritingCharacter) { }
scanScreenController.onClickWrite(secrets!!, nfcCharacter!!) { } else if (!isDoneWritingCharacter) {
isDoneWritingCharacter = true scanScreenController.onClickWrite(secrets!!, nfcCharacter!!) {
isDoneWritingCharacter = true
}
} }
} }
@ -147,7 +171,7 @@ fun ScanScreen(
LaunchedEffect(storageRepository) { LaunchedEffect(storageRepository) {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
storageRepository storageRepository
.deleteCharacter(characterId!!) .deleteCharacter(selectedCharacterId!!)
} }
} }
} }
@ -173,7 +197,7 @@ fun ScanScreen(
} else { } else {
ChooseConnectOption( ChooseConnectOption(
onClickRead = when { onClickRead = when {
characterId != null -> null selectedCharacterId != null -> null
else -> { else -> {
{ {
if(secrets == null) { if(secrets == null) {

View File

@ -146,108 +146,6 @@ class ScanScreenControllerImpl(
componentActivity.startActivity(Intent(Settings.ACTION_WIRELESS_SETTINGS)) componentActivity.startActivity(Intent(Settings.ACTION_WIRELESS_SETTINGS))
} }
/*
// Todo: Move all of this to a separate class
// Todo: Test the new mess
// Todo: Remove this
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,
ageInDays = nfcCharacter.ageInDays.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!"
}*/
override fun characterFromNfc(nfcCharacter: NfcCharacter): String { override fun characterFromNfc(nfcCharacter: NfcCharacter): String {
val nfcConverter = FromNfcConverter( val nfcConverter = FromNfcConverter(
componentActivity = componentActivity componentActivity = componentActivity

View File

@ -1,5 +1,6 @@
package com.github.nacabaro.vbhelper.screens.scanScreen.converters package com.github.nacabaro.vbhelper.screens.scanScreen.converters
import android.util.Log
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import com.github.cfogrady.vbnfc.be.BENfcCharacter import com.github.cfogrady.vbnfc.be.BENfcCharacter
import com.github.cfogrady.vbnfc.data.NfcCharacter import com.github.cfogrady.vbnfc.data.NfcCharacter
@ -11,6 +12,7 @@ import com.github.nacabaro.vbhelper.domain.device_data.BECharacterData
import com.github.nacabaro.vbhelper.domain.device_data.SpecialMissions import com.github.nacabaro.vbhelper.domain.device_data.SpecialMissions
import com.github.nacabaro.vbhelper.domain.device_data.UserCharacter import com.github.nacabaro.vbhelper.domain.device_data.UserCharacter
import com.github.nacabaro.vbhelper.domain.device_data.VBCharacterData import com.github.nacabaro.vbhelper.domain.device_data.VBCharacterData
import com.github.nacabaro.vbhelper.domain.device_data.VitalsHistory
import com.github.nacabaro.vbhelper.utils.DeviceType import com.github.nacabaro.vbhelper.utils.DeviceType
import java.util.GregorianCalendar import java.util.GregorianCalendar
@ -57,8 +59,6 @@ class FromNfcConverter (
isActive = true isActive = true
) )
updateCardProgress(cardData, nfcCharacter)
database database
.userCharacterDao() .userCharacterDao()
.clearActiveCharacter() .clearActiveCharacter()
@ -85,6 +85,11 @@ class FromNfcConverter (
dimData = cardData dimData = cardData
) )
addVitalsHistoryToDatabase(
characterId = characterId,
nfcCharacter = nfcCharacter
)
return "Done reading character!" return "Done reading character!"
} }
@ -95,7 +100,7 @@ class FromNfcConverter (
cardData: Card cardData: Card
) { ) {
val currentCardProgress = CardProgress( val currentCardProgress = CardProgress(
cardId = cardData.cardId, cardId = cardData.id,
currentStage = nfcCharacter.nextAdventureMissionStage.toInt(), currentStage = nfcCharacter.nextAdventureMissionStage.toInt(),
unlocked = nfcCharacter.nextAdventureMissionStage.toInt() > cardData.stageCount unlocked = nfcCharacter.nextAdventureMissionStage.toInt() > cardData.stageCount
) )
@ -107,23 +112,6 @@ class FromNfcConverter (
private fun updateCardProgress(
cardData: Card,
nfcCharacter: NfcCharacter,
) {
val cardProgress = CardProgress(
cardId = cardData.cardId,
currentStage = nfcCharacter.nextAdventureMissionStage.toInt(),
unlocked = nfcCharacter.nextAdventureMissionStage.toInt() > cardData.stageCount
)
database
.cardProgressDao()
.updateDimProgress(cardProgress)
}
private fun addVbCharacterToDatabase( private fun addVbCharacterToDatabase(
characterId: Long, characterId: Long,
nfcCharacter: VBNfcCharacter nfcCharacter: VBNfcCharacter
@ -139,8 +127,6 @@ class FromNfcConverter (
.insertVBCharacterData(extraCharacterData) .insertVBCharacterData(extraCharacterData)
addSpecialMissionsToDatabase(nfcCharacter, characterId) addSpecialMissionsToDatabase(nfcCharacter, characterId)
addVitalsHistoryToDatabase(characterId, nfcCharacter)
} }
@ -212,23 +198,20 @@ class FromNfcConverter (
nfcCharacter: NfcCharacter nfcCharacter: NfcCharacter
) { ) {
val vitalsHistoryWatch = nfcCharacter.vitalHistory val vitalsHistoryWatch = nfcCharacter.vitalHistory
vitalsHistoryWatch.map { item -> val vitalsHistory = vitalsHistoryWatch.map { historyElement ->
val date = GregorianCalendar( Log.d("VitalsHistory", "${historyElement.year.toInt()} ${historyElement.month.toInt()} ${historyElement.day.toInt()}")
item.year.toInt(), VitalsHistory(
item.month.toInt(), charId = characterId,
item.day.toInt() year = historyElement.year.toInt(),
month = historyElement.month.toInt(),
day = historyElement.day.toInt(),
vitalPoints = historyElement.vitalsGained.toInt()
) )
.time
.time
database
.characterDao()
.insertVitals(
characterId,
date,
item.vitalsGained.toInt()
)
} }
database
.userCharacterDao()
.insertVitals(*vitalsHistory.toTypedArray())
} }
@ -249,7 +232,7 @@ class FromNfcConverter (
.time .time
database database
.characterDao() .userCharacterDao()
.insertTransformation( .insertTransformation(
characterId, characterId,
item.toCharIndex.toInt(), item.toCharIndex.toInt(),

View File

@ -1,6 +1,7 @@
package com.github.nacabaro.vbhelper.screens.scanScreen.converters package com.github.nacabaro.vbhelper.screens.scanScreen.converters
import android.icu.util.Calendar import android.icu.util.Calendar
import android.util.Log
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import com.github.cfogrady.vbnfc.be.BENfcCharacter import com.github.cfogrady.vbnfc.be.BENfcCharacter
import com.github.cfogrady.vbnfc.be.FirmwareVersion import com.github.cfogrady.vbnfc.be.FirmwareVersion
@ -81,9 +82,7 @@ class ToNfcConverter(
activityLevel = userCharacter.activityLevel.toByte(), activityLevel = userCharacter.activityLevel.toByte(),
heartRateCurrent = userCharacter.heartRateCurrent.toUByte(), heartRateCurrent = userCharacter.heartRateCurrent.toUByte(),
transformationHistory = paddedTransformationArray, transformationHistory = paddedTransformationArray,
vitalHistory = Array(7) { vitalHistory = generateVitalsHistoryArray(characterId),
NfcCharacter.DailyVitals(0u, 0u, 0u, 0u)
},
appReserved1 = ByteArray(12) {0}, appReserved1 = ByteArray(12) {0},
appReserved2 = Array(3) {0u}, appReserved2 = Array(3) {0u},
generation = vbData.generation.toUShort(), generation = vbData.generation.toUShort(),
@ -120,6 +119,39 @@ class ToNfcConverter(
private suspend fun generateVitalsHistoryArray(
characterId: Long
): Array<NfcCharacter.DailyVitals> {
val vitalsHistory = database
.userCharacterDao()
.getVitalsHistory(characterId)
val nfcVitalsHistory = Array(7) {
NfcCharacter.DailyVitals(0u, 0u, 0u, 0u)
}
vitalsHistory.mapIndexed { index, historyElement ->
var actualYear = 0
if (historyElement.year != 2000) {
actualYear = historyElement.year
}
nfcVitalsHistory[index] = NfcCharacter.DailyVitals(
day = historyElement.day.toUByte(),
month = historyElement.month.toUByte(),
year = actualYear.toUShort(),
vitalsGained = vitalsHistory[index].vitalPoints.toUShort()
)
}
nfcVitalsHistory.map {
Log.d("NFC", it.toString())
}
return nfcVitalsHistory
}
private suspend fun nfcToBENfc( private suspend fun nfcToBENfc(
characterId: Long, characterId: Long,
characterInfo: CharacterDtos.CardCharacterInfo, characterInfo: CharacterDtos.CardCharacterInfo,
@ -157,9 +189,7 @@ class ToNfcConverter(
activityLevel = userCharacter.activityLevel.toByte(), activityLevel = userCharacter.activityLevel.toByte(),
heartRateCurrent = userCharacter.heartRateCurrent.toUByte(), heartRateCurrent = userCharacter.heartRateCurrent.toUByte(),
transformationHistory = paddedTransformationArray, transformationHistory = paddedTransformationArray,
vitalHistory = Array(7) { vitalHistory = generateVitalsHistoryArray(characterId),
NfcCharacter.DailyVitals(0u, 0u, 0u, 0u)
},
appReserved1 = ByteArray(12) {0}, appReserved1 = ByteArray(12) {0},
appReserved2 = Array(3) {0u}, appReserved2 = Array(3) {0u},
trainingHp = beData.trainingHp.toUShort(), trainingHp = beData.trainingHp.toUShort(),

View File

@ -132,7 +132,7 @@ class SettingsScreenControllerImpl(
.insertNewDim(cardModel) .insertNewDim(cardModel)
val cardProgress = CardProgress( val cardProgress = CardProgress(
cardId = cardModel.cardId, cardId = dimId,
currentStage = 0, currentStage = 0,
unlocked = false unlocked = false
) )