~cytrogen/vbhelper

47362028380d0df1fe92f5cad8e4a4203787bcba — nacabaro 1 year, 3 months ago aa0f424 + 08e3b84
Merge pull request #2 from nacabaro/database/initialise

Database/initialise
23 files changed, 523 insertions(+), 19 deletions(-)

M app/build.gradle.kts
M app/src/main/AndroidManifest.xml
M app/src/main/java/com/github/nacabaro/vbhelper/MainActivity.kt
A app/src/main/java/com/github/nacabaro/vbhelper/daos/DiMDao.kt
A app/src/main/java/com/github/nacabaro/vbhelper/daos/DiMProgressDao.kt
A app/src/main/java/com/github/nacabaro/vbhelper/daos/UserMonstersDao.kt
A app/src/main/java/com/github/nacabaro/vbhelper/daos/UserMonstersTransformationHistoryDao.kt
A app/src/main/java/com/github/nacabaro/vbhelper/database/AppDatabase.kt
A app/src/main/java/com/github/nacabaro/vbhelper/di/AppContainer.kt
A app/src/main/java/com/github/nacabaro/vbhelper/di/DefaultAppContainer.kt
A app/src/main/java/com/github/nacabaro/vbhelper/di/VBHelper.kt
A app/src/main/java/com/github/nacabaro/vbhelper/dtos/MonsterDataCombined.kt
M app/src/main/java/com/github/nacabaro/vbhelper/navigation/AppNavigation.kt
M app/src/main/java/com/github/nacabaro/vbhelper/screens/ScanScreen.kt
M app/src/main/java/com/github/nacabaro/vbhelper/screens/StorageScreen.kt
A app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/ReadingCharacter.kt
A app/src/main/java/com/github/nacabaro/vbhelper/source/StorageRepository.kt
A app/src/main/java/com/github/nacabaro/vbhelper/temporary_daos/TemporaryMonsterDao.kt
A app/src/main/java/com/github/nacabaro/vbhelper/temporary_domain/TemporaryBECharacterData.kt
A app/src/main/java/com/github/nacabaro/vbhelper/temporary_domain/TemporaryCharacterData.kt
A app/src/main/java/com/github/nacabaro/vbhelper/temporary_domain/TemporaryTransformationHistory.kt
A app/src/main/java/com/github/nacabaro/vbhelper/vm/StorageViewModel.kt
M vb-nfc-reader/src/main/java/com/github/cfogrady/vbnfc/TagCommunicator.kt
M app/build.gradle.kts => app/build.gradle.kts +1 -0
@@ 46,6 46,7 @@ dependencies {
    ksp(libs.androidx.room.compiler)
    annotationProcessor(libs.androidx.room.compiler)
    implementation(libs.androidx.core.ktx)
    implementation("androidx.room:room-ktx:2.6.1")
    implementation(libs.androidx.lifecycle.runtime.ktx)
    implementation(libs.androidx.activity.compose)
    implementation(platform(libs.androidx.compose.bom))

M app/src/main/AndroidManifest.xml => app/src/main/AndroidManifest.xml +1 -0
@@ 6,6 6,7 @@
    <uses-feature android:name="android.hardware.nfc" android:required="true" />

    <application
        android:name=".di.VBHelper"
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"

M app/src/main/java/com/github/nacabaro/vbhelper/MainActivity.kt => app/src/main/java/com/github/nacabaro/vbhelper/MainActivity.kt +128 -7
@@ 10,16 10,32 @@ import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.platform.LocalContext
import com.github.nacabaro.vbhelper.navigation.AppNavigation
import com.github.cfogrady.vbnfc.CryptographicTransformer
import com.github.cfogrady.vbnfc.R
import com.github.cfogrady.vbnfc.TagCommunicator
import com.github.cfogrady.vbnfc.be.BENfcCharacter
import com.github.cfogrady.vbnfc.data.DeviceType
import com.github.nacabaro.vbhelper.navigation.AppNavigation
import com.github.cfogrady.vbnfc.data.NfcCharacter
import com.github.nacabaro.vbhelper.di.VBHelper
import com.github.nacabaro.vbhelper.temporary_domain.TemporaryBECharacterData
import com.github.nacabaro.vbhelper.temporary_domain.TemporaryCharacterData
import com.github.nacabaro.vbhelper.temporary_domain.TemporaryTransformationHistory
import com.github.nacabaro.vbhelper.ui.theme.VBHelperTheme
import kotlinx.coroutines.flow.MutableStateFlow

class MainActivity : ComponentActivity() {
    private lateinit var nfcAdapter: NfcAdapter
    private lateinit var deviceToCryptographicTransformers: Map<UShort, CryptographicTransformer>

    private var nfcCharacter = MutableStateFlow<NfcCharacter?>(null)

    // EXTRACTED DIRECTLY FROM EXAMPLE APP
    override fun onCreate(savedInstanceState: Bundle?) {
        deviceToCryptographicTransformers = getMapOfCryptographicTransformers()


@@ 32,24 48,45 @@ class MainActivity : ComponentActivity() {
        }
        nfcAdapter = maybeNfcAdapter


        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        setContent {
            VBHelperTheme {
                AppNavigation()
                MainApplication()
            }
        }
    }

    @Composable
    private fun MainApplication() {
        var isDoneReadingCharacter by remember { mutableStateOf(false) }
        val application = LocalContext.current.applicationContext as VBHelper
        val storageRepository = application.container.db
        AppNavigation(
            isDoneReadingCharacter = isDoneReadingCharacter,
            onClickRead = {
                handleTag {
                    val character = it.receiveCharacter()
                    nfcCharacter.value = character
                    addCharacterScannedIntoDatabase()
                    isDoneReadingCharacter = true
                    "Done reading character"
                }
            },
            onClickScan = {
                isDoneReadingCharacter = false
            }
        )
    }

    // EXTRACTED DIRECTLY FROM EXAMPLE APP
    private fun getMapOfCryptographicTransformers(): Map<UShort, CryptographicTransformer> {
        return mapOf(
            Pair(DeviceType.VitalBraceletBEDeviceType,
                CryptographicTransformer(readableHmacKey1 = resources.getString(com.github.cfogrady.vbnfc.R.string.password1),
                    readableHmacKey2 = resources.getString(com.github.cfogrady.vbnfc.R.string.password2),
                    aesKey = resources.getString(com.github.cfogrady.vbnfc.R.string.decryptionKey),
                    substitutionCipher = resources.getIntArray(com.github.cfogrady.vbnfc.R.array.substitutionArray))),
                CryptographicTransformer(readableHmacKey1 = resources.getString(R.string.password1),
                    readableHmacKey2 = resources.getString(R.string.password2),
                    aesKey = resources.getString(R.string.decryptionKey),
                    substitutionCipher = resources.getIntArray(R.array.substitutionArray))),
//            Pair(DeviceType.VitalSeriesDeviceType,
//                CryptographicTransformer(hmacKey1 = resources.getString(R.string.password1),
//                    hmacKey2 = resources.getString(R.string.password2),


@@ 110,4 147,88 @@ class MainActivity : ComponentActivity() {
            nfcAdapter.disableReaderMode(this)
        }
    }

    //
    /*
    TODO:
    - Make it able to detect the different model of watches
    - 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() {
        val beCharacter = nfcCharacter as MutableStateFlow<BENfcCharacter?>
        val temporaryCharacterData = TemporaryCharacterData(
            dimId = nfcCharacter.value!!.dimId.toInt(),
            charIndex = nfcCharacter.value!!.charIndex.toInt(),
            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!!.transformationCountdown.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()
        )

        val application = applicationContext as VBHelper
        val storageRepository = application.container.db
        val characterId = storageRepository
            .temporaryMonsterDao()
            .insertCharacterData(temporaryCharacterData)

        val temporaryBECharacterData = TemporaryBECharacterData(
            id = characterId,
            trainingHp = beCharacter.value!!.trainingHp.toInt(),
            trainingAp = beCharacter.value!!.trainingAp.toInt(),
            trainingBp = beCharacter.value!!.trainingBp.toInt(),
            remainingTrainingTimeInMinutes = beCharacter.value!!.remainingTrainingTimeInMinutes.toInt(),
            itemEffectActivityLevelValue = beCharacter.value!!.itemEffectActivityLevelValue.toInt(),
            itemEffectMentalStateValue = beCharacter.value!!.itemEffectMentalStateValue.toInt(),
            itemEffectMentalStateMinutesRemaining = beCharacter.value!!.itemEffectMentalStateMinutesRemaining.toInt(),
            itemEffectActivityLevelMinutesRemaining = beCharacter.value!!.itemEffectActivityLevelMinutesRemaining.toInt(),
            itemEffectVitalPointsChangeValue = beCharacter.value!!.itemEffectVitalPointsChangeValue.toInt(),
            itemEffectVitalPointsChangeMinutesRemaining = beCharacter.value!!.itemEffectVitalPointsChangeMinutesRemaining.toInt(),
            abilityRarity = beCharacter.value!!.abilityRarity,
            abilityType = beCharacter.value!!.abilityType.toInt(),
            abilityBranch = beCharacter.value!!.abilityBranch.toInt(),
            abilityReset = beCharacter.value!!.abilityReset.toInt(),
            rank = beCharacter.value!!.abilityReset.toInt(),
            itemType = beCharacter.value!!.itemType.toInt(),
            itemMultiplier = beCharacter.value!!.itemMultiplier.toInt(),
            itemRemainingTime = beCharacter.value!!.itemRemainingTime.toInt(),
            otp0 = "", //beCharacter.value!!.otp0.toString(),
            otp1 = "", //beCharacter.value!!.otp1.toString(),
            minorVersion = beCharacter.value!!.characterCreationFirmwareVersion.minorVersion.toInt(),
            majorVersion = beCharacter.value!!.characterCreationFirmwareVersion.majorVersion.toInt(),
        )

        storageRepository
            .temporaryMonsterDao()
            .insertBECharacterData(temporaryBECharacterData)

        val transformationHistoryWatch = beCharacter.value!!.transformationHistory
        val domainTransformationHistory = transformationHistoryWatch.map { item ->
            TemporaryTransformationHistory(
                monId = characterId,
                toCharIndex = item.toCharIndex.toInt(),
                yearsSince1988 = item.yearsSince1988.toInt(),
                month = item.month.toInt(),
                day = item.day.toInt()
            )
        }

        storageRepository
            .temporaryMonsterDao()
            .insertTransformationHistory(*domainTransformationHistory.toTypedArray())
    }
}

A app/src/main/java/com/github/nacabaro/vbhelper/daos/DiMDao.kt => app/src/main/java/com/github/nacabaro/vbhelper/daos/DiMDao.kt +16 -0
@@ 0,0 1,16 @@
package com.github.nacabaro.vbhelper.daos

import androidx.room.Dao
import androidx.room.Insert
import androidx.room.Query
import com.github.nacabaro.vbhelper.domain.Dim
import kotlinx.coroutines.flow.Flow

@Dao
interface DiMDao {
    @Insert
    suspend fun insertNewDim(dim: Dim)

    @Query("SELECT * FROM Dim")
    fun getAllDims(): Flow<List<Dim>>
}
\ No newline at end of file

A app/src/main/java/com/github/nacabaro/vbhelper/daos/DiMProgressDao.kt => app/src/main/java/com/github/nacabaro/vbhelper/daos/DiMProgressDao.kt +11 -0
@@ 0,0 1,11 @@
package com.github.nacabaro.vbhelper.daos

import androidx.room.Dao
import androidx.room.Upsert
import com.github.nacabaro.vbhelper.domain.DimProgress

@Dao
interface DiMProgressDao {
    @Upsert
    suspend fun updateDimProgress(vararg dimProgress: DimProgress)
}
\ No newline at end of file

A app/src/main/java/com/github/nacabaro/vbhelper/daos/UserMonstersDao.kt => app/src/main/java/com/github/nacabaro/vbhelper/daos/UserMonstersDao.kt +15 -0
@@ 0,0 1,15 @@
package com.github.nacabaro.vbhelper.daos

import androidx.room.Dao
import androidx.room.Insert
import androidx.room.Query
import com.github.nacabaro.vbhelper.domain.UserMonsters

@Dao
interface UserMonstersDao {
    @Insert
    fun insertUserMonsters(userMonsters: UserMonsters)

    @Query("SELECT * FROM UserMonsters WHERE userId = :userId")
    fun getUserMonsters(userId: Int): List<UserMonsters>
}
\ No newline at end of file

A app/src/main/java/com/github/nacabaro/vbhelper/daos/UserMonstersTransformationHistoryDao.kt => app/src/main/java/com/github/nacabaro/vbhelper/daos/UserMonstersTransformationHistoryDao.kt +11 -0
@@ 0,0 1,11 @@
package com.github.nacabaro.vbhelper.daos

import androidx.room.Dao
import androidx.room.Insert
import com.github.nacabaro.vbhelper.temporary_domain.TemporaryTransformationHistory

@Dao
interface UserMonstersTransformationHistoryDao {
    @Insert
    fun insertTransformations(vararg transformations: TemporaryTransformationHistory)
}
\ No newline at end of file

A app/src/main/java/com/github/nacabaro/vbhelper/database/AppDatabase.kt => app/src/main/java/com/github/nacabaro/vbhelper/database/AppDatabase.kt +21 -0
@@ 0,0 1,21 @@
package com.github.nacabaro.vbhelper.database

import androidx.room.Database
import androidx.room.RoomDatabase
import com.github.nacabaro.vbhelper.temporary_daos.TemporaryMonsterDao
import com.github.nacabaro.vbhelper.temporary_domain.TemporaryBECharacterData
import com.github.nacabaro.vbhelper.temporary_domain.TemporaryCharacterData
import com.github.nacabaro.vbhelper.temporary_domain.TemporaryTransformationHistory

@Database(
    version = 1,
    entities = [
        TemporaryCharacterData::class,
        TemporaryBECharacterData::class,
        TemporaryTransformationHistory::class
    ]
)
abstract class AppDatabase : RoomDatabase() {
    abstract fun temporaryMonsterDao(): TemporaryMonsterDao

}
\ No newline at end of file

A app/src/main/java/com/github/nacabaro/vbhelper/di/AppContainer.kt => app/src/main/java/com/github/nacabaro/vbhelper/di/AppContainer.kt +7 -0
@@ 0,0 1,7 @@
package com.github.nacabaro.vbhelper.di

import com.github.nacabaro.vbhelper.database.AppDatabase

interface AppContainer {
    val db: AppDatabase
}
\ No newline at end of file

A app/src/main/java/com/github/nacabaro/vbhelper/di/DefaultAppContainer.kt => app/src/main/java/com/github/nacabaro/vbhelper/di/DefaultAppContainer.kt +14 -0
@@ 0,0 1,14 @@
import android.content.Context
import androidx.room.Room
import com.github.nacabaro.vbhelper.database.AppDatabase
import com.github.nacabaro.vbhelper.di.AppContainer

class DefaultAppContainer(private val context: Context) : AppContainer {
    override val db: AppDatabase by lazy {
        Room.databaseBuilder(
            context = context,
            klass = AppDatabase::class.java,
            "internalDb"
        ).build()
    }
}
\ No newline at end of file

A app/src/main/java/com/github/nacabaro/vbhelper/di/VBHelper.kt => app/src/main/java/com/github/nacabaro/vbhelper/di/VBHelper.kt +13 -0
@@ 0,0 1,13 @@
package com.github.nacabaro.vbhelper.di

import DefaultAppContainer
import android.app.Application

class VBHelper : Application() {
    lateinit var container: DefaultAppContainer

    override fun onCreate() {
        super.onCreate()
        container = DefaultAppContainer(applicationContext)
    }
}
\ No newline at end of file

A app/src/main/java/com/github/nacabaro/vbhelper/dtos/MonsterDataCombined.kt => app/src/main/java/com/github/nacabaro/vbhelper/dtos/MonsterDataCombined.kt +4 -0
@@ 0,0 1,4 @@
package com.github.nacabaro.vbhelper.dtos

class MonsterDataCombined {
}
\ No newline at end of file

M app/src/main/java/com/github/nacabaro/vbhelper/navigation/AppNavigation.kt => app/src/main/java/com/github/nacabaro/vbhelper/navigation/AppNavigation.kt +11 -2
@@ 14,7 14,11 @@ import com.github.nacabaro.vbhelper.screens.ScanScreen
import com.github.nacabaro.vbhelper.screens.StorageScreen

@Composable
fun AppNavigation() {
fun AppNavigation(
    onClickRead: () -> Unit,
    onClickScan: () -> Unit,
    isDoneReadingCharacter: Boolean
) {
    val navController = rememberNavController()

    Scaffold(


@@ 38,7 42,12 @@ fun AppNavigation() {
                StorageScreen()
            }
            composable(BottomNavItem.Scan.route) {
                ScanScreen()
                onClickScan()
                ScanScreen(
                    navController = navController,
                    onClickRead = onClickRead,
                    isDoneReadingCharacter = isDoneReadingCharacter
                )
            }
            composable(BottomNavItem.Dex.route) {
                DexScreen()

M app/src/main/java/com/github/nacabaro/vbhelper/screens/ScanScreen.kt => app/src/main/java/com/github/nacabaro/vbhelper/screens/ScanScreen.kt +45 -5
@@ 10,17 10,52 @@ import androidx.compose.material3.Button
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.navigation.NavController
import androidx.navigation.compose.rememberNavController
import com.github.nacabaro.vbhelper.components.TopBanner
import com.github.nacabaro.vbhelper.navigation.BottomNavItem
import com.github.nacabaro.vbhelper.screens.scanScreen.ReadingCharacterScreen

@Composable
fun ScanScreen() {
    Scaffold (
    topBar = { TopBanner(text = "Scan a Vital Bracelet") }
fun ScanScreen(
    navController: NavController,
    onClickRead: () -> Unit,
    isDoneReadingCharacter: Boolean
) {
    var readingScreen by remember { mutableStateOf(false) }

    if (isDoneReadingCharacter) {
        readingScreen = false
        navController.navigate(BottomNavItem.Home.route)
    }

    if (readingScreen) {
        ReadingCharacterScreen { readingScreen = false }
    } else {
        ChooseConnectOption(
            onClickRead = {
                readingScreen = true
                onClickRead()
            },
        )
    }
}

@Composable
private fun ChooseConnectOption(
    onClickRead: () -> Unit,
) {
    Scaffold(
        topBar = { TopBanner(text = "Scan a Vital Bracelet") }
    ) { contentPadding ->
        Column(
            verticalArrangement = Arrangement.Center,


@@ 31,7 66,7 @@ fun ScanScreen() {
        ) {
            ScanButton(
                text = "Vital Bracelet to App",
                onClick = {}
                onClick = onClickRead
            )
            Spacer(modifier = Modifier.height(16.dp))
            ScanButton(


@@ 42,6 77,7 @@ fun ScanScreen() {
    }
}


@Composable
fun ScanButton(
    text: String,


@@ 64,5 100,9 @@ fun ScanButton(
@Preview(showBackground = true)
@Composable
fun ScanScreenPreview() {
    ScanScreen()
    ScanScreen(
        navController = rememberNavController(),
        onClickRead = {  },
        isDoneReadingCharacter = false
    )
}
\ No newline at end of file

M app/src/main/java/com/github/nacabaro/vbhelper/screens/StorageScreen.kt => app/src/main/java/com/github/nacabaro/vbhelper/screens/StorageScreen.kt +44 -3
@@ 1,5 1,6 @@
package com.github.nacabaro.vbhelper.screens

import android.util.Log
import androidx.compose.foundation.Image
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.gestures.scrollable


@@ 9,35 10,75 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.items
import androidx.compose.foundation.rememberScrollState
import androidx.compose.material3.Card
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import com.github.nacabaro.vbhelper.R
import com.github.nacabaro.vbhelper.components.TopBanner
import com.github.nacabaro.vbhelper.di.VBHelper
import com.github.nacabaro.vbhelper.source.StorageRepository
import com.github.nacabaro.vbhelper.temporary_domain.TemporaryCharacterData
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch


@Composable
fun StorageScreen() {
    val coroutineScope = rememberCoroutineScope()
    val application = LocalContext.current.applicationContext as VBHelper
    val storageRepository = StorageRepository(application.container.db)
    val monList = remember { mutableStateListOf<TemporaryCharacterData>() }


    LaunchedEffect(storageRepository) {
        coroutineScope.launch {
            monList.clear()
            monList.addAll(storageRepository.getAllCharacters())
            Log.d("StorageScreen", "Updated data: $monList")
        }
    }

    Log.d("StorageScreen", "monList: $monList")

    Scaffold (
        topBar = { TopBanner(text = "My Digimon") }
    ) { contentPadding ->
        if (monList.isEmpty()) {
            Text(
                text = "Nothing to see here",
                modifier = Modifier
                    .padding(8.dp)
            )
        }

        LazyVerticalGrid(
            columns = GridCells.Fixed(3),
            modifier = Modifier
                .scrollable(state = rememberScrollState(), orientation = Orientation.Vertical)
                .padding(top = contentPadding.calculateTopPadding())
        ) {
            items(100) { i ->
            items(monList) { index ->
                StorageEntry(
                    name = "Digimon $i",
                    icon = R.drawable.baseline_question_mark_24
                    name = index.dimId.toString() + " - " + index.charIndex.toString(),
                    icon = R.drawable.ic_launcher_foreground,
                    modifier = Modifier
                        .padding(8.dp)
                )
            }
        }

A app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/ReadingCharacter.kt => app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/ReadingCharacter.kt +41 -0
@@ 0,0 1,41 @@
package com.github.nacabaro.vbhelper.screens.scanScreen

import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.github.nacabaro.vbhelper.components.TopBanner

@Composable
fun ReadingCharacterScreen(
    onClickCancel: () -> Unit,
) {
    Scaffold (
        topBar = {
            TopBanner("Reading Character")
        }
    ) { innerPadding ->
        Column (
            horizontalAlignment = Alignment.CenterHorizontally,
            verticalArrangement = Arrangement.Center,
            modifier = Modifier
                .padding(innerPadding)
                .fillMaxSize()
        ) {
            Text("Place your Vital Bracelet near the reader...")
            Button(
                onClick = onClickCancel,
                modifier = Modifier.padding(16.dp)
            ) {
                Text("Cancel")
            }
        }
    }
}
\ No newline at end of file

A app/src/main/java/com/github/nacabaro/vbhelper/source/StorageRepository.kt => app/src/main/java/com/github/nacabaro/vbhelper/source/StorageRepository.kt +12 -0
@@ 0,0 1,12 @@
package com.github.nacabaro.vbhelper.source

import com.github.nacabaro.vbhelper.database.AppDatabase
import com.github.nacabaro.vbhelper.temporary_domain.TemporaryCharacterData

class StorageRepository (
    private val db: AppDatabase
) {
    suspend fun getAllCharacters(): List<TemporaryCharacterData> {
        return db.temporaryMonsterDao().getAllCharacters()
    }
}
\ No newline at end of file

A app/src/main/java/com/github/nacabaro/vbhelper/temporary_daos/TemporaryMonsterDao.kt => app/src/main/java/com/github/nacabaro/vbhelper/temporary_daos/TemporaryMonsterDao.kt +27 -0
@@ 0,0 1,27 @@
package com.github.nacabaro.vbhelper.temporary_daos

import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import com.github.nacabaro.vbhelper.temporary_domain.TemporaryBECharacterData
import com.github.nacabaro.vbhelper.temporary_domain.TemporaryCharacterData
import com.github.nacabaro.vbhelper.temporary_domain.TemporaryTransformationHistory

@Dao
interface TemporaryMonsterDao {
    @Insert
    fun insertCharacterData(temporaryCharacterData: TemporaryCharacterData): Int

    @Insert
    fun insertBECharacterData(temporaryBECharacterData: TemporaryBECharacterData)

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    fun insertTransformationHistory(vararg transformationHistory: TemporaryTransformationHistory)

    @Query("SELECT * FROM TemporaryTransformationHistory WHERE monId = :monId")
    fun getTransformationHistory(monId: Int): List<TemporaryTransformationHistory>

    @Query("SELECT * FROM TemporaryCharacterData")
    suspend fun getAllCharacters(): List<TemporaryCharacterData>
}
\ No newline at end of file

A app/src/main/java/com/github/nacabaro/vbhelper/temporary_domain/TemporaryBECharacterData.kt => app/src/main/java/com/github/nacabaro/vbhelper/temporary_domain/TemporaryBECharacterData.kt +44 -0
@@ 0,0 1,44 @@
package com.github.nacabaro.vbhelper.temporary_domain

import androidx.room.Entity
import androidx.room.ForeignKey
import androidx.room.PrimaryKey
import com.github.cfogrady.vbnfc.be.BENfcCharacter
import com.github.cfogrady.vbnfc.be.FirmwareVersion
import com.github.cfogrady.vbnfc.data.NfcCharacter

@Entity(
    foreignKeys = [
        ForeignKey(
            entity = TemporaryCharacterData::class,
            parentColumns = ["id"],
            childColumns = ["id"],
            onDelete = ForeignKey.CASCADE
        )
    ]
)
data class TemporaryBECharacterData (
    @PrimaryKey(autoGenerate = true) val id: Int,
    val trainingHp: Int,
    val trainingAp: Int,
    val trainingBp: Int,
    val remainingTrainingTimeInMinutes: Int,
    val itemEffectMentalStateValue: Int,
    val itemEffectMentalStateMinutesRemaining: Int,
    val itemEffectActivityLevelValue: Int,
    val itemEffectActivityLevelMinutesRemaining: Int,
    val itemEffectVitalPointsChangeValue: Int,
    val itemEffectVitalPointsChangeMinutesRemaining: Int,
    val abilityRarity: NfcCharacter.AbilityRarity,
    val abilityType: Int,
    val abilityBranch: Int,
    val abilityReset: Int,
    val rank: Int,
    val itemType: Int,
    val itemMultiplier: Int,
    val itemRemainingTime: Int,
    val otp0: String,
    val otp1: String,
    val minorVersion: Int,
    val majorVersion: Int,
)
\ No newline at end of file

A app/src/main/java/com/github/nacabaro/vbhelper/temporary_domain/TemporaryCharacterData.kt => app/src/main/java/com/github/nacabaro/vbhelper/temporary_domain/TemporaryCharacterData.kt +27 -0
@@ 0,0 1,27 @@
package com.github.nacabaro.vbhelper.temporary_domain

import androidx.room.Entity
import androidx.room.PrimaryKey
import com.github.cfogrady.vbnfc.data.NfcCharacter

@Entity
data class TemporaryCharacterData (
    @PrimaryKey(autoGenerate = true) val id: Int = 0,
    val dimId: Int,
    var charIndex: Int,
    var stage: Int,
    var attribute: NfcCharacter.Attribute,
    var ageInDays: Int,
    var nextAdventureMissionStage: Int, // next adventure mission stage on the character's dim
    var mood: Int,
    var vitalPoints: Int,
    var transformationCountdown: Int,
    var injuryStatus: NfcCharacter.InjuryStatus,
    var trophies: Int,
    var currentPhaseBattlesWon: Int,
    var currentPhaseBattlesLost: Int,
    var totalBattlesWon: Int,
    var totalBattlesLost: Int,
    var activityLevel: Int,
    var heartRateCurrent: Int,
)
\ No newline at end of file

A app/src/main/java/com/github/nacabaro/vbhelper/temporary_domain/TemporaryTransformationHistory.kt => app/src/main/java/com/github/nacabaro/vbhelper/temporary_domain/TemporaryTransformationHistory.kt +25 -0
@@ 0,0 1,25 @@
package com.github.nacabaro.vbhelper.temporary_domain

import androidx.room.Entity
import androidx.room.ForeignKey
import androidx.room.PrimaryKey

@Entity(
    foreignKeys = [
        ForeignKey(
            entity = TemporaryCharacterData::class,
            parentColumns = ["id"],
            childColumns = ["monId"],
            onDelete = ForeignKey.CASCADE
        )
    ]
)
// Bit lazy, will correct later...
data class TemporaryTransformationHistory (
    @PrimaryKey(autoGenerate = true) val id: Int = 0,
    val monId: Int,
    val toCharIndex: Int,
    val yearsSince1988: Int,
    val month: Int,
    val day: Int
)

A app/src/main/java/com/github/nacabaro/vbhelper/vm/StorageViewModel.kt => app/src/main/java/com/github/nacabaro/vbhelper/vm/StorageViewModel.kt +2 -0
@@ 0,0 1,2 @@
package com.github.nacabaro.vbhelper.vm


M vb-nfc-reader/src/main/java/com/github/cfogrady/vbnfc/TagCommunicator.kt => vb-nfc-reader/src/main/java/com/github/cfogrady/vbnfc/TagCommunicator.kt +3 -2
@@ 69,8 69,9 @@ class TagCommunicator(
        checksumCalculator.checkChecksums(decryptedCharacterData)
        val nfcCharacter = translator.parseNfcCharacter(decryptedCharacterData)
        Log.i(TAG, "Known Character Stats: $nfcCharacter")
        Log.i(TAG, "Signaling operation complete")
        nfcData.transceive(translator.getOperationCommandBytes(header, OPERATION_TRANSFERRED_TO_APP))
        // Not ready to lose any of my mons in this...
        //Log.i(TAG, "Signaling operation complete")
        //nfcData.transceive(translator.getOperationCommandBytes(header, OPERATION_TRANSFERRED_TO_APP))
        return nfcCharacter
    }