~cytrogen/vbhelper

e4366ce15c05b847d70edfeb3cd47e2e710ac4c6 — nacabaro 1 year, 3 months ago 5edd753 + 7582f94
Merge pull request #8 from nacabaro/nfc/upload_mon_new_model

Nfc/upload mon new model
24 files changed, 333 insertions(+), 304 deletions(-)

M app/src/main/java/com/github/nacabaro/vbhelper/MainActivity.kt
M app/src/main/java/com/github/nacabaro/vbhelper/components/CharacterEntry.kt
R app/src/main/java/com/github/nacabaro/vbhelper/components/{StorageEntry => DexDimEntry}.kt
M app/src/main/java/com/github/nacabaro/vbhelper/daos/CharacterDao.kt
M app/src/main/java/com/github/nacabaro/vbhelper/daos/DiMDao.kt
A app/src/main/java/com/github/nacabaro/vbhelper/daos/UserCharacterDao.kt
D app/src/main/java/com/github/nacabaro/vbhelper/daos/UserMonstersDao.kt
M app/src/main/java/com/github/nacabaro/vbhelper/database/AppDatabase.kt
M app/src/main/java/com/github/nacabaro/vbhelper/domain/Character.kt
A app/src/main/java/com/github/nacabaro/vbhelper/domain/DeviceType.kt
M app/src/main/java/com/github/nacabaro/vbhelper/domain/Dim.kt
R app/src/main/java/com/github/nacabaro/vbhelper/{temporary_domain/TemporaryBECharacterData => domain/device_data/BECharacterData}.kt
R app/src/main/java/com/github/nacabaro/vbhelper/{temporary_domain/TemporaryTransformationHistory => domain/device_data/TransformationHistory}.kt
A app/src/main/java/com/github/nacabaro/vbhelper/domain/device_data/UserCharacter.kt
A app/src/main/java/com/github/nacabaro/vbhelper/domain/device_data/VBCharacterData.kt
R app/src/main/java/com/github/nacabaro/vbhelper/{temporary_domain/TemporaryCharacterData => dtos/CharacterDtos}.kt
D app/src/main/java/com/github/nacabaro/vbhelper/dtos/MonsterDataCombined.kt
M app/src/main/java/com/github/nacabaro/vbhelper/screens/DexScreen.kt
M app/src/main/java/com/github/nacabaro/vbhelper/screens/DimScreen.kt
M app/src/main/java/com/github/nacabaro/vbhelper/screens/StorageScreen.kt
M app/src/main/java/com/github/nacabaro/vbhelper/source/StorageRepository.kt
D app/src/main/java/com/github/nacabaro/vbhelper/temporary_daos/TemporaryMonsterDao.kt
A app/src/main/java/com/github/nacabaro/vbhelper/utils/BitmapData.kt
D app/src/main/java/com/github/nacabaro/vbhelper/vm/StorageViewModel.kt
M app/src/main/java/com/github/nacabaro/vbhelper/MainActivity.kt => app/src/main/java/com/github/nacabaro/vbhelper/MainActivity.kt +92 -79
@@ 29,8 29,10 @@ import com.github.nacabaro.vbhelper.di.VBHelper
import com.github.nacabaro.vbhelper.domain.Dim
import com.github.nacabaro.vbhelper.domain.Sprites
import com.github.nacabaro.vbhelper.domain.Character
import com.github.nacabaro.vbhelper.domain.device_data.BECharacterData
import com.github.nacabaro.vbhelper.domain.device_data.TransformationHistory
import com.github.nacabaro.vbhelper.domain.device_data.UserCharacter
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


@@ 72,6 74,9 @@ class MainActivity : ComponentActivity() {
            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()
                }


@@ 81,44 86,27 @@ class MainActivity : ComponentActivity() {
                    val dimReader = DimReader()
                    val card = dimReader.readCard(fileReader, false)
                    val dimModel = Dim(
                        id = card.header.dimId,
                        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
                    )
                    val application = applicationContext as VBHelper
                    val storageRepository = application.container.db
                    storageRepository.dimDao().insertNewDim(dimModel)

                    val characters = card.characterStats.characterEntries
                    val dimId = storageRepository
                        .dimDao()
                        .insertNewDim(dimModel)

                    /*
                    Confusing math for me ahead
                    sprite[0] logo
                    sprite[10] name
                    sprite[10 + 1] character_1
                    sprite[10 + 2] character_2
                    sprite[16] name 1
                    sprite[17] character_1
                    sprite[18] character_2
                    sprite[23] name 2
                    sprite[24] character_1
                    sprite[25] character_2
                    sprite[23 + 12 + 1] name 3
                    sprite[23 + 12 + 2] character_1
                    sprite[23 + 12 + 3] character_2
                     */
                    val characters = card.characterStats.characterEntries

                    var spriteCounter = 10
                    var domainCharacters = mutableListOf<Character>()
                    val domainCharacters = mutableListOf<Character>()

                    for (index in 0 until characters.size) {
                        domainCharacters.add(
                            Character(
                                id = 0,
                                dimId = card.header.dimId,
                                dimId = dimId,
                                monIndex = index,
                                name = card.spriteData.sprites[spriteCounter].pixelData,
                                stage = characters[index].stage,


@@ 135,6 123,7 @@ class MainActivity : ComponentActivity() {
                            )
                        )

                        // TODO: Improve this
                        if (index == 0) {
                            spriteCounter += 6
                        } else if (index == 1) {


@@ 176,9 165,12 @@ class MainActivity : ComponentActivity() {
                handleTag {
                    val character = it.receiveCharacter()
                    nfcCharacter.value = character
                    addCharacterScannedIntoDatabase()

                    val importStatus = addCharacterScannedIntoDatabase()

                    isDoneReadingCharacter = true
                    "Done reading character"

                    importStatus
                }
            },
            onClickScan = {


@@ 273,19 265,31 @@ class MainActivity : ComponentActivity() {
    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?>
    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 temporaryCharacterData = TemporaryCharacterData(
            dimId = nfcCharacter.value!!.dimId.toInt(),
            charIndex = nfcCharacter.value!!.charIndex.toInt(),
        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!!.transformationCountdown.toInt(),
            transformationCountdown = nfcCharacter.value!!.transformationCountdownInMinutes.toInt(),
            injuryStatus = nfcCharacter.value!!.injuryStatus,
            trophies = nfcCharacter.value!!.trophies.toInt(),
            currentPhaseBattlesWon = nfcCharacter.value!!.currentPhaseBattlesWon.toInt(),


@@ 293,58 297,67 @@ class MainActivity : ComponentActivity() {
            totalBattlesWon = nfcCharacter.value!!.totalBattlesWon.toInt(),
            totalBattlesLost = nfcCharacter.value!!.totalBattlesLost.toInt(),
            activityLevel = nfcCharacter.value!!.activityLevel.toInt(),
            heartRateCurrent = nfcCharacter.value!!.heartRateCurrent.toInt()
            heartRateCurrent = nfcCharacter.value!!.heartRateCurrent.toInt(),
            characterType = when (nfcCharacter.value) {
                is BENfcCharacter -> com.github.nacabaro.vbhelper.domain.DeviceType.BEDevice
                else -> com.github.nacabaro.vbhelper.domain.DeviceType.VBDevice
            }
        )

        val application = applicationContext as VBHelper
        val storageRepository = application.container.db
        val characterId: Long = 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(),
        )
            .userCharacterDao()
            .insertCharacterData(characterData)

        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()
        if (nfcCharacter.value is BENfcCharacter) {
            val beCharacter = nfcCharacter as MutableStateFlow<BENfcCharacter?>
            val extraCharacterData = BECharacterData(
                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
                .userCharacterDao()
                .insertBECharacterData(extraCharacterData)

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

            storageRepository
                .userCharacterDao()
                .insertTransformationHistory(*domainTransformationHistory.toTypedArray())
        } else {
            return "Not implemented yet"
        }

        storageRepository
            .temporaryMonsterDao()
            .insertTransformationHistory(*domainTransformationHistory.toTypedArray())
        return "Done reading character!"
    }
}

M app/src/main/java/com/github/nacabaro/vbhelper/components/CharacterEntry.kt => app/src/main/java/com/github/nacabaro/vbhelper/components/CharacterEntry.kt +14 -3
@@ 1,5 1,6 @@
package com.github.nacabaro.vbhelper.components

import android.graphics.Bitmap
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding


@@ 7,17 8,27 @@ import androidx.compose.foundation.layout.size
import androidx.compose.material3.Card
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.FilterQuality
import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.unit.dp
import com.github.nacabaro.vbhelper.utils.BitmapData
import java.nio.ByteBuffer

@Composable
fun CharacterEntry(
    icon: ImageBitmap,
    icon: BitmapData,
    modifier: Modifier = Modifier,
    onClick: () -> Unit = {  }
) {
    val bitmap = remember (icon.bitmap) {
        Bitmap.createBitmap(icon.width, icon.height, Bitmap.Config.RGB_565).apply {
            copyPixelsFromBuffer(ByteBuffer.wrap(icon.bitmap))
        }
    }
    val imageBitmap = remember(bitmap) { bitmap.asImageBitmap() }

    Card(
        shape = MaterialTheme.shapes.medium,
        onClick = onClick,


@@ 26,7 37,7 @@ fun CharacterEntry(
            .size(96.dp)
    ) {
        Image(
            bitmap = icon,
            bitmap = imageBitmap,
            contentDescription = "Icon",
            filterQuality = FilterQuality.None,
            modifier = Modifier

R app/src/main/java/com/github/nacabaro/vbhelper/components/StorageEntry.kt => app/src/main/java/com/github/nacabaro/vbhelper/components/DexDimEntry.kt +35 -37
@@ 1,64 1,62 @@
package com.github.nacabaro.vbhelper.components

import android.graphics.Bitmap
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.Card
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.graphics.FilterQuality
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.unit.dp
import com.github.nacabaro.vbhelper.utils.BitmapData
import java.nio.ByteBuffer

@Composable
fun StorageEntry(
fun DexDiMEntry(
    name: String,
    nameBitmap: ImageBitmap? = null,
    modifier: Modifier = Modifier,
    icon: Int? = null,
    bitmap: ImageBitmap? = null,
    onClick: () -> Unit = {  }
    logo: BitmapData,
    onClick: () -> Unit,
    modifier: Modifier = Modifier
) {
    Card(
    val bitmap = remember (logo.bitmap) {
        Bitmap.createBitmap(logo.width, logo.height, Bitmap.Config.RGB_565).apply {
            copyPixelsFromBuffer(ByteBuffer.wrap(logo.bitmap))
        }
    }
    val imageBitmap = remember(bitmap) { bitmap.asImageBitmap() }

    Card (
        shape = MaterialTheme.shapes.medium,
        onClick = onClick,
        modifier = modifier
            .padding(8.dp)
        modifier = modifier,
        onClick = onClick
    ) {
        Column(
            horizontalAlignment = Alignment.CenterHorizontally,
        Row (
            horizontalArrangement = Arrangement.Start,
            verticalAlignment = Alignment.CenterVertically,
            modifier = Modifier
                .fillMaxSize()
                .padding(8.dp)
        ) {
            if (bitmap != null) {
                Image(
                    bitmap = bitmap,
                    contentDescription = name,
                    modifier = Modifier
                        .padding(8.dp)
                        .size(64.dp)
                )
            } else if (icon != null) {
                Image(
                    painter = painterResource(id = icon),
                    contentDescription = name,
                    modifier = Modifier
                        .padding(8.dp)
                        .size(64.dp)
                )
            }
            Image (
                bitmap = imageBitmap,
                contentDescription = name,
                filterQuality = FilterQuality.None,
                modifier = Modifier
                    .padding(8.dp)
                    .size(64.dp)
            )
            Text(
                text = name,
                textAlign = TextAlign.Center,
                modifier = Modifier
                    .padding(8.dp)
            )
        }
    }
}
}
\ No newline at end of file

M app/src/main/java/com/github/nacabaro/vbhelper/daos/CharacterDao.kt => app/src/main/java/com/github/nacabaro/vbhelper/daos/CharacterDao.kt +3 -1
@@ 5,7 5,6 @@ import androidx.room.Insert
import androidx.room.Query
import com.github.nacabaro.vbhelper.domain.Character
import com.github.nacabaro.vbhelper.domain.Sprites
import org.w3c.dom.CharacterData

@Dao
interface CharacterDao {


@@ 18,6 17,9 @@ interface CharacterDao {
    @Query("SELECT * FROM Character WHERE dimId = :dimId")
    suspend fun getCharacterByDimId(dimId: Int): List<Character>

    @Query("SELECT * FROM Character WHERE monIndex = :monIndex AND dimId = :dimId LIMIT 1")
    fun getCharacterByMonIndex(monIndex: Int, dimId: Long): Character

    @Insert
    suspend fun insertSprite(vararg sprite: Sprites)


M app/src/main/java/com/github/nacabaro/vbhelper/daos/DiMDao.kt => app/src/main/java/com/github/nacabaro/vbhelper/daos/DiMDao.kt +4 -1
@@ 9,8 9,11 @@ import com.github.nacabaro.vbhelper.domain.Dim
@Dao
interface DiMDao {
    @Insert(onConflict = OnConflictStrategy.IGNORE)
    suspend fun insertNewDim(dim: Dim)
    suspend fun insertNewDim(dim: Dim): Long

    @Query("SELECT * FROM Dim")
    suspend fun getAllDims(): List<Dim>

    @Query("SELECT * FROM Dim WHERE dimId = :id")
    fun getDimById(id: Int): Dim?
}
\ No newline at end of file

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

import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import com.github.nacabaro.vbhelper.domain.device_data.UserCharacter
import com.github.nacabaro.vbhelper.domain.device_data.BECharacterData
import com.github.nacabaro.vbhelper.domain.device_data.TransformationHistory
import com.github.nacabaro.vbhelper.dtos.CharacterDtos

@Dao
interface UserCharacterDao {
    @Insert
    fun insertCharacterData(characterData: UserCharacter): Long

    @Insert
    fun insertBECharacterData(characterData: BECharacterData)

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

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

    @Query("""
        SELECT
            uc.*,
            c.sprite1 AS spriteIdle,
            c.spritesWidth AS spriteWidth,
            c.spritesHeight AS spriteHeight
        FROM UserCharacter uc
        JOIN Character c ON uc.charId = c.id
        """)
    suspend fun getAllCharacters(): List<CharacterDtos.CharacterWithSprites>

    @Query("SELECT * FROM UserCharacter WHERE id = :id")
    suspend fun getCharacter(id: Long): UserCharacter
}
\ No newline at end of file

D app/src/main/java/com/github/nacabaro/vbhelper/daos/UserMonstersDao.kt => app/src/main/java/com/github/nacabaro/vbhelper/daos/UserMonstersDao.kt +0 -15
@@ 1,15 0,0 @@
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

M app/src/main/java/com/github/nacabaro/vbhelper/database/AppDatabase.kt => app/src/main/java/com/github/nacabaro/vbhelper/database/AppDatabase.kt +9 -6
@@ 4,10 4,13 @@ import androidx.room.Database
import androidx.room.RoomDatabase
import com.github.nacabaro.vbhelper.daos.CharacterDao
import com.github.nacabaro.vbhelper.daos.DiMDao
import com.github.nacabaro.vbhelper.daos.UserCharacterDao
import com.github.nacabaro.vbhelper.domain.Character
import com.github.nacabaro.vbhelper.domain.Dim
import com.github.nacabaro.vbhelper.domain.Sprites
import com.github.nacabaro.vbhelper.temporary_daos.TemporaryMonsterDao
import com.github.nacabaro.vbhelper.domain.device_data.BECharacterData
import com.github.nacabaro.vbhelper.domain.device_data.TransformationHistory
import com.github.nacabaro.vbhelper.domain.device_data.UserCharacter
import com.github.nacabaro.vbhelper.temporary_domain.TemporaryBECharacterData
import com.github.nacabaro.vbhelper.temporary_domain.TemporaryCharacterData
import com.github.nacabaro.vbhelper.temporary_domain.TemporaryTransformationHistory


@@ 15,16 18,16 @@ import com.github.nacabaro.vbhelper.temporary_domain.TemporaryTransformationHist
@Database(
    version = 1,
    entities = [
        TemporaryCharacterData::class,
        TemporaryBECharacterData::class,
        TemporaryTransformationHistory::class,
        Dim::class,
        Character::class,
        Sprites::class
        Sprites::class,
        UserCharacter::class,
        BECharacterData::class,
        TransformationHistory::class
    ]
)
abstract class AppDatabase : RoomDatabase() {
    abstract fun temporaryMonsterDao(): TemporaryMonsterDao
    abstract fun dimDao(): DiMDao
    abstract fun characterDao(): CharacterDao
    abstract fun userCharacterDao(): UserCharacterDao
}
\ No newline at end of file

M app/src/main/java/com/github/nacabaro/vbhelper/domain/Character.kt => app/src/main/java/com/github/nacabaro/vbhelper/domain/Character.kt +3 -3
@@ 15,8 15,8 @@ import androidx.room.ForeignKey
    ]
)
data class Character (
    @PrimaryKey(autoGenerate = true) val id: Long,
    val dimId: Int,
    @PrimaryKey(autoGenerate = true) val id: Long = 0,
    val dimId: Long,
    val monIndex: Int,
    val name: ByteArray,
    val stage: Int, // These should be replaced with enums


@@ 30,4 30,4 @@ data class Character (
    val nameHeight: Int,
    val spritesWidth: Int,
    val spritesHeight: Int
)
\ No newline at end of file
)

A app/src/main/java/com/github/nacabaro/vbhelper/domain/DeviceType.kt => app/src/main/java/com/github/nacabaro/vbhelper/domain/DeviceType.kt +6 -0
@@ 0,0 1,6 @@
package com.github.nacabaro.vbhelper.domain

enum class DeviceType {
    VBDevice,
    BEDevice
}
\ No newline at end of file

M app/src/main/java/com/github/nacabaro/vbhelper/domain/Dim.kt => app/src/main/java/com/github/nacabaro/vbhelper/domain/Dim.kt +2 -1
@@ 7,7 7,8 @@ import androidx.room.PrimaryKey
@Entity
data class Dim(
    @PrimaryKey(autoGenerate = true)
    val id: Int,
    val id: Long = 0,
    val dimId: Int,
    val logo: ByteArray,
    val logoWidth: Int,
    val logoHeight: Int,

R app/src/main/java/com/github/nacabaro/vbhelper/temporary_domain/TemporaryBECharacterData.kt => app/src/main/java/com/github/nacabaro/vbhelper/domain/device_data/BECharacterData.kt +4 -5
@@ 1,23 1,22 @@
package com.github.nacabaro.vbhelper.temporary_domain
package com.github.nacabaro.vbhelper.domain.device_data

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
import com.github.nacabaro.vbhelper.temporary_domain.TemporaryCharacterData

@Entity(
    foreignKeys = [
        ForeignKey(
            entity = TemporaryCharacterData::class,
            entity = UserCharacter::class,
            parentColumns = ["id"],
            childColumns = ["id"],
            onDelete = ForeignKey.CASCADE
        )
    ]
)
data class TemporaryBECharacterData (
data class BECharacterData (
    @PrimaryKey(autoGenerate = true) val id: Long,
    val trainingHp: Int,
    val trainingAp: Int,

R app/src/main/java/com/github/nacabaro/vbhelper/temporary_domain/TemporaryTransformationHistory.kt => app/src/main/java/com/github/nacabaro/vbhelper/domain/device_data/TransformationHistory.kt +4 -5
@@ 1,4 1,4 @@
package com.github.nacabaro.vbhelper.temporary_domain
package com.github.nacabaro.vbhelper.domain.device_data

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


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

A app/src/main/java/com/github/nacabaro/vbhelper/domain/device_data/UserCharacter.kt => app/src/main/java/com/github/nacabaro/vbhelper/domain/device_data/UserCharacter.kt +39 -0
@@ 0,0 1,39 @@
package com.github.nacabaro.vbhelper.domain.device_data

import androidx.room.Entity
import androidx.room.ForeignKey
import androidx.room.PrimaryKey
import com.github.cfogrady.vbnfc.data.NfcCharacter
import com.github.nacabaro.vbhelper.domain.DeviceType
import com.github.nacabaro.vbhelper.domain.Character

@Entity(
    foreignKeys = [
        ForeignKey(
            entity = Character::class,
            parentColumns = ["id"],
            childColumns = ["charId"],
            onDelete = ForeignKey.CASCADE
        )
    ]
)
data class UserCharacter (
    @PrimaryKey(autoGenerate = true) val id: Long = 0,
    var charId: Long,
    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,
    var characterType: DeviceType
)
\ No newline at end of file

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

class VBCharacterData {
}
\ No newline at end of file

R app/src/main/java/com/github/nacabaro/vbhelper/temporary_domain/TemporaryCharacterData.kt => app/src/main/java/com/github/nacabaro/vbhelper/dtos/CharacterDtos.kt +28 -23
@@ 1,27 1,32 @@
package com.github.nacabaro.vbhelper.temporary_domain
package com.github.nacabaro.vbhelper.dtos

import androidx.room.Entity
import androidx.room.PrimaryKey
import com.github.cfogrady.vbnfc.data.NfcCharacter
import com.github.nacabaro.vbhelper.domain.DeviceType

@Entity
data class TemporaryCharacterData (
    @PrimaryKey(autoGenerate = true) val id: Long = 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

object CharacterDtos {
    data class CharacterWithSprites(
        var id: Long = 0,
        var charId: Long,
        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,
        var characterType: DeviceType,
        val spriteIdle: ByteArray,
        val spriteWidth: Int,
        val spriteHeight: Int
    )
}
\ No newline at end of file

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

class MonsterDataCombined {
}
\ No newline at end of file

M app/src/main/java/com/github/nacabaro/vbhelper/screens/DexScreen.kt => app/src/main/java/com/github/nacabaro/vbhelper/screens/DexScreen.kt +7 -55
@@ 1,38 1,27 @@
package com.github.nacabaro.vbhelper.screens

import android.graphics.Bitmap
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
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.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.graphics.FilterQuality
import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
import com.github.nacabaro.vbhelper.utils.BitmapData
import com.github.nacabaro.vbhelper.components.DexDiMEntry
import com.github.nacabaro.vbhelper.components.TopBanner
import com.github.nacabaro.vbhelper.di.VBHelper
import com.github.nacabaro.vbhelper.domain.Dim
import com.github.nacabaro.vbhelper.navigation.BottomNavItem
import com.github.nacabaro.vbhelper.source.DexRepository
import kotlinx.coroutines.launch
import java.nio.ByteBuffer

@Composable
fun DexScreen(


@@ 66,16 55,13 @@ fun DexScreen(
                .padding(top = contentPadding.calculateTopPadding())
        ) {
            items(dimList.value) {
                val bitmap = remember (it.logo) {
                    Bitmap.createBitmap(it.logoWidth, it.logoHeight, Bitmap.Config.RGB_565).apply {
                        copyPixelsFromBuffer(ByteBuffer.wrap(it.logo))
                    }
                }
                val imageBitmap = remember(bitmap) { bitmap.asImageBitmap() }

                DexDiMEntry(
                    name = it.name,
                    logo = imageBitmap,
                    logo = BitmapData(
                        bitmap = it.logo,
                        width = it.logoWidth,
                        height = it.logoHeight
                    ),
                    onClick = {
                        navController
                            .navigate(


@@ 93,37 79,3 @@ fun DexScreen(
    }
}

@Composable
fun DexDiMEntry(
    name: String,
    logo: ImageBitmap,
    onClick: () -> Unit,
    modifier: Modifier = Modifier
) {
    Card (
        shape = MaterialTheme.shapes.medium,
        modifier = modifier,
        onClick = onClick
    ) {
        Row (
            horizontalArrangement = Arrangement.Start,
            verticalAlignment = Alignment.CenterVertically,
            modifier = Modifier
                .padding(8.dp)
        ) {
            Image (
                bitmap = logo,
                contentDescription = name,
                filterQuality = FilterQuality.None,
                modifier = Modifier
                    .padding(8.dp)
                    .size(64.dp)
            )
            Text(
                text = name,
                modifier = Modifier
                    .padding(8.dp)
            )
        }
    }
}
\ No newline at end of file

M app/src/main/java/com/github/nacabaro/vbhelper/screens/DimScreen.kt => app/src/main/java/com/github/nacabaro/vbhelper/screens/DimScreen.kt +7 -11
@@ 1,6 1,5 @@
package com.github.nacabaro.vbhelper.screens

import android.graphics.Bitmap
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.items


@@ 10,16 9,15 @@ import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.platform.LocalContext
import androidx.navigation.NavController
import com.github.nacabaro.vbhelper.utils.BitmapData
import com.github.nacabaro.vbhelper.components.CharacterEntry
import com.github.nacabaro.vbhelper.components.TopBanner
import com.github.nacabaro.vbhelper.domain.Character
import com.github.nacabaro.vbhelper.di.VBHelper
import com.github.nacabaro.vbhelper.source.DexRepository
import kotlinx.coroutines.launch
import java.nio.ByteBuffer

@Composable
fun DiMScreen(


@@ 54,15 52,13 @@ fun DiMScreen(
            contentPadding = contentPadding
        ) {
            items(characterList.value) { character ->
                val bitmapCharacter = remember (character.sprite1) {
                    Bitmap.createBitmap(character.spritesWidth, character.spritesHeight, Bitmap.Config.RGB_565).apply {
                        copyPixelsFromBuffer(ByteBuffer.wrap(character.sprite1))
                    }
                }
                val imageBitmapCharacter = remember(bitmapCharacter) { bitmapCharacter.asImageBitmap() }
                CharacterEntry(
                    icon = imageBitmapCharacter,
                    onClick = {  }
                    onClick = {  },
                    icon = BitmapData(
                        bitmap = character.sprite1,
                        width = character.spritesWidth,
                        height = character.spritesHeight
                    ),
                )
            }
        }

M app/src/main/java/com/github/nacabaro/vbhelper/screens/StorageScreen.kt => app/src/main/java/com/github/nacabaro/vbhelper/screens/StorageScreen.kt +18 -19
@@ 16,8 16,6 @@ import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Button
import androidx.compose.material3.Card
import androidx.compose.material3.CardElevation
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable


@@ 32,18 30,18 @@ import androidx.compose.runtime.setValue
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 androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import com.github.nacabaro.vbhelper.R
import com.github.nacabaro.vbhelper.components.StorageEntry
import com.github.nacabaro.vbhelper.components.CharacterEntry
import com.github.nacabaro.vbhelper.components.TopBanner
import com.github.nacabaro.vbhelper.di.VBHelper
import com.github.nacabaro.vbhelper.domain.device_data.UserCharacter
import com.github.nacabaro.vbhelper.dtos.CharacterDtos
import com.github.nacabaro.vbhelper.source.StorageRepository
import com.github.nacabaro.vbhelper.temporary_domain.TemporaryCharacterData
import kotlinx.coroutines.coroutineScope
import com.github.nacabaro.vbhelper.utils.BitmapData
import kotlinx.coroutines.launch




@@ 52,15 50,14 @@ fun StorageScreen() {
    val coroutineScope = rememberCoroutineScope()
    val application = LocalContext.current.applicationContext as VBHelper
    val storageRepository = StorageRepository(application.container.db)
    val monList = remember { mutableStateListOf<TemporaryCharacterData>() }
    val monList = remember { mutableStateOf<List<CharacterDtos.CharacterWithSprites>>(emptyList()) }

    var selectedCharacter by remember { mutableStateOf<Long?>(null) }

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



@@ 69,7 66,7 @@ fun StorageScreen() {
    Scaffold (
        topBar = { TopBanner(text = "My Digimon") }
    ) { contentPadding ->
        if (monList.isEmpty()) {
        if (monList.value.isEmpty()) {
            Column (
                horizontalAlignment = Alignment.CenterHorizontally,
                verticalArrangement = Arrangement.Center,


@@ 91,15 88,17 @@ fun StorageScreen() {
                .scrollable(state = rememberScrollState(), orientation = Orientation.Vertical)
                .padding(top = contentPadding.calculateTopPadding())
        ) {
            items(monList) { index ->
                var showDialog by rememberSaveable { mutableStateOf(false) }

                StorageEntry(
                    name = index.dimId.toString() + " - " + index.charIndex.toString(),
                    icon = R.drawable.ic_launcher_foreground,
                    onClick = { selectedCharacter = index.id },
            items(monList.value) { index ->
                CharacterEntry(
                    icon = BitmapData(
                        bitmap = index.spriteIdle,
                        width = index.spriteWidth,
                        height = index.spriteHeight
                    ),
                    modifier = Modifier
                        .padding(8.dp)
                        .size(96.dp)

                )

                if (selectedCharacter != null) {


@@ 121,7 120,7 @@ fun StorageDialog(
    val coroutineScope = rememberCoroutineScope()
    val application = LocalContext.current.applicationContext as VBHelper
    val storageRepository = StorageRepository(application.container.db)
    val character = remember { mutableStateOf<TemporaryCharacterData?>(null) }
    val character = remember { mutableStateOf<UserCharacter?>(null) }

    LaunchedEffect(storageRepository) {
        coroutineScope.launch {

M app/src/main/java/com/github/nacabaro/vbhelper/source/StorageRepository.kt => app/src/main/java/com/github/nacabaro/vbhelper/source/StorageRepository.kt +7 -4
@@ 1,16 1,19 @@
package com.github.nacabaro.vbhelper.source

import com.github.nacabaro.vbhelper.daos.CharacterDao
import com.github.nacabaro.vbhelper.database.AppDatabase
import com.github.nacabaro.vbhelper.domain.device_data.UserCharacter
import com.github.nacabaro.vbhelper.dtos.CharacterDtos
import com.github.nacabaro.vbhelper.temporary_domain.TemporaryCharacterData

class StorageRepository (
    private val db: AppDatabase
) {
    suspend fun getAllCharacters(): List<TemporaryCharacterData> {
        return db.temporaryMonsterDao().getAllCharacters()
    suspend fun getAllCharacters(): List<CharacterDtos.CharacterWithSprites> {
        return db.userCharacterDao().getAllCharacters()
    }

    suspend fun getSingleCharacter(id: Long): TemporaryCharacterData {
        return db.temporaryMonsterDao().getCharacter(id)
    suspend fun getSingleCharacter(id: Long): UserCharacter {
        return db.userCharacterDao().getCharacter(id)
    }
}
\ No newline at end of file

D app/src/main/java/com/github/nacabaro/vbhelper/temporary_daos/TemporaryMonsterDao.kt => app/src/main/java/com/github/nacabaro/vbhelper/temporary_daos/TemporaryMonsterDao.kt +0 -30
@@ 1,30 0,0 @@
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): Long

    @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>

    @Query("SELECT * FROM TemporaryCharacterData WHERE id = :id")
    suspend fun getCharacter(id: Long): TemporaryCharacterData
}
\ No newline at end of file

A app/src/main/java/com/github/nacabaro/vbhelper/utils/BitmapData.kt => app/src/main/java/com/github/nacabaro/vbhelper/utils/BitmapData.kt +8 -0
@@ 0,0 1,8 @@
package com.github.nacabaro.vbhelper.utils

// simple, but smooth
data class BitmapData (
    val bitmap: ByteArray,
    val width: Int,
    val height: Int
)
\ No newline at end of file

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