~cytrogen/vbhelper

aac05a3b1c499cd75dc9cf43c9047c39a00a5886 — nacabaro 7 months ago 2586190 + b827fdc
Merge pull request #37 from nacabaro/card/import_more

Agarrate, que vienen curvas
20 files changed, 698 insertions(+), 174 deletions(-)

M app/src/main/java/com/github/nacabaro/vbhelper/daos/AdventureDao.kt
M app/src/main/java/com/github/nacabaro/vbhelper/daos/CardDao.kt
M app/src/main/java/com/github/nacabaro/vbhelper/daos/CharacterDao.kt
M app/src/main/java/com/github/nacabaro/vbhelper/daos/DexDao.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
M app/src/main/java/com/github/nacabaro/vbhelper/domain/Evolutions.kt
M app/src/main/java/com/github/nacabaro/vbhelper/domain/UserMonsters.kt
A app/src/main/java/com/github/nacabaro/vbhelper/domain/card/Background.kt
R app/src/main/java/com/github/nacabaro/vbhelper/domain/{characters/Character => card/CharacterData}.kt
A app/src/main/java/com/github/nacabaro/vbhelper/domain/card/PossibleTransformations.kt
M app/src/main/java/com/github/nacabaro/vbhelper/domain/characters/Dex.kt
M app/src/main/java/com/github/nacabaro/vbhelper/domain/device_data/TransformationHistory.kt
M app/src/main/java/com/github/nacabaro/vbhelper/domain/device_data/UserCharacter.kt
M app/src/main/java/com/github/nacabaro/vbhelper/dtos/CharacterDtos.kt
M app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardViewScreen.kt
A app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/dialogs/DexCharaDetailsDialog.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/utils/BitmapData.kt
M app/src/main/java/com/github/nacabaro/vbhelper/daos/AdventureDao.kt => app/src/main/java/com/github/nacabaro/vbhelper/daos/AdventureDao.kt +6 -4
@@ 18,7 18,8 @@ interface AdventureDao {
    """)
    fun getAdventureCount(): Int

    @Query("""
    @Query(
        """
        SELECT
            uc.*,
            c.stage,


@@ 30,11 31,12 @@ interface AdventureDao {
            a.finishesAdventure AS finishesAdventure,
            a.originalDuration AS originalTimeInMinutes
        FROM UserCharacter uc
        JOIN Character c ON uc.charId = c.id
        JOIN CharacterData c ON uc.charId = c.id
        JOIN Sprite s ON s.id = c.spriteId
        JOIN Card d ON c.dimId = d.id
        JOIN Card d ON c.cardId = d.id
        JOIN Adventure a ON uc.id = a.characterId
    """)
    """
    )
    suspend fun getAdventureCharacters(): List<CharacterDtos.AdventureCharacterWithSprites>

    @Query("""

M app/src/main/java/com/github/nacabaro/vbhelper/daos/CardDao.kt => app/src/main/java/com/github/nacabaro/vbhelper/daos/CardDao.kt +5 -3
@@ 17,13 17,15 @@ interface CardDao {
    @Query("SELECT * FROM Card WHERE id = :id")
    fun getCardById(id: Long): Card?

    @Query("""
    @Query(
        """
        SELECT ca.* 
        FROM Card ca
        JOIN Character ch ON ca.id = ch.dimId
        JOIN CharacterData ch ON ca.id = ch.cardId
        JOIN UserCharacter uc ON ch.id = uc.charId
        WHERE uc.id = :id
    """)
    """
    )
    suspend fun getCardByCharacterId(id: Long): Card

    @Query("UPDATE Card SET name = :newName WHERE id = :id")

M app/src/main/java/com/github/nacabaro/vbhelper/daos/CharacterDao.kt => app/src/main/java/com/github/nacabaro/vbhelper/daos/CharacterDao.kt +59 -7
@@ 3,17 3,17 @@ package com.github.nacabaro.vbhelper.daos
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.Query
import com.github.nacabaro.vbhelper.domain.characters.Character
import com.github.nacabaro.vbhelper.domain.card.CharacterData
import com.github.nacabaro.vbhelper.domain.characters.Sprite
import com.github.nacabaro.vbhelper.dtos.CharacterDtos

@Dao
interface CharacterDao {
    @Insert
    suspend fun insertCharacter(vararg characterData: Character)
    suspend fun insertCharacter(vararg characterData: CharacterData)

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

    @Insert
    suspend fun insertSprite(vararg sprite: Sprite)


@@ 22,14 22,66 @@ interface CharacterDao {
        """
        SELECT 
            d.cardId as cardId,
            c.monIndex as charId,
            c.charaIndex as charId,
            c.stage as stage,
            c.attribute as attribute
        FROM Character c
        FROM CharacterData c
        JOIN UserCharacter uc ON c.id = uc.charId
        JOIN Card d ON c.dimId = d.id
        JOIN Card d ON c.cardId = d.id
        WHERE c.id = :charId
    """
    )
    suspend fun getCharacterInfo(charId: Long): CharacterDtos.CardCharacterInfo

    @Query(
        """
        INSERT INTO PossibleTransformations (charaId, requiredVitals, requiredTrophies, requiredBattles, requiredWinRate, changeTimerHours, requiredAdventureLevelCompleted, toCharaId)
        SELECT
            (SELECT id FROM CharacterData WHERE charaIndex = :fromChraraIndex AND cardId = :cardId),
            :requiredVitals,
            :requiredTrophies,
            :requiredBattles,
            :requiredWinRate,
            :changeTimerHours,
            :requiredAdventureLevelCompleted,
            (SELECT id FROM CharacterData WHERE charaIndex = :toChraraIndex AND cardId = :cardId)
    """
    )
    suspend fun insertPossibleTransformation(
        fromChraraIndex: Int,
        toChraraIndex: Int,
        cardId: Long,
        requiredVitals: Int,
        requiredTrophies: Int,
        requiredBattles: Int,
        changeTimerHours: Int,
        requiredWinRate: Int,
        requiredAdventureLevelCompleted: Int
    )

    @Query(
        """
        SELECT 
            pt.charaId as fromCharaId,
            pt.toCharaId as charaId,
            s.spriteIdle1 as spriteIdle,
            s.width as spriteWidth,
            s.height as spriteHeight,
            d.discoveredOn as discoveredOn,
            pt.requiredTrophies as requiredTrophies,
            pt.requiredVitals as requiredVitals,
            pt.requiredBattles as requiredBattles,
            pt.requiredWinRate as requiredWinRate,
            pt.changeTimerHours as changeTimerHours,
            pt.requiredAdventureLevelCompleted as requiredAdventureLevelCompleted
        FROM
            PossibleTransformations pt
        JOIN CharacterData c on pt.toCharaId = c.id
        JOIN Sprite s ON s.id = c.spriteId
        LEFT JOIN Dex d ON d.id = pt.toCharaId
        WHERE
            c.cardId = :cardId
    """
    )
    suspend fun getEvolutionRequirementsForCard(cardId: Long): List<CharacterDtos.EvolutionRequirementsWithSpritesAndObtained>
}
\ No newline at end of file

M app/src/main/java/com/github/nacabaro/vbhelper/daos/DexDao.kt => app/src/main/java/com/github/nacabaro/vbhelper/daos/DexDao.kt +27 -13
@@ 7,39 7,53 @@ import com.github.nacabaro.vbhelper.dtos.CharacterDtos

@Dao
interface DexDao {
    @Query("""
    @Query(
        """
        INSERT OR IGNORE INTO Dex(id, discoveredOn)
        VALUES (
            (SELECT id FROM Character WHERE monIndex = :charIndex AND dimId = :cardId), 
            (SELECT id FROM CharacterData WHERE charaIndex = :charIndex AND cardId = :cardId), 
            :discoveredOn
        )
    """)
    """
    )
    fun insertCharacter(charIndex: Int, cardId: Long, discoveredOn: Long)

    @Query("""
    @Query(
        """
        SELECT 
            c.id AS id,
            s.spriteIdle1 AS spriteIdle,
            s.width AS spriteWidth,
            s.height AS spriteHeight,
            d.discoveredOn AS discoveredOn
        FROM Character c
            c.nameSprite AS nameSprite,
            c.nameWidth AS nameSpriteWidth,
            c.nameHeight AS nameSpriteHeight,
            d.discoveredOn AS discoveredOn,
            c.baseHp as baseHp,
            c.baseBp as baseBp,
            c.baseAp as baseAp,
            c.stage as stage,
            c.attribute as attribute
        FROM CharacterData c
        JOIN Sprite s ON c.spriteId = s.id
        LEFT JOIN dex d ON c.id = d.id
        WHERE c.dimId = :cardId
    """)
    suspend fun getSingleCardProgress(cardId: Long): List<CharacterDtos.CardProgress>
        WHERE c.cardId = :cardId
    """
    )
    suspend fun getSingleCardProgress(cardId: Long): List<CharacterDtos.CardCharaProgress>

    @Query("""
    @Query(
        """
        SELECT 
            c.id as cardId,
            c.name as cardName,
            c.logo as cardLogo,
            c.logoWidth as logoWidth,
            c.logoHeight as logoHeight, 
            (SELECT COUNT(*) FROM Character cc WHERE cc.dimId = c.id) AS totalCharacters,
            (SELECT COUNT(*) FROM Dex d JOIN Character cc ON d.id = cc.id WHERE cc.dimId = c.id AND d.discoveredOn IS NOT NULL) AS obtainedCharacters
            (SELECT COUNT(*) FROM CharacterData cc WHERE cc.cardId = c.id) AS totalCharacters,
            (SELECT COUNT(*) FROM Dex d JOIN CharacterData cc ON d.id = cc.id WHERE cc.cardId = c.id AND d.discoveredOn IS NOT NULL) AS obtainedCharacters
        FROM Card c
    """)
    """
    )
    suspend fun getCardsWithProgress(): List<CardDtos.CardProgress>
}
\ 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 +33 -27
@@ 5,7 5,7 @@ import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import androidx.room.Upsert
import com.github.nacabaro.vbhelper.domain.characters.Character
import com.github.nacabaro.vbhelper.domain.card.CharacterData
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


@@ 37,19 37,21 @@ interface UserCharacterDao {
    @Upsert
    fun insertSpecialMissions(vararg specialMissions: SpecialMissions)

    @Query("""
    @Query(
        """
        SELECT 
            c.id AS id,
            s.spriteIdle1 AS spriteIdle,
            s.width AS spriteWidth,
            s.height AS spriteHeight,
            c.monIndex AS monIndex, 
            c.charaIndex AS monIndex, 
            t.transformationDate AS transformationDate
        FROM TransformationHistory t 
        JOIN Character c ON c.id = t.stageId
        JOIN CharacterData c ON c.id = t.stageId
        JOIN Sprite s ON s.id = c.spriteId
        WHERE monId = :monId
    """)
    """
    )
    suspend fun getTransformationHistory(monId: Long): List<CharacterDtos.TransformationHistory>?

    @Query(


@@ 62,14 64,14 @@ interface UserCharacterDao {
            s.spriteIdle2 AS spriteIdle2,
            s.width AS spriteWidth,
            s.height AS spriteHeight,
            c.name as nameSprite,
            c.nameSprite as nameSprite,
            c.nameWidth as nameSpriteWidth,
            c.nameHeight as nameSpriteHeight,
            d.isBEm as isBemCard,
            a.characterId = uc.id as isInAdventure
        FROM UserCharacter uc
        JOIN Character c ON uc.charId = c.id
        JOIN Card d ON  d.id = c.dimId
        JOIN CharacterData c ON uc.charId = c.id
        JOIN Card d ON  d.id = c.cardId
        JOIN Sprite s ON s.id = c.spriteId
        LEFT JOIN Adventure a ON a.characterId = uc.id
        """


@@ 86,18 88,19 @@ interface UserCharacterDao {
            s.spriteIdle2 AS spriteIdle2,
            s.width AS spriteWidth,
            s.height AS spriteHeight,
            c.name as nameSprite,
            c.nameSprite as nameSprite,
            c.nameWidth as nameSpriteWidth,
            c.nameHeight as nameSpriteHeight,
            d.isBEm as isBemCard,
            a.characterId = uc.id as isInAdventure
        FROM UserCharacter uc
        JOIN Character c ON uc.charId = c.id
        JOIN Card d ON c.dimId = d.id
        JOIN CharacterData c ON uc.charId = c.id
        JOIN Card d ON c.cardId = d.id
        JOIN Sprite s ON s.id = c.spriteId
        LEFT JOIN Adventure a ON a.characterId = uc.id
        WHERE uc.id = :id
    """)
    """
    )
    suspend fun getCharacterWithSprites(id: Long): CharacterDtos.CharacterWithSprites

    @Query("SELECT * FROM UserCharacter WHERE id = :id")


@@ 122,19 125,20 @@ interface UserCharacterDao {
            s.spriteIdle2 AS spriteIdle2,
            s.width AS spriteWidth,
            s.height AS spriteHeight,
            c.name as nameSprite,
            c.nameSprite as nameSprite,
            c.nameWidth as nameSpriteWidth,
            c.nameHeight as nameSpriteHeight,
            d.isBEm as isBemCard,
            a.characterId as isInAdventure            
        FROM UserCharacter uc
        JOIN Character c ON uc.charId = c.id
        JOIN Card d ON c.dimId = d.id
        JOIN CharacterData c ON uc.charId = c.id
        JOIN Card d ON c.cardId = d.id
        JOIN Sprite s ON s.id = c.spriteId
        LEFT JOIN Adventure a ON a.characterId = uc.id
        WHERE uc.isActive = 1
        LIMIT 1
    """)
    """
    )
    suspend fun getActiveCharacter(): CharacterDtos.CharacterWithSprites?

    @Query("DELETE FROM UserCharacter WHERE id = :id")


@@ 149,22 153,24 @@ interface UserCharacterDao {
    @Query(
        """
        SELECT c.*
        FROM Character c
        FROM CharacterData c
        join UserCharacter uc on c.id = uc.charId
        where uc.id = :charId
        LIMIT 1
        """
    )
    suspend fun getCharacterInfo(charId: Long): Character
    suspend fun getCharacterInfo(charId: Long): CharacterData


    @Query("""
    @Query(
        """
        INSERT INTO TransformationHistory(monId, stageId, transformationDate)
        VALUES 
            (:monId, 
            (SELECT id FROM Character WHERE monIndex = :stage AND dimId = :dimId),
            (SELECT id FROM CharacterData WHERE charaIndex = :stage AND cardId = :dimId),
            :transformationDate)
    """)
    """
    )
    fun insertTransformation(monId: Long, stage: Int, dimId: Long, transformationDate: Long)

    @Upsert


@@ 183,14 189,14 @@ interface UserCharacterDao {
            s.spriteIdle2 AS spriteIdle2,
            s.width AS spriteWidth,
            s.height AS spriteHeight,
            c.name as nameSprite,
            c.nameSprite as nameSprite,
            c.nameWidth as nameSpriteWidth,
            c.nameHeight as nameSpriteHeight,
            d.isBEm as isBemCard,
            a.characterId = uc.id as isInAdventure
        FROM UserCharacter uc
        JOIN Character c ON uc.charId = c.id
        JOIN Card d ON  d.id = c.dimId
        JOIN CharacterData c ON uc.charId = c.id
        JOIN Card d ON  d.id = c.cardId
        JOIN Sprite s ON s.id = c.spriteId
        LEFT JOIN Adventure a ON a.characterId = uc.id
        WHERE uc.characterType = "BEDevice"


@@ 208,14 214,14 @@ interface UserCharacterDao {
            s.spriteIdle2 AS spriteIdle2,
            s.width AS spriteWidth,
            s.height AS spriteHeight,
            c.name as nameSprite,
            c.nameSprite as nameSprite,
            c.nameWidth as nameSpriteWidth,
            c.nameHeight as nameSpriteHeight,
            d.isBEm as isBemCard,
            a.characterId = uc.id as isInAdventure
        FROM UserCharacter uc
        JOIN Character c ON uc.charId = c.id
        JOIN Card d ON  d.id = c.dimId
        JOIN CharacterData c ON uc.charId = c.id
        JOIN Card d ON  d.id = c.cardId
        JOIN Sprite s ON s.id = c.spriteId
        LEFT JOIN Adventure a ON a.characterId = uc.id
        WHERE uc.characterType = "VBDevice"

M app/src/main/java/com/github/nacabaro/vbhelper/database/AppDatabase.kt => app/src/main/java/com/github/nacabaro/vbhelper/database/AppDatabase.kt +7 -3
@@ 11,9 11,11 @@ import com.github.nacabaro.vbhelper.daos.ItemDao
import com.github.nacabaro.vbhelper.daos.SpecialMissionDao
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.card.Background
import com.github.nacabaro.vbhelper.domain.card.CharacterData
import com.github.nacabaro.vbhelper.domain.card.Card
import com.github.nacabaro.vbhelper.domain.card.CardProgress
import com.github.nacabaro.vbhelper.domain.card.PossibleTransformations
import com.github.nacabaro.vbhelper.domain.characters.Sprite
import com.github.nacabaro.vbhelper.domain.characters.Adventure
import com.github.nacabaro.vbhelper.domain.characters.Dex


@@ 30,7 32,7 @@ import com.github.nacabaro.vbhelper.domain.items.Items
    entities = [
        Card::class,
        CardProgress::class,
        Character::class,
        CharacterData::class,
        Sprite::class,
        UserCharacter::class,
        BECharacterData::class,


@@ 40,7 42,9 @@ import com.github.nacabaro.vbhelper.domain.items.Items
        VitalsHistory::class,
        Dex::class,
        Items::class,
        Adventure::class
        Adventure::class,
        Background::class,
        PossibleTransformations::class
    ]
)
abstract class AppDatabase : RoomDatabase() {

M app/src/main/java/com/github/nacabaro/vbhelper/domain/Evolutions.kt => app/src/main/java/com/github/nacabaro/vbhelper/domain/Evolutions.kt +0 -1
@@ 3,7 3,6 @@ package com.github.nacabaro.vbhelper.domain
import androidx.room.Entity
import androidx.room.ForeignKey
import androidx.room.PrimaryKey
import com.github.nacabaro.vbhelper.domain.characters.Character

@Entity(
    foreignKeys = [

M app/src/main/java/com/github/nacabaro/vbhelper/domain/UserMonsters.kt => app/src/main/java/com/github/nacabaro/vbhelper/domain/UserMonsters.kt +0 -1
@@ 3,7 3,6 @@ package com.github.nacabaro.vbhelper.domain
import androidx.room.Entity
import androidx.room.ForeignKey
import androidx.room.PrimaryKey
import com.github.nacabaro.vbhelper.domain.characters.Character

@Entity(
    foreignKeys = [

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

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

@Entity(
    foreignKeys = [
        ForeignKey(
            entity = Card::class,
            parentColumns = ["id"],
            childColumns = ["cardId"],
            onDelete = ForeignKey.CASCADE
        )
    ]
)
data class Background (
    @PrimaryKey(autoGenerate = true) val id: Long,
    val cardId: Long,
    val background: ByteArray,
    val backgroundWidth: Int,
    val backgroundHeight: Int
)
\ No newline at end of file

R app/src/main/java/com/github/nacabaro/vbhelper/domain/characters/Character.kt => app/src/main/java/com/github/nacabaro/vbhelper/domain/card/CharacterData.kt +9 -9
@@ 1,17 1,17 @@
package com.github.nacabaro.vbhelper.domain.characters
package com.github.nacabaro.vbhelper.domain.card

import androidx.room.Entity
import androidx.room.PrimaryKey
import androidx.room.ForeignKey
import androidx.room.PrimaryKey
import com.github.cfogrady.vbnfc.data.NfcCharacter
import com.github.nacabaro.vbhelper.domain.card.Card
import com.github.nacabaro.vbhelper.domain.characters.Sprite

@Entity(
    foreignKeys = [
        ForeignKey(
            entity = Card::class,
            parentColumns = ["id"],
            childColumns = ["dimId"],
            childColumns = ["cardId"],
            onDelete = ForeignKey.CASCADE
        ),
        ForeignKey(


@@ 28,17 28,17 @@ import com.github.nacabaro.vbhelper.domain.card.Card
 * and monIndex.
 * TODO: Customs will mean this should be unique per cardName and monIndex
 */
data class Character (
data class CharacterData (
    @PrimaryKey(autoGenerate = true) val id: Long = 0,
    val dimId: Long,
    val cardId: Long,
    val spriteId: Long,
    val monIndex: Int,
    val name: ByteArray,
    val charaIndex: Int,
    val stage: Int, // These should be replaced with enums
    val attribute: NfcCharacter.Attribute, // This one too
    val baseHp: Int,
    val baseBp: Int,
    val baseAp: Int,
    val nameSprite: ByteArray,
    val nameWidth: Int,
    val nameHeight: Int
)
)
\ No newline at end of file

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

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

@Entity(
    foreignKeys = [
        ForeignKey(
            entity = CharacterData::class,
            parentColumns = ["id"],
            childColumns = ["charaId"],
            onDelete = ForeignKey.CASCADE
        ),
        ForeignKey(
            entity = CharacterData::class,
            parentColumns = ["id"],
            childColumns = ["toCharaId"],
            onDelete = ForeignKey.CASCADE
        )
    ]
)
data class PossibleTransformations (
    @PrimaryKey(autoGenerate = true) val id: Long = 0,
    var charaId: Long,
    val requiredVitals: Int,
    val requiredTrophies: Int,
    val requiredBattles: Int,
    val requiredWinRate: Int,
    val changeTimerHours: Int,
    val requiredAdventureLevelCompleted: Int,
    val toCharaId: Long?
)
\ No newline at end of file

M app/src/main/java/com/github/nacabaro/vbhelper/domain/characters/Dex.kt => app/src/main/java/com/github/nacabaro/vbhelper/domain/characters/Dex.kt +2 -1
@@ 3,11 3,12 @@ package com.github.nacabaro.vbhelper.domain.characters
import androidx.room.Entity
import androidx.room.ForeignKey
import androidx.room.PrimaryKey
import com.github.nacabaro.vbhelper.domain.card.CharacterData

@Entity(
    foreignKeys = [
        ForeignKey(
            entity = Character::class,
            entity = CharacterData::class,
            parentColumns = ["id"],
            childColumns = ["id"],
            onDelete = ForeignKey.CASCADE

M app/src/main/java/com/github/nacabaro/vbhelper/domain/device_data/TransformationHistory.kt => app/src/main/java/com/github/nacabaro/vbhelper/domain/device_data/TransformationHistory.kt +2 -2
@@ 3,7 3,7 @@ package com.github.nacabaro.vbhelper.domain.device_data
import androidx.room.Entity
import androidx.room.ForeignKey
import androidx.room.PrimaryKey
import com.github.nacabaro.vbhelper.domain.characters.Character
import com.github.nacabaro.vbhelper.domain.card.CharacterData

@Entity(
    foreignKeys = [


@@ 14,7 14,7 @@ import com.github.nacabaro.vbhelper.domain.characters.Character
            onDelete = ForeignKey.CASCADE
        ),
        ForeignKey(
            entity = Character::class,
            entity = CharacterData::class,
            parentColumns = ["id"],
            childColumns = ["stageId"],
            onDelete = ForeignKey.CASCADE

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 +2 -2
@@ 5,12 5,12 @@ import androidx.room.ForeignKey
import androidx.room.PrimaryKey
import com.github.cfogrady.vbnfc.data.NfcCharacter
import com.github.nacabaro.vbhelper.utils.DeviceType
import com.github.nacabaro.vbhelper.domain.characters.Character
import com.github.nacabaro.vbhelper.domain.card.CharacterData

@Entity(
    foreignKeys = [
        ForeignKey(
            entity = Character::class,
            entity = CharacterData::class,
            parentColumns = ["id"],
            childColumns = ["charId"],
            onDelete = ForeignKey.CASCADE

M app/src/main/java/com/github/nacabaro/vbhelper/dtos/CharacterDtos.kt => app/src/main/java/com/github/nacabaro/vbhelper/dtos/CharacterDtos.kt +31 -1
@@ 50,12 50,27 @@ object CharacterDtos {
        val transformationDate: Long
    )

    data class CardCharaProgress(
        val id: Long,
        val spriteIdle: ByteArray,
        val spriteWidth: Int,
        val spriteHeight: Int,
        val nameSprite: ByteArray,
        val nameSpriteWidth: Int,
        val nameSpriteHeight: Int,
        val discoveredOn: Long?,
        val baseHp: Int,
        val baseBp: Int,
        val baseAp: Int,
        val stage: Int,
        val attribute: NfcCharacter.Attribute,
    )

    data class CardProgress(
        val id: Long,
        val spriteIdle: ByteArray,
        val spriteWidth: Int,
        val spriteHeight: Int,
        val discoveredOn: Long?
    )

    data class AdventureCharacterWithSprites(


@@ 83,4 98,19 @@ object CharacterDtos {
        val finishesAdventure: Long,
        val originalTimeInMinutes: Long
    )

    data class EvolutionRequirementsWithSpritesAndObtained(
        val charaId: Long,
        val fromCharaId: Long,
        val spriteIdle: ByteArray,
        val spriteWidth: Int,
        val spriteHeight: Int,
        val discoveredOn: Long?,
        val requiredTrophies: Int,
        val requiredVitals: Int,
        val requiredBattles: Int,
        val requiredWinRate: Int,
        val changeTimerHours: Int,
        val requiredAdventureLevelCompleted: Int
    )
}
\ No newline at end of file

M app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardViewScreen.kt => app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardViewScreen.kt +23 -3
@@ 16,6 16,7 @@ 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.dtos.CharacterDtos
import com.github.nacabaro.vbhelper.screens.cardScreen.dialogs.DexCharaDetailsDialog
import com.github.nacabaro.vbhelper.source.DexRepository
import kotlinx.coroutines.launch



@@ 28,12 29,18 @@ fun CardViewScreen(
    val application = LocalContext.current.applicationContext as VBHelper
    val dexRepository = DexRepository(application.container.db)

    val characterList = remember { mutableStateOf<List<CharacterDtos.CardProgress>>(emptyList()) }
    val characterList = remember { mutableStateOf<List<CharacterDtos.CardCharaProgress>>(emptyList()) }
    val cardPossibleTransformations = remember { mutableStateOf<List<CharacterDtos.EvolutionRequirementsWithSpritesAndObtained>>(emptyList()) }

    val selectedCharacter = remember { mutableStateOf<CharacterDtos.CardCharaProgress?>(null) }

    LaunchedEffect(dexRepository) {
        coroutineScope.launch {
            val newCharacterList = dexRepository.getCharactersByDimId(dimId)
            val newCharacterList = dexRepository.getCharactersByCardId(dimId)
            characterList.value = newCharacterList

            val newCardPossibleTransformations = dexRepository.getCardPossibleTransformations(dimId)
            cardPossibleTransformations.value = newCardPossibleTransformations
        }
    }



@@ 53,7 60,9 @@ fun CardViewScreen(
        ) {
            items(characterList.value) { character ->
                CharacterEntry(
                    onClick = {  },
                    onClick = {
                        selectedCharacter.value = character
                    },
                    obscure = character.discoveredOn == null,
                    icon = BitmapData(
                        bitmap = character.spriteIdle,


@@ 63,5 72,16 @@ fun CardViewScreen(
                )
            }
        }

        if (selectedCharacter.value != null) {
            DexCharaDetailsDialog(
                currentChara = selectedCharacter.value!!,
                possibleTransformations = cardPossibleTransformations.value,
                obscure = selectedCharacter.value!!.discoveredOn == null,
                onClickClose = {
                    selectedCharacter.value = null
                }
            )
        }
    }
}
\ No newline at end of file

A app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/dialogs/DexCharaDetailsDialog.kt => app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/dialogs/DexCharaDetailsDialog.kt +215 -0
@@ 0,0 1,215 @@
package com.github.nacabaro.vbhelper.screens.cardScreen.dialogs

import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.material3.Button
import androidx.compose.material3.Card
import androidx.compose.material3.CardColors
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.contentColorFor
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.FilterQuality
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import com.github.nacabaro.vbhelper.dtos.CharacterDtos
import com.github.nacabaro.vbhelper.utils.BitmapData
import com.github.nacabaro.vbhelper.utils.getImageBitmap


@Composable
fun DexCharaDetailsDialog(
    currentChara: CharacterDtos.CardCharaProgress,
    possibleTransformations: List<CharacterDtos.EvolutionRequirementsWithSpritesAndObtained>,
    obscure: Boolean,
    onClickClose: () -> Unit
) {
    val nameMultiplier = 3
    val charaMultiplier = 4

    val currentCharaPossibleTransformations = possibleTransformations.filter { it.fromCharaId == currentChara.id }

    val romanNumeralsStage = when (currentChara.stage) {
        1 -> "II"
        2 -> "III"
        3 -> "IV"
        4 -> "V"
        5 -> "VI"
        6 -> "VII"
        else -> "I"
    }

    val charaBitmapData = BitmapData(
        bitmap = currentChara.spriteIdle,
        width = currentChara.spriteWidth,
        height = currentChara.spriteHeight
    )
    val charaImageBitmapData = charaBitmapData.getImageBitmap(
        context = LocalContext.current,
        multiplier = charaMultiplier,
        obscure = obscure
    )

    val nameBitmapData = BitmapData(
        bitmap = currentChara.nameSprite,
        width = currentChara.nameSpriteWidth,
        height = currentChara.nameSpriteHeight
    )
    val nameImageBitmapData = nameBitmapData.getImageBitmap(
        context = LocalContext.current,
        multiplier = nameMultiplier,
        obscure = obscure
    )

    Dialog(
        onDismissRequest = onClickClose
    ) {
        Card (
            modifier = Modifier
                .fillMaxWidth()
        ) {
            Column (
                modifier = Modifier
                    .padding(16.dp)
            ) {
                Row (
                    verticalAlignment = Alignment.CenterVertically,
                    modifier = Modifier
                ) {
                    Card (
                        colors = CardColors(
                            containerColor = MaterialTheme.colorScheme.surfaceVariant,
                            contentColor = MaterialTheme.colorScheme.contentColorFor(
                                backgroundColor = MaterialTheme.colorScheme.surfaceVariant,
                            ),
                            disabledContainerColor = MaterialTheme.colorScheme.surfaceVariant,
                            disabledContentColor = MaterialTheme.colorScheme.contentColorFor(
                                backgroundColor = MaterialTheme.colorScheme.surfaceVariant,
                            )
                        )
                    ) {
                        Image(
                            bitmap = charaImageBitmapData.imageBitmap,
                            contentDescription = "Icon",
                            modifier = Modifier
                                .size(charaImageBitmapData.dpWidth)
                                .padding(8.dp),
                            colorFilter = when (obscure) {
                                true -> ColorFilter.tint(color = MaterialTheme.colorScheme.secondary)
                                false -> null
                            },
                            filterQuality = FilterQuality.None
                        )
                    }
                    Spacer(
                        modifier = Modifier
                            .padding(16.dp)
                    )
                    if (!obscure) {
                        Column {
                            Image(
                                bitmap = nameImageBitmapData.imageBitmap,
                                contentDescription = "Icon",
                                modifier = Modifier
                                    .width(nameImageBitmapData.dpWidth)
                                    .height(nameImageBitmapData.dpHeight),
                                filterQuality = FilterQuality.None
                            )
                            Spacer(modifier = Modifier.padding(4.dp))
                            if (currentChara.baseHp != 65535) {
                                Text(
                                    text = "HP: ${currentChara.baseHp}, BP: ${currentChara.baseBp}, AP: ${currentChara.baseAp}"
                                )
                                Text(text = "Stg: ${romanNumeralsStage}, Atr: ${currentChara.attribute.toString().substring(0, 2)}")
                            }
                        }
                    } else {
                        Column {
                            Text(text = "????????????????")
                            Spacer(modifier = Modifier.padding(4.dp))
                            Text(text = "Stg: -, Atr: -")
                            Text(text = "HP: -, BP: -, AP: -")
                        }
                    }
                }
                Spacer(modifier = Modifier.padding(16.dp))
                Column {
                    currentCharaPossibleTransformations.map {
                        val selectedCharaBitmap = BitmapData(
                            bitmap = it.spriteIdle,
                            width = it.spriteWidth,
                            height = it.spriteHeight
                        )
                        val selectedCharaImageBitmap = selectedCharaBitmap.getImageBitmap(
                            context = LocalContext.current,
                            multiplier = 4,
                            obscure = it.discoveredOn == null
                        )

                        Card (
                            modifier = Modifier
                                .padding(vertical = 8.dp)
                        ) {
                            Row (
                                verticalAlignment = Alignment.CenterVertically,
                                modifier = Modifier
                                    .fillMaxWidth()
                            ) {
                                Card (
                                    colors = CardColors(
                                        containerColor = MaterialTheme.colorScheme.surfaceVariant,
                                        contentColor = MaterialTheme.colorScheme.contentColorFor(
                                            backgroundColor = MaterialTheme.colorScheme.surfaceVariant,
                                        ),
                                        disabledContainerColor = MaterialTheme.colorScheme.surfaceVariant,
                                        disabledContentColor = MaterialTheme.colorScheme.contentColorFor(
                                            backgroundColor = MaterialTheme.colorScheme.surfaceVariant,
                                        )
                                    )
                                ) {
                                    Image(
                                        bitmap = selectedCharaImageBitmap.imageBitmap,
                                        contentDescription = "Icon",
                                        modifier = Modifier
                                            .size(selectedCharaImageBitmap.dpWidth)
                                            .padding(8.dp),
                                        colorFilter = when (it.discoveredOn == null) {
                                            true -> ColorFilter.tint(color = MaterialTheme.colorScheme.secondary)
                                            false -> null
                                        },
                                        filterQuality = FilterQuality.None
                                    )
                                }
                                Spacer(
                                    modifier = Modifier
                                        .padding(16.dp)
                                )
                                Column {
                                    Text("Tr: ${it.requiredTrophies}; Bt: ${it.requiredBattles}; Vr: ${it.requiredVitals}; Wr: ${it.requiredWinRate}%; Ct: ${it.changeTimerHours}h")
                                    Text("AdvLvl ${it.requiredAdventureLevelCompleted + 1}")
                                }
                            }
                        }
                    }
                }

                Button(
                    onClick = onClickClose
                ) {
                    Text("Close")
                }
            }
        }
    }
}
\ 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 +185 -96
@@ 18,7 18,7 @@ import com.github.nacabaro.vbhelper.di.VBHelper
import com.github.nacabaro.vbhelper.domain.characters.Sprite
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.domain.card.CharacterData
import com.github.nacabaro.vbhelper.source.ApkSecretsImporter
import com.github.nacabaro.vbhelper.source.SecretsImporter
import com.github.nacabaro.vbhelper.source.SecretsRepository


@@ 109,10 109,189 @@ class SettingsScreenControllerImpl(
        filePickerCard.launch(arrayOf("*/*"))
    }

    private suspend fun importEvoData(
        cardId: Long,
        card: com.github.cfogrady.vb.dim.card.Card<*, *, *, *, *, *>
    ) {
        for (index in 0 until card.transformationRequirements.transformationEntries.size) {
            val evo = card.transformationRequirements.transformationEntries[index]

            var transformationTimerHours: Int
            var unlockAdventureLevel: Int

            if (card is BemCard) {
                transformationTimerHours = card
                    .transformationRequirements
                    .transformationEntries[index]
                    .minutesUntilTransformation / 60
                unlockAdventureLevel = if (
                    card
                        .transformationRequirements
                        .transformationEntries[index]
                        .requiredCompletedAdventureLevel == 65535
                ) {
                    0
                } else {
                    card
                        .transformationRequirements
                        .transformationEntries[index]
                        .requiredCompletedAdventureLevel
                }
            } else {
                transformationTimerHours = (card as DimCard)
                    .transformationRequirements
                    .transformationEntries[index]
                    .hoursUntilEvolution
                unlockAdventureLevel = if (
                    card
                        .adventureLevels
                        .levels
                        .last()
                        .bossCharacterIndex == card.transformationRequirements.transformationEntries[index].toCharacterIndex
                ) {
                    14
                    /*
                    Magic number incoming!!

                    In the case of DiMCards, stage 15 is the one that unlocks the locked character.
                    We know it is a locked character if the last adventure level's boss character index
                    is the current index. If it is, we add stage 15 complete as a requirement for transformation.
                     */
                } else {
                    0
                    /*
                    Another magic number...

                    The rest of the characters are not locked.
                     */
                }
            }

            database
                .characterDao()
                .insertPossibleTransformation(
                    cardId = cardId,
                    fromChraraIndex = evo.fromCharacterIndex,
                    toChraraIndex = evo.toCharacterIndex,
                    requiredVitals = evo.requiredVitalValues,
                    requiredTrophies = evo.requiredTrophies,
                    requiredBattles = evo.requiredBattles,
                    requiredWinRate = evo.requiredWinRatio,
                    requiredAdventureLevelCompleted = unlockAdventureLevel,
                    changeTimerHours = transformationTimerHours
                )
        }
    }

    private suspend fun importCharacterData(
        cardId: Long,
        card: com.github.cfogrady.vb.dim.card.Card<*, *, *, *, *, *>
    ) {
        var spriteCounter = when (card is BemCard) {
            true -> 54
            false -> 10
        }

        val domainCharacters = mutableListOf<CharacterData>()

        val characters = card
            .characterStats
            .characterEntries

        for (index in 0 until characters.size) {
            var domainSprite: Sprite?
            if (index < 2 && card is DimCard) {
                domainSprite = Sprite(
                    width = card.spriteData.sprites[spriteCounter + 1].spriteDimensions.width,
                    height = card.spriteData.sprites[spriteCounter + 1].spriteDimensions.height,
                    spriteIdle1 = card.spriteData.sprites[spriteCounter + 1].pixelData,
                    spriteIdle2 = card.spriteData.sprites[spriteCounter + 2].pixelData,
                    spriteWalk1 = card.spriteData.sprites[spriteCounter + 1].pixelData,
                    spriteWalk2 = card.spriteData.sprites[spriteCounter + 3].pixelData,
                    spriteRun1 = card.spriteData.sprites[spriteCounter + 1].pixelData,
                    spriteRun2 = card.spriteData.sprites[spriteCounter + 3].pixelData,
                    spriteTrain1 = card.spriteData.sprites[spriteCounter + 1].pixelData,
                    spriteTrain2 = card.spriteData.sprites[spriteCounter + 3].pixelData,
                    spriteHappy = card.spriteData.sprites[spriteCounter + 4].pixelData,
                    spriteSleep = card.spriteData.sprites[spriteCounter + 5].pixelData,
                    spriteAttack = card.spriteData.sprites[spriteCounter + 2].pixelData,
                    spriteDodge = card.spriteData.sprites[spriteCounter + 3].pixelData
                )
            } else {
                domainSprite = Sprite(
                    width = card.spriteData.sprites[spriteCounter + 1].spriteDimensions.width,
                    height = card.spriteData.sprites[spriteCounter + 1].spriteDimensions.height,
                    spriteIdle1 = card.spriteData.sprites[spriteCounter + 1].pixelData,
                    spriteIdle2 = card.spriteData.sprites[spriteCounter + 2].pixelData,
                    spriteWalk1 = card.spriteData.sprites[spriteCounter + 3].pixelData,
                    spriteWalk2 = card.spriteData.sprites[spriteCounter + 4].pixelData,
                    spriteRun1 = card.spriteData.sprites[spriteCounter + 5].pixelData,
                    spriteRun2 = card.spriteData.sprites[spriteCounter + 6].pixelData,
                    spriteTrain1 = card.spriteData.sprites[spriteCounter + 7].pixelData,
                    spriteTrain2 = card.spriteData.sprites[spriteCounter + 8].pixelData,
                    spriteHappy = card.spriteData.sprites[spriteCounter + 9].pixelData,
                    spriteSleep = card.spriteData.sprites[spriteCounter + 10].pixelData,
                    spriteAttack = card.spriteData.sprites[spriteCounter + 11].pixelData,
                    spriteDodge = card.spriteData.sprites[spriteCounter + 12].pixelData
                )
            }

            val spriteId = database
                .spriteDao()
                .insertSprite(domainSprite)


            domainCharacters.add(
                CharacterData(
                    cardId = cardId,
                    spriteId = spriteId,
                    charaIndex = index,
                    nameSprite = card.spriteData.sprites[spriteCounter].pixelData,
                    stage = characters[index].stage,
                    attribute = NfcCharacter.Attribute.entries[characters[index].attribute],
                    baseHp = characters[index].hp,
                    baseBp = characters[index].dp,
                    baseAp = characters[index].ap,
                    nameWidth = card.spriteData.sprites[spriteCounter].spriteDimensions.width,
                    nameHeight = card.spriteData.sprites[spriteCounter].spriteDimensions.height
                )
            )

            spriteCounter += if (card is BemCard) {
                14
            } else {
                when (index) {
                    0 -> 6
                    1 -> 7
                    else -> 14
                }
            }
        }

        database
            .characterDao()
            .insertCharacter(*domainCharacters.toTypedArray())
    }

    private fun updateCardProgress(
        cardId: Long,
    ) {
        val cardProgress = CardProgress(
            cardId = cardId,
            currentStage = 0,
            unlocked = false
        )

        database
            .cardProgressDao()
            .updateDimProgress(cardProgress)
    }

    private fun importCard(uri: Uri) {
        context.lifecycleScope.launch(Dispatchers.IO) {
            val contentResolver = context.contentResolver
            val inputStream = contentResolver.openInputStream(uri)

            inputStream.use { fileReader ->
                val dimReader = DimReader()
                val card = dimReader.readCard(fileReader, false)


@@ 120,112 299,22 @@ class SettingsScreenControllerImpl(
                val cardModel = Card(
                    cardId = card.header.dimId,
                    logo = card.spriteData.sprites[0].pixelData,
                    name = card.spriteData.text, // TODO Make user write card name
                    name = card.spriteData.text,
                    stageCount = card.adventureLevels.levels.size,
                    logoHeight = card.spriteData.sprites[0].height,
                    logoWidth = card.spriteData.sprites[0].width,
                    isBEm = card is BemCard
                )

                val dimId = database
                val cardId = database
                    .cardDao()
                    .insertNewCard(cardModel)

                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
                    false -> 10
                }
                updateCardProgress(cardId = cardId)

                val domainCharacters = mutableListOf<Character>()

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

                    if (index < 2 && card is DimCard) {
                        domainSprite = Sprite(
                            width = card.spriteData.sprites[spriteCounter + 1].spriteDimensions.width,
                            height = card.spriteData.sprites[spriteCounter + 1].spriteDimensions.height,
                            spriteIdle1 = card.spriteData.sprites[spriteCounter + 1].pixelData,
                            spriteIdle2 = card.spriteData.sprites[spriteCounter + 2].pixelData,
                            spriteWalk1 = card.spriteData.sprites[spriteCounter + 1].pixelData,
                            spriteWalk2 = card.spriteData.sprites[spriteCounter + 3].pixelData,
                            spriteRun1 = card.spriteData.sprites[spriteCounter + 1].pixelData,
                            spriteRun2 = card.spriteData.sprites[spriteCounter + 3].pixelData,
                            spriteTrain1 = card.spriteData.sprites[spriteCounter + 1].pixelData,
                            spriteTrain2 = card.spriteData.sprites[spriteCounter + 3].pixelData,
                            spriteHappy = card.spriteData.sprites[spriteCounter + 4].pixelData,
                            spriteSleep = card.spriteData.sprites[spriteCounter + 5].pixelData,
                            spriteAttack = card.spriteData.sprites[spriteCounter + 2].pixelData,
                            spriteDodge = card.spriteData.sprites[spriteCounter + 3].pixelData
                        )
                    } else {
                        domainSprite = Sprite(
                            width = card.spriteData.sprites[spriteCounter + 1].spriteDimensions.width,
                            height = card.spriteData.sprites[spriteCounter + 1].spriteDimensions.height,
                            spriteIdle1 = card.spriteData.sprites[spriteCounter + 1].pixelData,
                            spriteIdle2 = card.spriteData.sprites[spriteCounter + 2].pixelData,
                            spriteWalk1 = card.spriteData.sprites[spriteCounter + 3].pixelData,
                            spriteWalk2 = card.spriteData.sprites[spriteCounter + 4].pixelData,
                            spriteRun1 = card.spriteData.sprites[spriteCounter + 5].pixelData,
                            spriteRun2 = card.spriteData.sprites[spriteCounter + 6].pixelData,
                            spriteTrain1 = card.spriteData.sprites[spriteCounter + 7].pixelData,
                            spriteTrain2 = card.spriteData.sprites[spriteCounter + 8].pixelData,
                            spriteHappy = card.spriteData.sprites[spriteCounter + 9].pixelData,
                            spriteSleep = card.spriteData.sprites[spriteCounter + 10].pixelData,
                            spriteAttack = card.spriteData.sprites[spriteCounter + 11].pixelData,
                            spriteDodge = card.spriteData.sprites[spriteCounter + 12].pixelData
                        )
                    }


                    val spriteId = database
                        .spriteDao()
                        .insertSprite(domainSprite)

                    domainCharacters.add(
                        Character(
                            dimId = dimId,
                            spriteId = spriteId,
                            monIndex = index,
                            name = card.spriteData.sprites[spriteCounter].pixelData,
                            stage = characters[index].stage,
                            attribute = NfcCharacter.Attribute.entries[characters[index].attribute],
                            baseHp = characters[index].hp,
                            baseBp = characters[index].dp,
                            baseAp = characters[index].ap,
                            nameWidth = card.spriteData.sprites[spriteCounter].spriteDimensions.width,
                            nameHeight = card.spriteData.sprites[spriteCounter].spriteDimensions.height,
                        )
                    )

                    spriteCounter += if (card is BemCard) {
                        14
                    } else {
                        when (index) {
                            0 -> 6
                            1 -> 7
                            else -> 14
                        }
                    }
                }
                importCharacterData(cardId, card)

                database
                    .characterDao()
                    .insertCharacter(*domainCharacters.toTypedArray())
                importEvoData(cardId, card)
            }

            inputStream?.close()

M app/src/main/java/com/github/nacabaro/vbhelper/source/DexRepository.kt => app/src/main/java/com/github/nacabaro/vbhelper/source/DexRepository.kt +5 -1
@@ 11,7 11,11 @@ class DexRepository (
        return db.dexDao().getCardsWithProgress()
    }

    suspend fun getCharactersByDimId(cardId: Long): List<CharacterDtos.CardProgress> {
    suspend fun getCharactersByCardId(cardId: Long): List<CharacterDtos.CardCharaProgress> {
        return db.dexDao().getSingleCardProgress(cardId)
    }

    suspend fun getCardPossibleTransformations(cardId: Long): List<CharacterDtos.EvolutionRequirementsWithSpritesAndObtained> {
        return db.characterDao().getEvolutionRequirementsForCard(cardId)
    }
}
\ No newline at end of file

M app/src/main/java/com/github/nacabaro/vbhelper/utils/BitmapData.kt => app/src/main/java/com/github/nacabaro/vbhelper/utils/BitmapData.kt +31 -0
@@ 1,6 1,11 @@
package com.github.nacabaro.vbhelper.utils

import android.content.Context
import android.graphics.Bitmap
import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.github.cfogrady.vb.dim.sprite.SpriteData

// simple, but smooth


@@ 10,6 15,32 @@ data class BitmapData (
    val height: Int
)

data class ImageBitmapData(
    val imageBitmap: ImageBitmap,
    val dpWidth: Dp,
    val dpHeight: Dp
)

fun BitmapData.getImageBitmap(
    context: Context,
    multiplier: Int,
    obscure: Boolean
): ImageBitmapData {
    val density: Float = context.resources.displayMetrics.density

    val imageBitmap: ImageBitmap = if (obscure) {
        this.getObscuredBitmap().asImageBitmap()
    } else {
        this.getBitmap().asImageBitmap()
    }

    return ImageBitmapData(
        imageBitmap = imageBitmap,
        dpWidth = (this.width  * multiplier / density).dp,
        dpHeight = (this.height * multiplier / density).dp
    )
}

fun BitmapData.getBitmap(): Bitmap {
    return Bitmap.createBitmap(createARGBIntArray(), this.width, this.height, Bitmap.Config.HARDWARE)
}