~cytrogen/vbhelper

ac05dfc5410f44118b2df05c73e96602f24f830d — nacabaro 8 months ago 1eed6a4 + 220a61a
Merge pull request #33 from nacabaro/vb/nfc_compat

Add VB Compatibility
24 files changed, 764 insertions(+), 311 deletions(-)

R app/src/main/java/com/github/nacabaro/vbhelper/daos/{DiMDao => CardDao}.kt
R app/src/main/java/com/github/nacabaro/vbhelper/daos/{DiMProgressDao => CardProgressDao}.kt
M app/src/main/java/com/github/nacabaro/vbhelper/daos/CharacterDao.kt
M app/src/main/java/com/github/nacabaro/vbhelper/daos/UserCharacterDao.kt
M app/src/main/java/com/github/nacabaro/vbhelper/database/AppDatabase.kt
R app/src/main/java/com/github/nacabaro/vbhelper/domain/{characters => card}/Card.kt
R app/src/main/java/com/github/nacabaro/vbhelper/domain/{DimProgress => card/CardProgress}.kt
M app/src/main/java/com/github/nacabaro/vbhelper/domain/characters/Character.kt
A app/src/main/java/com/github/nacabaro/vbhelper/domain/device_data/SpecialMissions.kt
M app/src/main/java/com/github/nacabaro/vbhelper/domain/device_data/UserCharacter.kt
M app/src/main/java/com/github/nacabaro/vbhelper/domain/device_data/VBCharacterData.kt
A app/src/main/java/com/github/nacabaro/vbhelper/domain/device_data/VitalsHistory.kt
M app/src/main/java/com/github/nacabaro/vbhelper/dtos/CharacterDtos.kt
M app/src/main/java/com/github/nacabaro/vbhelper/screens/DexScreen.kt
M app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/ScanScreen.kt
M app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/ScanScreenController.kt
M app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/ScanScreenControllerImpl.kt
A app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/converters/FromNfcConverter.kt
A app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/converters/ToNfcConverter.kt
M app/src/main/java/com/github/nacabaro/vbhelper/screens/settingsScreen/SettingsScreenControllerImpl.kt
M app/src/main/java/com/github/nacabaro/vbhelper/source/DexRepository.kt
M app/src/main/java/com/github/nacabaro/vbhelper/source/StorageRepository.kt
D app/src/main/java/com/github/nacabaro/vbhelper/utils/CharacterToNFCCharacter.kt
D app/src/main/java/com/github/nacabaro/vbhelper/utils/padTransformationArray.kt
R app/src/main/java/com/github/nacabaro/vbhelper/daos/DiMDao.kt => app/src/main/java/com/github/nacabaro/vbhelper/daos/CardDao.kt +3 -6
@@ 4,16 4,13 @@ import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import com.github.nacabaro.vbhelper.domain.characters.Card
import com.github.nacabaro.vbhelper.domain.card.Card

@Dao
interface DiMDao {
interface CardDao {
    @Insert(onConflict = OnConflictStrategy.IGNORE)
    suspend fun insertNewDim(card: Card): Long

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

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

R app/src/main/java/com/github/nacabaro/vbhelper/daos/DiMProgressDao.kt => app/src/main/java/com/github/nacabaro/vbhelper/daos/CardProgressDao.kt +9 -3
@@ 1,11 1,17 @@
package com.github.nacabaro.vbhelper.daos

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

@Dao
interface DiMProgressDao {
interface CardProgressDao {
    @Upsert
    suspend fun updateDimProgress(vararg dimProgress: DimProgress)
    fun updateDimProgress(vararg cardProgresses: CardProgress)

    @Query(
        "SELECT currentStage FROM CardProgress WHERE cardId = :cardId"
    )
    fun getCardProgress(cardId: Int): Int
}
\ 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 +6 -22
@@ 12,40 12,24 @@ interface CharacterDao {
    @Insert
    suspend fun insertCharacter(vararg characterData: Character)

    @Query("SELECT * FROM Character")
    suspend fun getAllCharacters(): List<Character>

    @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: Sprite)

    @Query("SELECT * FROM Sprite")
    suspend fun getAllSprites(): List<Sprite>

    @Query(
        """
        SELECT 
            d.dimId as cardId,
            c.monIndex as charId
            d.cardId as cardId,
            c.monIndex as charId,
            c.stage as stage,
            c.attribute as attribute
        FROM Character c
        JOIN UserCharacter uc ON c.id = uc.charId
        JOIN Card d ON c.dimId = d.id
        WHERE uc.id = :charId
        WHERE c.id = :charId
    """
    )
    suspend fun getCharacterInfo(charId: Long): CharacterDtos.DiMInfo

    @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)
    suspend fun getCharacterInfo(charId: Long): CharacterDtos.CardCharacterInfo
}
\ No newline at end of file

M app/src/main/java/com/github/nacabaro/vbhelper/daos/UserCharacterDao.kt => app/src/main/java/com/github/nacabaro/vbhelper/daos/UserCharacterDao.kt +32 -1
@@ 8,7 8,10 @@ import androidx.room.Upsert
import com.github.nacabaro.vbhelper.domain.characters.Character
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.SpecialMissions
import com.github.nacabaro.vbhelper.domain.device_data.TransformationHistory
import com.github.nacabaro.vbhelper.domain.device_data.VBCharacterData
import com.github.nacabaro.vbhelper.domain.device_data.VitalsHistory
import com.github.nacabaro.vbhelper.dtos.CharacterDtos

@Dao


@@ 19,6 22,9 @@ interface UserCharacterDao {
    @Insert
    fun insertBECharacterData(characterData: BECharacterData)

    @Insert
    fun insertVBCharacterData(characterData: VBCharacterData)

    @Upsert
    fun updateCharacter(character: UserCharacter)



@@ 28,6 34,9 @@ interface UserCharacterDao {
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    fun insertTransformationHistory(vararg transformationHistory: TransformationHistory)

    @Insert
    fun insertSpecialMissions(vararg specialMissions: SpecialMissions)

    @Query("""
        SELECT 
            c.id AS id,


@@ 41,7 50,7 @@ interface UserCharacterDao {
        JOIN Sprite s ON s.id = c.spriteId
        WHERE monId = :monId
    """)
    fun getTransformationHistory(monId: Long): List<CharacterDtos.TransformationHistory>?
    suspend fun getTransformationHistory(monId: Long): List<CharacterDtos.TransformationHistory>?

    @Query(
        """


@@ 97,6 106,12 @@ interface UserCharacterDao {
    @Query("SELECT * FROM BECharacterData WHERE id = :id")
    suspend fun getBeData(id: Long): BECharacterData

    @Query("SELECT * FROM VBCharacterData WHERE id = :id")
    suspend fun getVbData(id: Long): VBCharacterData

    @Query("SELECT * FROM SpecialMissions WHERE characterId = :id")
    suspend fun getSpecialMissions(id: Long): List<SpecialMissions>

    @Query(
        """
        SELECT


@@ 141,4 156,20 @@ interface UserCharacterDao {
        """
    )
    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>
}
\ 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 +13 -3
@@ 5,36 5,46 @@ import androidx.room.RoomDatabase
import com.github.nacabaro.vbhelper.daos.AdventureDao
import com.github.nacabaro.vbhelper.daos.CharacterDao
import com.github.nacabaro.vbhelper.daos.DexDao
import com.github.nacabaro.vbhelper.daos.DiMDao
import com.github.nacabaro.vbhelper.daos.CardDao
import com.github.nacabaro.vbhelper.daos.CardProgressDao
import com.github.nacabaro.vbhelper.daos.ItemDao
import com.github.nacabaro.vbhelper.daos.SpriteDao
import com.github.nacabaro.vbhelper.daos.UserCharacterDao
import com.github.nacabaro.vbhelper.domain.characters.Character
import com.github.nacabaro.vbhelper.domain.characters.Card
import com.github.nacabaro.vbhelper.domain.card.Card
import com.github.nacabaro.vbhelper.domain.card.CardProgress
import com.github.nacabaro.vbhelper.domain.characters.Sprite
import com.github.nacabaro.vbhelper.domain.characters.Adventure
import com.github.nacabaro.vbhelper.domain.characters.Dex
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.TransformationHistory
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.VitalsHistory
import com.github.nacabaro.vbhelper.domain.items.Items

@Database(
    version = 1,
    entities = [
        Card::class,
        CardProgress::class,
        Character::class,
        Sprite::class,
        UserCharacter::class,
        BECharacterData::class,
        VBCharacterData::class,
        SpecialMissions::class,
        TransformationHistory::class,
        VitalsHistory::class,
        Dex::class,
        Items::class,
        Adventure::class
    ]
)
abstract class AppDatabase : RoomDatabase() {
    abstract fun dimDao(): DiMDao
    abstract fun cardDao(): CardDao
    abstract fun cardProgressDao(): CardProgressDao
    abstract fun characterDao(): CharacterDao
    abstract fun userCharacterDao(): UserCharacterDao
    abstract fun dexDao(): DexDao

R app/src/main/java/com/github/nacabaro/vbhelper/domain/characters/Card.kt => app/src/main/java/com/github/nacabaro/vbhelper/domain/card/Card.kt +2 -2
@@ 1,4 1,4 @@
package com.github.nacabaro.vbhelper.domain.characters
package com.github.nacabaro.vbhelper.domain.card

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


@@ 7,7 7,7 @@ import androidx.room.PrimaryKey
data class Card(
    @PrimaryKey(autoGenerate = true)
    val id: Long = 0,
    val dimId: Int,
    val cardId: Int,
    val logo: ByteArray,
    val logoWidth: Int,
    val logoHeight: Int,

R app/src/main/java/com/github/nacabaro/vbhelper/domain/DimProgress.kt => app/src/main/java/com/github/nacabaro/vbhelper/domain/card/CardProgress.kt +4 -12
@@ 1,29 1,21 @@
package com.github.nacabaro.vbhelper.domain
package com.github.nacabaro.vbhelper.domain.card

import androidx.room.Entity
import androidx.room.ForeignKey
import androidx.room.PrimaryKey
import com.github.nacabaro.vbhelper.domain.characters.Card

@Entity(
    foreignKeys = [
        ForeignKey(
            entity = User::class,
            parentColumns = ["id"],
            childColumns = ["userId"],
            onDelete = ForeignKey.CASCADE
        ),
        ForeignKey(
            entity = Card::class,
            parentColumns = ["id"],
            childColumns = ["dimId"],
            childColumns = ["cardId"],
            onDelete = ForeignKey.CASCADE
        )
    ]
)
data class DimProgress(
    @PrimaryKey val dimId: Int,
    @PrimaryKey val userId: Int,
data class CardProgress(
    @PrimaryKey val cardId: Long,
    val currentStage: Int,
    val unlocked: Boolean
)

M app/src/main/java/com/github/nacabaro/vbhelper/domain/characters/Character.kt => app/src/main/java/com/github/nacabaro/vbhelper/domain/characters/Character.kt +2 -1
@@ 4,6 4,7 @@ import androidx.room.Entity
import androidx.room.PrimaryKey
import androidx.room.ForeignKey
import com.github.cfogrady.vbnfc.data.NfcCharacter
import com.github.nacabaro.vbhelper.domain.card.Card

@Entity(
    foreignKeys = [


@@ 23,7 24,7 @@ import com.github.cfogrady.vbnfc.data.NfcCharacter
)

/*
 * Character represents a character on a DIM card. There should only be one of these per dimId
 * Character represents a character on a card. There should only be one of these per dimId
 * and monIndex.
 * TODO: Customs will mean this should be unique per cardName and monIndex
 */

A app/src/main/java/com/github/nacabaro/vbhelper/domain/device_data/SpecialMissions.kt => app/src/main/java/com/github/nacabaro/vbhelper/domain/device_data/SpecialMissions.kt +28 -0
@@ 0,0 1,28 @@
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.vb.SpecialMission

@Entity(
    foreignKeys = [
        ForeignKey(
            entity = UserCharacter::class,
            parentColumns = ["id"],
            childColumns = ["characterId"],
            onDelete = ForeignKey.CASCADE
        )
    ]
)
data class SpecialMissions (
    @PrimaryKey(autoGenerate = true) var id: Long = 0,
    var characterId: Long,
    var goal: Int,
    val watchId: Int,
    val progress: Int,
    val status: SpecialMission.Status,
    val timeElapsedInMinutes: Int,
    val timeLimitInMinutes: Int,
    val missionType: SpecialMission.Type
)
\ No newline at end of file

M 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 +0 -1
@@ 24,7 24,6 @@ data class UserCharacter (
    @PrimaryKey(autoGenerate = true) val id: Long = 0,
    var charId: Long,
    var ageInDays: Int,
    var nextAdventureMissionStage: Int, // next adventure mission stage on the character's dim
    var mood: Int,
    var vitalPoints: Int,
    var transformationCountdown: Int,

M 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 +19 -2
@@ 1,4 1,21 @@
package com.github.nacabaro.vbhelper.domain.device_data

class VBCharacterData {
}
\ No newline at end of file
import androidx.room.Entity
import androidx.room.ForeignKey
import androidx.room.PrimaryKey

@Entity(
    foreignKeys = [
        ForeignKey(
            entity = UserCharacter::class,
            parentColumns = ["id"],
            childColumns = ["id"],
            onDelete = ForeignKey.CASCADE
        )
    ]
)
data class VBCharacterData (
    @PrimaryKey val id: Long,
    val generation: Int,
    val totalTrophies: Int
)
\ No newline at end of file

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

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

@Entity(
    foreignKeys = [
        ForeignKey(
            entity = UserCharacter::class,
            parentColumns = ["id"],
            childColumns = ["charId"],
            onDelete = ForeignKey.CASCADE
        )
    ]
)
data class VitalsHistory (
    @PrimaryKey(autoGenerate = true) val id: Long = 0,
    val charId: Long,
    val year: Int,
    val month: Int,
    val day: Int,
    val vitalPoints: Int
)
\ No newline at end of file

M app/src/main/java/com/github/nacabaro/vbhelper/dtos/CharacterDtos.kt => app/src/main/java/com/github/nacabaro/vbhelper/dtos/CharacterDtos.kt +4 -4
@@ 11,7 11,6 @@ object CharacterDtos {
        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,


@@ 35,9 34,11 @@ object CharacterDtos {
        val isInAdventure: Boolean
    )

    data class DiMInfo(
    data class CardCharacterInfo(
        val cardId: Int,
        val charId: Int
        val charId: Int,
        val stage: Int,
        val attribute: NfcCharacter.Attribute
    )

    data class TransformationHistory(


@@ 63,7 64,6 @@ object CharacterDtos {
        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,

M app/src/main/java/com/github/nacabaro/vbhelper/screens/DexScreen.kt => app/src/main/java/com/github/nacabaro/vbhelper/screens/DexScreen.kt +0 -1
@@ 18,7 18,6 @@ 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.characters.Card
import com.github.nacabaro.vbhelper.dtos.CardDtos
import com.github.nacabaro.vbhelper.navigation.NavigationItems
import com.github.nacabaro.vbhelper.source.DexRepository

M app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/ScanScreen.kt => app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/ScanScreen.kt +58 -17
@@ 34,7 34,6 @@ import com.github.nacabaro.vbhelper.navigation.NavigationItems
import com.github.nacabaro.vbhelper.source.StorageRepository
import com.github.nacabaro.vbhelper.source.isMissingSecrets
import com.github.nacabaro.vbhelper.source.proto.Secrets
import com.github.nacabaro.vbhelper.utils.characterToNfc
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.withContext


@@ 58,33 57,69 @@ fun ScanScreen(
    val storageRepository = StorageRepository(application.container.db)
    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

    LaunchedEffect(storageRepository) {
        withContext(Dispatchers.IO) {
            if(characterId != null && nfcCharacter == null) {
                nfcCharacter = characterToNfc(context, 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!!)
               }
            }
        }
    }

    DisposableEffect(readingScreen || writingScreen, isDoneSendingCard) {
    DisposableEffect(readingScreen) {
        if(readingScreen) {
            scanScreenController.registerActivityLifecycleListener(SCAN_SCREEN_ACTIVITY_LIFECYCLE_LISTENER, object: ActivityLifecycleListener {
                override fun onPause() {
                    scanScreenController.cancelRead()
                }
            scanScreenController.registerActivityLifecycleListener(
                SCAN_SCREEN_ACTIVITY_LIFECYCLE_LISTENER,
                object: ActivityLifecycleListener {
                    override fun onPause() {
                        scanScreenController.cancelRead()
                    }

                override fun onResume() {
                    scanScreenController.onClickRead(secrets!!) {
                        isDoneReadingCharacter = true
                    override fun onResume() {
                        scanScreenController.onClickRead(secrets!!) {
                            isDoneReadingCharacter = true
                        }
                    }
                }

            })
            )
            scanScreenController.onClickRead(secrets!!) {
                isDoneReadingCharacter = true
            }
        } else if (writingScreen) {
        }
        onDispose {
            if(readingScreen) {
                scanScreenController.unregisterActivityLifecycleListener(
                    SCAN_SCREEN_ACTIVITY_LIFECYCLE_LISTENER
                )
                scanScreenController.cancelRead()
            }
        }
    }

    DisposableEffect(writingScreen, isDoneSendingCard) {
        if (writingScreen) {
            scanScreenController.registerActivityLifecycleListener(
                SCAN_SCREEN_ACTIVITY_LIFECYCLE_LISTENER,
                object : ActivityLifecycleListener {


@@ 105,6 140,9 @@ fun ScanScreen(
                    }
                }
            )
        }

        if (secrets != null && nfcCharacter != null) {
            if (!isDoneSendingCard) {
                scanScreenController.onClickCheckCard(secrets!!, nfcCharacter!!) {
                    isDoneSendingCard = true


@@ 115,8 153,9 @@ fun ScanScreen(
                }
            }
        }

        onDispose {
            if(readingScreen || writingScreen) {
            if(writingScreen) {
                scanScreenController.unregisterActivityLifecycleListener(SCAN_SCREEN_ACTIVITY_LIFECYCLE_LISTENER)
                scanScreenController.cancelRead()
            }


@@ 132,7 171,7 @@ fun ScanScreen(
        LaunchedEffect(storageRepository) {
            withContext(Dispatchers.IO) {
                storageRepository
                    .deleteCharacter(characterId!!)
                    .deleteCharacter(selectedCharacterId!!)
            }
        }
    }


@@ 158,7 197,7 @@ fun ScanScreen(
    } else {
        ChooseConnectOption(
            onClickRead = when {
                characterId != null -> null
                selectedCharacterId != null -> null
                else -> {
                    {
                        if(secrets == null) {


@@ 268,6 307,8 @@ fun ScanScreenPreview() {
            override fun onClickCheckCard(secrets: Secrets, nfcCharacter: NfcCharacter, onComplete: () -> Unit) {}
            override fun onClickWrite(secrets: Secrets, nfcCharacter: NfcCharacter, onComplete: () -> Unit) {}
            override fun cancelRead() {}
            override fun characterFromNfc(nfcCharacter: NfcCharacter): String { return "" }
            override suspend fun characterToNfc(characterId: Long): NfcCharacter? { return null }
        },
        characterId = null
    )

M app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/ScanScreenController.kt => app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/ScanScreenController.kt +3 -0
@@ 15,4 15,7 @@ interface ScanScreenController {

    fun registerActivityLifecycleListener(key: String, activityLifecycleListener: ActivityLifecycleListener)
    fun unregisterActivityLifecycleListener(key: String)

    fun characterFromNfc(nfcCharacter: NfcCharacter): String
    suspend fun characterToNfc(characterId: Long): NfcCharacter?
}
\ No newline at end of file

M app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/ScanScreenControllerImpl.kt => app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/ScanScreenControllerImpl.kt +13 -102
@@ 11,22 11,17 @@ import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.lifecycle.lifecycleScope
import com.github.cfogrady.vbnfc.TagCommunicator
import com.github.cfogrady.vbnfc.be.BENfcCharacter
import com.github.cfogrady.vbnfc.data.NfcCharacter
import com.github.cfogrady.vbnfc.vb.VBNfcCharacter
import com.github.nacabaro.vbhelper.ActivityLifecycleListener
import com.github.nacabaro.vbhelper.di.VBHelper
import com.github.nacabaro.vbhelper.domain.device_data.BECharacterData
import com.github.nacabaro.vbhelper.domain.device_data.UserCharacter
import com.github.nacabaro.vbhelper.screens.scanScreen.converters.FromNfcConverter
import com.github.nacabaro.vbhelper.screens.scanScreen.converters.ToNfcConverter
import com.github.nacabaro.vbhelper.source.getCryptographicTransformerMap
import com.github.nacabaro.vbhelper.source.isMissingSecrets
import com.github.nacabaro.vbhelper.source.proto.Secrets
import com.github.nacabaro.vbhelper.utils.DeviceType
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import java.util.GregorianCalendar

class ScanScreenControllerImpl(
    override val secretsFlow: Flow<Secrets>,


@@ 49,7 44,7 @@ class ScanScreenControllerImpl(
    override fun onClickRead(secrets: Secrets, onComplete: ()->Unit) {
        handleTag(secrets) { tagCommunicator ->
            val character = tagCommunicator.receiveCharacter()
            val resultMessage = addCharacterScannedIntoDatabase(character)
            val resultMessage = characterFromNfc(character)
            onComplete.invoke()
            resultMessage
        }


@@ 151,101 146,17 @@ class ScanScreenControllerImpl(
        componentActivity.startActivity(Intent(Settings.ACTION_WIRELESS_SETTINGS))
    }

    private fun addCharacterScannedIntoDatabase(nfcCharacter: NfcCharacter): String {
        val application = componentActivity.applicationContext as VBHelper
        val storageRepository = application.container.db

        val dimData = storageRepository
            .dimDao()
            .getDimById(nfcCharacter.dimId.toInt())

        if (dimData == null) return "Card not found"

        val cardCharData = storageRepository
            .characterDao()
            .getCharacterByMonIndex(nfcCharacter.charIndex.toInt(), dimData.id)

        val characterData = UserCharacter(
            charId = cardCharData.id,
            ageInDays = nfcCharacter.ageInDays.toInt(),
            nextAdventureMissionStage = nfcCharacter.nextAdventureMissionStage.toInt(),
            mood = nfcCharacter.mood.toInt(),
            vitalPoints = nfcCharacter.vitalPoints.toInt(),
            transformationCountdown = nfcCharacter.transformationCountdownInMinutes.toInt(),
            injuryStatus = nfcCharacter.injuryStatus,
            trophies = nfcCharacter.trophies.toInt(),
            currentPhaseBattlesWon = nfcCharacter.currentPhaseBattlesWon.toInt(),
            currentPhaseBattlesLost = nfcCharacter.currentPhaseBattlesLost.toInt(),
            totalBattlesWon = nfcCharacter.totalBattlesWon.toInt(),
            totalBattlesLost = nfcCharacter.totalBattlesLost.toInt(),
            activityLevel = nfcCharacter.activityLevel.toInt(),
            heartRateCurrent = nfcCharacter.heartRateCurrent.toInt(),
            characterType = when (nfcCharacter) {
                is BENfcCharacter -> DeviceType.BEDevice
                else -> DeviceType.VBDevice
            },
            isActive = true
    override fun characterFromNfc(nfcCharacter: NfcCharacter): String {
        val nfcConverter = FromNfcConverter(
            componentActivity = componentActivity
        )
        return nfcConverter.addCharacter(nfcCharacter)
    }

        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 suspend fun characterToNfc(characterId: Long): NfcCharacter {
        val nfcGenerator = ToNfcConverter(
            componentActivity = componentActivity
        )
        return nfcGenerator.characterToNfc(characterId)
    }
}
\ No newline at end of file

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

import android.util.Log
import androidx.activity.ComponentActivity
import com.github.cfogrady.vbnfc.be.BENfcCharacter
import com.github.cfogrady.vbnfc.data.NfcCharacter
import com.github.cfogrady.vbnfc.vb.VBNfcCharacter
import com.github.nacabaro.vbhelper.di.VBHelper
import com.github.nacabaro.vbhelper.domain.card.Card
import com.github.nacabaro.vbhelper.domain.card.CardProgress
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.UserCharacter
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 java.util.GregorianCalendar

class FromNfcConverter (
    componentActivity: ComponentActivity
) {
    private val application = componentActivity.applicationContext as VBHelper
    private val database = application.container.db



    fun addCharacter(nfcCharacter: NfcCharacter): String {
        val cardData = database
            .cardDao()
            .getDimById(nfcCharacter.dimId.toInt())

        if (cardData == null)
            return "Card not found"

        val cardCharData = database
            .characterDao()
            .getCharacterByMonIndex(nfcCharacter.charIndex.toInt(), cardData.id)

        updateCardProgress(nfcCharacter, cardData)

        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
        )

        database
            .userCharacterDao()
            .clearActiveCharacter()

        val characterId: Long = database
            .userCharacterDao()
            .insertCharacterData(characterData)

        if (nfcCharacter is BENfcCharacter) {
            addBeCharacterToDatabase(
                characterId = characterId,
                nfcCharacter = nfcCharacter
            )
        } else if (nfcCharacter is VBNfcCharacter) {
            addVbCharacterToDatabase(
                characterId = characterId,
                nfcCharacter = nfcCharacter
            )
        }

        addTransformationHistoryToDatabase(
            characterId = characterId,
            nfcCharacter = nfcCharacter,
            dimData = cardData
        )

        addVitalsHistoryToDatabase(
            characterId = characterId,
            nfcCharacter = nfcCharacter
        )

        return "Done reading character!"
    }



    private fun updateCardProgress(
        nfcCharacter: NfcCharacter,
        cardData: Card
    ) {
        val currentCardProgress = CardProgress(
            cardId = cardData.id,
            currentStage = nfcCharacter.nextAdventureMissionStage.toInt(),
            unlocked = nfcCharacter.nextAdventureMissionStage.toInt() > cardData.stageCount
        )

        database
            .cardProgressDao()
            .updateDimProgress(currentCardProgress)
    }



    private fun addVbCharacterToDatabase(
        characterId: Long,
        nfcCharacter: VBNfcCharacter
    ) {
        val extraCharacterData = VBCharacterData(
            id = characterId,
            generation = nfcCharacter.generation.toInt(),
            totalTrophies = nfcCharacter.totalTrophies.toInt()
        )

        database
            .userCharacterDao()
            .insertVBCharacterData(extraCharacterData)

        addSpecialMissionsToDatabase(nfcCharacter, characterId)
    }



    private fun addSpecialMissionsToDatabase(
        nfcCharacter: VBNfcCharacter,
        characterId: Long
    ) {
        val specialMissionsWatch = nfcCharacter.specialMissions
        val specialMissionsDb = specialMissionsWatch.map { item ->
            SpecialMissions(
                characterId = characterId,
                goal = item.goal.toInt(),
                watchId = item.id.toInt(),
                progress = item.progress.toInt(),
                status = item.status,
                timeElapsedInMinutes = item.timeElapsedInMinutes.toInt(),
                timeLimitInMinutes = item.timeLimitInMinutes.toInt(),
                missionType = item.type,
            )
        }

        database
            .userCharacterDao()
            .insertSpecialMissions(*specialMissionsDb.toTypedArray())
    }



    private fun addBeCharacterToDatabase(
        characterId: Long,
        nfcCharacter: 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(),
        )

        database
            .userCharacterDao()
            .insertBECharacterData(extraCharacterData)
    }



    private fun addVitalsHistoryToDatabase(
        characterId: Long,
        nfcCharacter: NfcCharacter
    ) {
        val vitalsHistoryWatch = nfcCharacter.vitalHistory
        val vitalsHistory = vitalsHistoryWatch.map { historyElement ->
            Log.d("VitalsHistory", "${historyElement.year.toInt()} ${historyElement.month.toInt()} ${historyElement.day.toInt()}")
            VitalsHistory(
                charId = characterId,
                year = historyElement.year.toInt(),
                month = historyElement.month.toInt(),
                day = historyElement.day.toInt(),
                vitalPoints = historyElement.vitalsGained.toInt()
            )
        }

        database
            .userCharacterDao()
            .insertVitals(*vitalsHistory.toTypedArray())
    }


    private fun addTransformationHistoryToDatabase(
        characterId: Long,
        nfcCharacter: NfcCharacter,
        dimData: Card
    ) {
        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

                database
                    .userCharacterDao()
                    .insertTransformation(
                        characterId,
                        item.toCharIndex.toInt(),
                        dimData.id,
                        date
                    )

                database
                    .dexDao()
                    .insertCharacter(
                        item.toCharIndex.toInt(),
                        dimData.id,
                        date
                    )
            }
        }
    }
}
\ No newline at end of file

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

import android.icu.util.Calendar
import android.util.Log
import androidx.activity.ComponentActivity
import com.github.cfogrady.vbnfc.be.BENfcCharacter
import com.github.cfogrady.vbnfc.be.FirmwareVersion
import com.github.cfogrady.vbnfc.data.NfcCharacter
import com.github.cfogrady.vbnfc.vb.SpecialMission
import com.github.cfogrady.vbnfc.vb.VBNfcCharacter
import com.github.nacabaro.vbhelper.database.AppDatabase
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.utils.DeviceType
import java.util.Date

class ToNfcConverter(
    private val componentActivity: ComponentActivity
) {
    private val application: VBHelper = componentActivity.applicationContext as VBHelper
    private val database: AppDatabase = application.container.db



    suspend fun characterToNfc(
        characterId: Long
    ): NfcCharacter {
        val app = componentActivity.applicationContext as VBHelper
        val database = app.container.db

        val userCharacter = database
            .userCharacterDao()
            .getCharacter(characterId)

        val characterInfo = database
            .characterDao()
            .getCharacterInfo(userCharacter.charId)

        val currentCardStage = database
            .cardProgressDao()
            .getCardProgress(characterInfo.cardId)

        return if (userCharacter.characterType == DeviceType.BEDevice)
            nfcToBENfc(characterId, characterInfo, currentCardStage, userCharacter)
        else
            nfcToVBNfc(characterId, characterInfo, currentCardStage, userCharacter)
    }



    private suspend fun nfcToVBNfc(
        characterId: Long,
        characterInfo: CharacterDtos.CardCharacterInfo,
        currentCardStage: Int,
        userCharacter: UserCharacter
    ): VBNfcCharacter {
        val vbData = database
            .userCharacterDao()
            .getVbData(characterId)

        val paddedTransformationArray = generateTransformationHistory(characterId)

        val watchSpecialMissions = generateSpecialMissionsArray(characterId)

        val nfcData = VBNfcCharacter(
            dimId = characterInfo.cardId.toUShort(),
            charIndex = characterInfo.charId.toUShort(),
            stage = characterInfo.stage.toByte(),
            attribute = characterInfo.attribute,
            ageInDays = userCharacter.ageInDays.toByte(),
            nextAdventureMissionStage = currentCardStage.toByte(),
            mood = userCharacter.mood.toByte(),
            vitalPoints = userCharacter.vitalPoints.toUShort(),
            transformationCountdownInMinutes = userCharacter.transformationCountdown.toUShort(),
            injuryStatus = userCharacter.injuryStatus,
            trophies = userCharacter.trophies.toUShort(),
            currentPhaseBattlesWon = userCharacter.currentPhaseBattlesWon.toUShort(),
            currentPhaseBattlesLost = userCharacter.currentPhaseBattlesLost.toUShort(),
            totalBattlesWon = userCharacter.totalBattlesWon.toUShort(),
            totalBattlesLost = userCharacter.totalBattlesLost.toUShort(),
            activityLevel = userCharacter.activityLevel.toByte(),
            heartRateCurrent = userCharacter.heartRateCurrent.toUByte(),
            transformationHistory = paddedTransformationArray,
            vitalHistory = generateVitalsHistoryArray(characterId),
            appReserved1 = ByteArray(12) {0},
            appReserved2 = Array(3) {0u},
            generation = vbData.generation.toUShort(),
            totalTrophies = vbData.totalTrophies.toUShort(),
            specialMissions = watchSpecialMissions.toTypedArray()
        )

        return nfcData
    }



    private suspend fun generateSpecialMissionsArray(
        characterId: Long
    ): List<SpecialMission> {
        val specialMissions = database
            .userCharacterDao()
            .getSpecialMissions(characterId)

        val watchSpecialMissions = specialMissions.map {
            SpecialMission(
                goal = it.goal.toUShort(),
                id = it.watchId.toUShort(),
                progress = it.progress.toUShort(),
                status = it.status,
                timeElapsedInMinutes = it.timeElapsedInMinutes.toUShort(),
                timeLimitInMinutes = it.timeLimitInMinutes.toUShort(),
                type = it.missionType
            )
        }

        return watchSpecialMissions
    }



    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(
        characterId: Long,
        characterInfo: CharacterDtos.CardCharacterInfo,
        currentCardStage: Int,
        userCharacter: UserCharacter
    ): BENfcCharacter {
        val beData = database
            .userCharacterDao()
            .getBeData(characterId)

        val paddedTransformationArray = generateTransformationHistory(characterId)

        val nfcData = BENfcCharacter(
            dimId = characterInfo.cardId.toUShort(),
            charIndex = characterInfo.charId.toUShort(),
            stage = characterInfo.stage.toByte(),
            attribute = characterInfo.attribute,
            ageInDays = userCharacter.ageInDays.toByte(),
            nextAdventureMissionStage = currentCardStage.toByte(),
            mood = userCharacter.mood.toByte(),
            vitalPoints = userCharacter.vitalPoints.toUShort(),
            itemEffectMentalStateValue = beData.itemEffectMentalStateValue.toByte(),
            itemEffectMentalStateMinutesRemaining = beData.itemEffectMentalStateMinutesRemaining.toByte(),
            itemEffectActivityLevelValue = beData.itemEffectActivityLevelValue.toByte(),
            itemEffectActivityLevelMinutesRemaining = beData.itemEffectActivityLevelMinutesRemaining.toByte(),
            itemEffectVitalPointsChangeValue = beData.itemEffectVitalPointsChangeValue.toByte(),
            itemEffectVitalPointsChangeMinutesRemaining = beData.itemEffectVitalPointsChangeMinutesRemaining.toByte(),
            transformationCountdownInMinutes = userCharacter.transformationCountdown.toUShort(),
            injuryStatus = userCharacter.injuryStatus,
            trainingPp = userCharacter.trophies.toUShort(),
            currentPhaseBattlesWon = userCharacter.currentPhaseBattlesWon.toUShort(),
            currentPhaseBattlesLost = userCharacter.currentPhaseBattlesLost.toUShort(),
            totalBattlesWon = userCharacter.totalBattlesWon.toUShort(),
            totalBattlesLost = userCharacter.totalBattlesLost.toUShort(),
            activityLevel = userCharacter.activityLevel.toByte(),
            heartRateCurrent = userCharacter.heartRateCurrent.toUByte(),
            transformationHistory = paddedTransformationArray,
            vitalHistory = generateVitalsHistoryArray(characterId),
            appReserved1 = ByteArray(12) {0},
            appReserved2 = Array(3) {0u},
            trainingHp = beData.trainingHp.toUShort(),
            trainingAp = beData.trainingAp.toUShort(),
            trainingBp = beData.trainingBp.toUShort(),
            remainingTrainingTimeInMinutes = beData.remainingTrainingTimeInMinutes.toUShort(),
            abilityRarity = beData.abilityRarity,
            abilityType = beData.abilityType.toUShort(),
            abilityBranch = beData.abilityBranch.toUShort(),
            abilityReset = beData.abilityReset.toByte(),
            rank = beData.rank.toByte(),
            itemType = beData.itemType.toByte(),
            itemMultiplier = beData.itemMultiplier.toByte(),
            itemRemainingTime = beData.itemRemainingTime.toByte(),
            otp0 = byteArrayOf(8),
            otp1 = byteArrayOf(8),
            characterCreationFirmwareVersion = FirmwareVersion(
                minorVersion = beData.minorVersion.toByte(),
                majorVersion = beData.majorVersion.toByte()
            )
        )

        return nfcData
    }



    private suspend fun generateTransformationHistory(
        characterId: Long
    ): Array<NfcCharacter.Transformation> {
        val transformationHistory = database
            .userCharacterDao()
            .getTransformationHistory(characterId)!!
            .map {
                val date = Date(it.transformationDate)
                val calendar = android.icu.util.GregorianCalendar()
                calendar.time = date

                NfcCharacter.Transformation(
                    toCharIndex = it.monIndex.toUByte(),
                    year = calendar
                        .get(Calendar.YEAR)
                        .toUShort(),
                    month = calendar
                        .get(Calendar.MONTH)
                        .toUByte(),
                    day = calendar
                        .get(Calendar.DAY_OF_MONTH)
                        .toUByte()
                )
            }.toTypedArray()

        val paddedTransformationArray = padTransformationArray(transformationHistory)

        return paddedTransformationArray
    }



    private fun padTransformationArray(
        transformationArray: Array<NfcCharacter.Transformation>
    ): Array<NfcCharacter.Transformation> {
        if (transformationArray.size >= 8) {
            return transformationArray
        }

        val paddedArray = Array(8) {
            NfcCharacter.Transformation(
                toCharIndex = 255u,
                year = 65535u,
                month = 255u,
                day = 255u
            )
        }

        System.arraycopy(transformationArray, 0, paddedArray, 0, transformationArray.size)
        return paddedArray
    }
}
\ No newline at end of file

M app/src/main/java/com/github/nacabaro/vbhelper/screens/settingsScreen/SettingsScreenControllerImpl.kt => app/src/main/java/com/github/nacabaro/vbhelper/screens/settingsScreen/SettingsScreenControllerImpl.kt +19 -6
@@ 16,7 16,8 @@ import com.github.cfogrady.vbnfc.data.NfcCharacter
import com.github.nacabaro.vbhelper.database.AppDatabase
import com.github.nacabaro.vbhelper.di.VBHelper
import com.github.nacabaro.vbhelper.domain.characters.Sprite
import com.github.nacabaro.vbhelper.domain.characters.Card
import com.github.nacabaro.vbhelper.domain.card.Card
import com.github.nacabaro.vbhelper.domain.card.CardProgress
import com.github.nacabaro.vbhelper.domain.characters.Character
import com.github.nacabaro.vbhelper.source.ApkSecretsImporter
import com.github.nacabaro.vbhelper.source.SecretsImporter


@@ 117,9 118,9 @@ class SettingsScreenControllerImpl(
                val card = dimReader.readCard(fileReader, false)

                val cardModel = Card(
                    dimId = card.header.dimId,
                    cardId = card.header.dimId,
                    logo = card.spriteData.sprites[0].pixelData,
                    name = card.spriteData.text, // TODO Make user write card name// TODO Make user write card name
                    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,


@@ 127,10 128,22 @@ class SettingsScreenControllerImpl(
                )

                val dimId = database
                    .dimDao()
                    .cardDao()
                    .insertNewDim(cardModel)

                val characters = card.characterStats.characterEntries
                val cardProgress = CardProgress(
                    cardId = dimId,
                    currentStage = 0,
                    unlocked = false
                )

                database
                    .cardProgressDao()
                    .updateDimProgress(cardProgress)

                val characters = card
                    .characterStats
                    .characterEntries

                var spriteCounter = when (card is BemCard) {
                    true -> 54


@@ 140,7 153,7 @@ class SettingsScreenControllerImpl(
                val domainCharacters = mutableListOf<Character>()

                for (index in 0 until characters.size) {
                    var domainSprite: Sprite? = null;
                    var domainSprite: Sprite?

                    if (index < 2 && card is DimCard) {
                        domainSprite = Sprite(

M app/src/main/java/com/github/nacabaro/vbhelper/source/DexRepository.kt => app/src/main/java/com/github/nacabaro/vbhelper/source/DexRepository.kt +0 -1
@@ 1,7 1,6 @@
package com.github.nacabaro.vbhelper.source

import com.github.nacabaro.vbhelper.database.AppDatabase
import com.github.nacabaro.vbhelper.domain.characters.Card
import com.github.nacabaro.vbhelper.dtos.CardDtos
import com.github.nacabaro.vbhelper.dtos.CharacterDtos


M app/src/main/java/com/github/nacabaro/vbhelper/source/StorageRepository.kt => app/src/main/java/com/github/nacabaro/vbhelper/source/StorageRepository.kt +1 -5
@@ 19,14 19,10 @@ class StorageRepository (
        return db.userCharacterDao().getBeData(id)
    }

    fun getTransformationHistory(characterId: Long): List<CharacterDtos.TransformationHistory>? {
    suspend fun getTransformationHistory(characterId: Long): List<CharacterDtos.TransformationHistory>? {
        return db.userCharacterDao().getTransformationHistory(characterId)
    }

    suspend fun getCharacterData(id: Long): CharacterDtos.DiMInfo {
        return db.characterDao().getCharacterInfo(id)
    }

    suspend fun getActiveCharacter(): CharacterDtos.CharacterWithSprites? {
        return db.userCharacterDao().getActiveCharacter()
    }

D app/src/main/java/com/github/nacabaro/vbhelper/utils/CharacterToNFCCharacter.kt => app/src/main/java/com/github/nacabaro/vbhelper/utils/CharacterToNFCCharacter.kt +0 -99
@@ 1,99 0,0 @@
package com.github.nacabaro.vbhelper.utils

import android.content.Context
import android.icu.util.Calendar
import android.icu.util.GregorianCalendar
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.di.VBHelper
import com.github.nacabaro.vbhelper.source.StorageRepository
import java.util.Date

suspend fun characterToNfc(context: Context, characterId: Long): NfcCharacter? {
    val app = context.applicationContext as VBHelper
    val database = app.container.db
    val storageRepository = StorageRepository(database)
    val userCharacter = storageRepository.getSingleCharacter(characterId)
    val characterInfo = storageRepository.getCharacterData(characterId)

    if (userCharacter.characterType == DeviceType.BEDevice) {
        val beData = storageRepository.getCharacterBeData(characterId)
        val transformationHistory = storageRepository
            .getTransformationHistory(characterId)!!
            .map {
                val date = Date(it.transformationDate)
                val calendar = GregorianCalendar()
                calendar.time = date

                NfcCharacter.Transformation(
                    toCharIndex = it.monIndex.toUByte(),
                    year = calendar
                        .get(Calendar.YEAR)
                        .toUShort(),
                    month = calendar
                        .get(Calendar.MONTH)
                        .toUByte(),
                    day = calendar
                        .get(Calendar.DAY_OF_MONTH)
                        .toUByte()
                )
            }.toTypedArray()

        val paddedTransformationArray = padTransformationArray(transformationHistory)

        val nfcData = BENfcCharacter(
            dimId = characterInfo.cardId.toUShort(),
            charIndex = characterInfo.charId.toUShort(),
            stage = userCharacter.stage.toByte(),
            attribute = userCharacter.attribute,
            ageInDays = userCharacter.ageInDays.toByte(),
            nextAdventureMissionStage = userCharacter.nextAdventureMissionStage.toByte(),
            mood = userCharacter.mood.toByte(),
            vitalPoints = userCharacter.vitalPoints.toUShort(),
            itemEffectMentalStateValue = beData.itemEffectMentalStateValue.toByte(),
            itemEffectMentalStateMinutesRemaining = beData.itemEffectMentalStateMinutesRemaining.toByte(),
            itemEffectActivityLevelValue = beData.itemEffectActivityLevelValue.toByte(),
            itemEffectActivityLevelMinutesRemaining = beData.itemEffectActivityLevelMinutesRemaining.toByte(),
            itemEffectVitalPointsChangeValue = beData.itemEffectVitalPointsChangeValue.toByte(),
            itemEffectVitalPointsChangeMinutesRemaining = beData.itemEffectVitalPointsChangeMinutesRemaining.toByte(),
            transformationCountdownInMinutes = userCharacter.transformationCountdown.toUShort(),
            injuryStatus = userCharacter.injuryStatus,
            trainingPp = userCharacter.trophies.toUShort(),
            currentPhaseBattlesWon = userCharacter.currentPhaseBattlesWon.toUShort(),
            currentPhaseBattlesLost = userCharacter.currentPhaseBattlesLost.toUShort(),
            totalBattlesWon = userCharacter.totalBattlesWon.toUShort(),
            totalBattlesLost = userCharacter.totalBattlesLost.toUShort(),
            activityLevel = userCharacter.activityLevel.toByte(),
            heartRateCurrent = userCharacter.heartRateCurrent.toUByte(),
            transformationHistory = paddedTransformationArray,
            vitalHistory = Array(7) {
                NfcCharacter.DailyVitals(0u, 0u, 0u, 0u)
            },
            appReserved1 = ByteArray(12) {0},
            appReserved2 = Array(3) {0u},
            trainingHp = beData.trainingHp.toUShort(),
            trainingAp = beData.trainingAp.toUShort(),
            trainingBp = beData.trainingBp.toUShort(),
            remainingTrainingTimeInMinutes = beData.remainingTrainingTimeInMinutes.toUShort(),
            abilityRarity = beData.abilityRarity,
            abilityType = beData.abilityType.toUShort(),
            abilityBranch = beData.abilityBranch.toUShort(),
            abilityReset = beData.abilityReset.toByte(),
            rank = beData.rank.toByte(),
            itemType = beData.itemType.toByte(),
            itemMultiplier = beData.itemMultiplier.toByte(),
            itemRemainingTime = beData.itemRemainingTime.toByte(),
            otp0 = byteArrayOf(8),
            otp1 = byteArrayOf(8),
            characterCreationFirmwareVersion = FirmwareVersion(
                minorVersion = beData.minorVersion.toByte(),
                majorVersion = beData.majorVersion.toByte()
            )
        )

        return nfcData
    }

    return null
}
\ No newline at end of file

D app/src/main/java/com/github/nacabaro/vbhelper/utils/padTransformationArray.kt => app/src/main/java/com/github/nacabaro/vbhelper/utils/padTransformationArray.kt +0 -23
@@ 1,23 0,0 @@
package com.github.nacabaro.vbhelper.utils

import com.github.cfogrady.vbnfc.data.NfcCharacter

fun padTransformationArray(
    transformationArray: Array<NfcCharacter.Transformation>
): Array<NfcCharacter.Transformation> {
    if (transformationArray.size >= 8) {
        return transformationArray
    }

    val paddedArray = Array(8) {
        NfcCharacter.Transformation(
            toCharIndex = 255u,
            year = 65535u,
            month = 255u,
            day = 255u
        )
    }

    System.arraycopy(transformationArray, 0, paddedArray, 0, transformationArray.size)
    return paddedArray
}
\ No newline at end of file