From 7160eb792a6e01e77c2644c298ef3e996b188224 Mon Sep 17 00:00:00 2001 From: Nacho Date: Wed, 10 Sep 2025 02:09:15 +0200 Subject: [PATCH 1/8] Adventure Missions progress - Viewing the adventure missions completed in a card is now possible. --- app/build.gradle.kts | 2 +- .../nacabaro/vbhelper/daos/AdventureDao.kt | 2 +- .../vbhelper/daos/CardAdventureDao.kt | 58 +++++++++ .../github/nacabaro/vbhelper/daos/CardDao.kt | 2 +- .../nacabaro/vbhelper/daos/CardFusionsDao.kt | 32 +++++ .../nacabaro/vbhelper/daos/CardProgressDao.kt | 2 +- .../nacabaro/vbhelper/daos/CharacterDao.kt | 16 +-- .../github/nacabaro/vbhelper/daos/DexDao.kt | 8 +- .../vbhelper/daos/UserCharacterDao.kt | 20 +-- .../nacabaro/vbhelper/database/AppDatabase.kt | 12 +- .../vbhelper/domain/card/CardAdventure.kt | 32 +++++ .../{CharacterData.kt => CardCharacter.kt} | 2 +- .../vbhelper/domain/card/CardFusions.kt | 48 +++++++ .../domain/card/PossibleTransformations.kt | 4 +- .../vbhelper/domain/characters/Dex.kt | 4 +- .../device_data/TransformationHistory.kt | 4 +- .../domain/device_data/UserCharacter.kt | 4 +- .../github/nacabaro/vbhelper/dtos/CardDtos.kt | 14 ++ .../nacabaro/vbhelper/dtos/CharacterDtos.kt | 2 +- .../vbhelper/navigation/AppNavigation.kt | 14 +- .../vbhelper/navigation/NavigationItems.kt | 1 + .../screens/cardScreen/CardAdventureEntry.kt | 122 ++++++++++++++++++ .../screens/cardScreen/CardAdventureScreen.kt | 63 +++++++++ .../cardScreen/CardScreenController.kt | 4 + .../cardScreen/CardScreenControllerImpl.kt | 14 +- .../screens/cardScreen/CardViewScreen.kt | 18 ++- .../SettingsScreenControllerImpl.kt | 70 +++++++++- 27 files changed, 528 insertions(+), 46 deletions(-) create mode 100644 app/src/main/java/com/github/nacabaro/vbhelper/daos/CardAdventureDao.kt create mode 100644 app/src/main/java/com/github/nacabaro/vbhelper/daos/CardFusionsDao.kt create mode 100644 app/src/main/java/com/github/nacabaro/vbhelper/domain/card/CardAdventure.kt rename app/src/main/java/com/github/nacabaro/vbhelper/domain/card/{CharacterData.kt => CardCharacter.kt} (97%) create mode 100644 app/src/main/java/com/github/nacabaro/vbhelper/domain/card/CardFusions.kt create mode 100644 app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardAdventureEntry.kt create mode 100644 app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardAdventureScreen.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 40b711f..a5fea01 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -15,7 +15,7 @@ android { minSdk = 28 targetSdk = 35 versionCode = 1 - versionName = "Alpha 0.6" + versionName = "Alpha 0.6.1" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/daos/AdventureDao.kt b/app/src/main/java/com/github/nacabaro/vbhelper/daos/AdventureDao.kt index 5197e7d..a1db8f2 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/daos/AdventureDao.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/daos/AdventureDao.kt @@ -31,7 +31,7 @@ interface AdventureDao { a.finishesAdventure AS finishesAdventure, a.originalDuration AS originalTimeInMinutes FROM UserCharacter uc - JOIN CharacterData c ON uc.charId = c.id + JOIN CardCharacter c ON uc.charId = c.id JOIN Sprite s ON s.id = c.spriteId JOIN Card d ON c.cardId = d.id JOIN Adventure a ON uc.id = a.characterId diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/daos/CardAdventureDao.kt b/app/src/main/java/com/github/nacabaro/vbhelper/daos/CardAdventureDao.kt new file mode 100644 index 0000000..03db5ac --- /dev/null +++ b/app/src/main/java/com/github/nacabaro/vbhelper/daos/CardAdventureDao.kt @@ -0,0 +1,58 @@ +package com.github.nacabaro.vbhelper.daos + +import androidx.room.Dao +import androidx.room.Query +import com.github.nacabaro.vbhelper.dtos.CardDtos + +@Dao +interface CardAdventureDao { + @Query(""" + INSERT INTO + CardAdventure (cardId, characterId, steps, bossAp, bossHp, bossDp, bossBp) + SELECT + :cardId, + cc.id, + :steps, + :bossAp, + :bossHp, + :bossDp, + :bossBp + FROM + CardCharacter cc + WHERE + cc.charaIndex = :characterId AND + cc.cardId = :cardId + """) + suspend fun insertNewAdventure( + cardId: Long, + characterId: Int, + steps: Int, + bossAp: Int, + bossHp: Int, + bossDp: Int, + bossBp: Int? + ) + + @Query(""" + SELECT + cc.nameSprite as characterName, + cc.nameWidth as characterNameWidth, + cc.nameHeight as characterNameHeight, + s.spriteIdle1 as characterIdleSprite, + s.width as characterIdleSpriteWidth, + s.height as characterIdleSpriteHeight, + ca.bossAp as characterAp, + ca.bossBp as characterBp, + ca.bossDp as characterDp, + ca.bossHp as characterHp, + ca.steps as steps + FROM CardCharacter cc + JOIN Sprite s ON cc.spriteId = s.id + JOIN CardAdventure ca ON cc.id = ca.characterId + WHERE + cc.cardId = :cardId + """) + suspend fun getAdventureForCard( + cardId: Long + ): List +} \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/daos/CardDao.kt b/app/src/main/java/com/github/nacabaro/vbhelper/daos/CardDao.kt index a55ad14..0401f9e 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/daos/CardDao.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/daos/CardDao.kt @@ -21,7 +21,7 @@ interface CardDao { """ SELECT ca.* FROM Card ca - JOIN CharacterData ch ON ca.id = ch.cardId + JOIN CardCharacter ch ON ca.id = ch.cardId JOIN UserCharacter uc ON ch.id = uc.charId WHERE uc.id = :id """ diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/daos/CardFusionsDao.kt b/app/src/main/java/com/github/nacabaro/vbhelper/daos/CardFusionsDao.kt new file mode 100644 index 0000000..e9f7e34 --- /dev/null +++ b/app/src/main/java/com/github/nacabaro/vbhelper/daos/CardFusionsDao.kt @@ -0,0 +1,32 @@ +package com.github.nacabaro.vbhelper.daos + +import androidx.room.Dao +import androidx.room.Query + +@Dao +interface CardFusionsDao { + @Query(""" + INSERT INTO + CardFusions ( + fromCharaId, + attribute1Fusion, + attribute2Fusion, + attribute3Fusion, + attribute4Fusion + ) + SELECT + (SELECT id FROM CardCharacter WHERE cardId = :cardId AND charaIndex = :fromCharaId), + (SELECT id FROM CardCharacter WHERE cardId = :cardId AND charaIndex = :toCharaIdAttr1), + (SELECT id FROM CardCharacter WHERE cardId = :cardId AND charaIndex = :toCharaIdAttr2), + (SELECT id FROM CardCharacter WHERE cardId = :cardId AND charaIndex = :toCharaIdAttr3), + (SELECT id FROM CardCharacter WHERE cardId = :cardId AND charaIndex = :toCharaIdAttr4) + """) + suspend fun insertNewFusion( + cardId: Long, + fromCharaId: Int, + toCharaIdAttr1: Int, + toCharaIdAttr2: Int, + toCharaIdAttr3: Int, + toCharaIdAttr4: Int + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/daos/CardProgressDao.kt b/app/src/main/java/com/github/nacabaro/vbhelper/daos/CardProgressDao.kt index 788dc77..b5c1d80 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/daos/CardProgressDao.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/daos/CardProgressDao.kt @@ -13,5 +13,5 @@ interface CardProgressDao { @Query( "SELECT currentStage FROM CardProgress WHERE cardId = :cardId" ) - fun getCardProgress(cardId: Int): Int + fun getCardProgress(cardId: Long): Int } \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/daos/CharacterDao.kt b/app/src/main/java/com/github/nacabaro/vbhelper/daos/CharacterDao.kt index a7683f1..56066c5 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/daos/CharacterDao.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/daos/CharacterDao.kt @@ -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.card.CharacterData +import com.github.nacabaro.vbhelper.domain.card.CardCharacter import com.github.nacabaro.vbhelper.domain.characters.Sprite import com.github.nacabaro.vbhelper.dtos.CharacterDtos @Dao interface CharacterDao { @Insert - suspend fun insertCharacter(vararg characterData: CharacterData) + suspend fun insertCharacter(vararg characterData: CardCharacter) - @Query("SELECT * FROM CharacterData WHERE charaIndex = :monIndex AND cardId = :dimId LIMIT 1") - fun getCharacterByMonIndex(monIndex: Int, dimId: Long): CharacterData + @Query("SELECT * FROM CardCharacter WHERE charaIndex = :monIndex AND cardId = :dimId LIMIT 1") + fun getCharacterByMonIndex(monIndex: Int, dimId: Long): CardCharacter @Insert suspend fun insertSprite(vararg sprite: Sprite) @@ -25,7 +25,7 @@ interface CharacterDao { c.charaIndex as charId, c.stage as stage, c.attribute as attribute - FROM CharacterData c + FROM CardCharacter c JOIN UserCharacter uc ON c.id = uc.charId JOIN Card d ON c.cardId = d.id WHERE c.id = :charId @@ -37,14 +37,14 @@ interface CharacterDao { """ INSERT INTO PossibleTransformations (charaId, requiredVitals, requiredTrophies, requiredBattles, requiredWinRate, changeTimerHours, requiredAdventureLevelCompleted, toCharaId) SELECT - (SELECT id FROM CharacterData WHERE charaIndex = :fromChraraIndex AND cardId = :cardId), + (SELECT id FROM CardCharacter WHERE charaIndex = :fromChraraIndex AND cardId = :cardId), :requiredVitals, :requiredTrophies, :requiredBattles, :requiredWinRate, :changeTimerHours, :requiredAdventureLevelCompleted, - (SELECT id FROM CharacterData WHERE charaIndex = :toChraraIndex AND cardId = :cardId) + (SELECT id FROM CardCharacter WHERE charaIndex = :toChraraIndex AND cardId = :cardId) """ ) suspend fun insertPossibleTransformation( @@ -76,7 +76,7 @@ interface CharacterDao { pt.requiredAdventureLevelCompleted as requiredAdventureLevelCompleted FROM PossibleTransformations pt - JOIN CharacterData c on pt.toCharaId = c.id + JOIN CardCharacter c on pt.toCharaId = c.id JOIN Sprite s ON s.id = c.spriteId LEFT JOIN Dex d ON d.id = pt.toCharaId WHERE diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/daos/DexDao.kt b/app/src/main/java/com/github/nacabaro/vbhelper/daos/DexDao.kt index ecba234..f31b365 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/daos/DexDao.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/daos/DexDao.kt @@ -11,7 +11,7 @@ interface DexDao { """ INSERT OR IGNORE INTO Dex(id, discoveredOn) VALUES ( - (SELECT id FROM CharacterData WHERE charaIndex = :charIndex AND cardId = :cardId), + (SELECT id FROM CardCharacter WHERE charaIndex = :charIndex AND cardId = :cardId), :discoveredOn ) """ @@ -34,7 +34,7 @@ interface DexDao { c.baseAp as baseAp, c.stage as stage, c.attribute as attribute - FROM CharacterData c + FROM CardCharacter c JOIN Sprite s ON c.spriteId = s.id LEFT JOIN dex d ON c.id = d.id WHERE c.cardId = :cardId @@ -50,8 +50,8 @@ interface DexDao { c.logo as cardLogo, c.logoWidth as logoWidth, c.logoHeight as logoHeight, - (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 + (SELECT COUNT(*) FROM CardCharacter cc WHERE cc.cardId = c.id) AS totalCharacters, + (SELECT COUNT(*) FROM Dex d JOIN CardCharacter cc ON d.id = cc.id WHERE cc.cardId = c.id AND d.discoveredOn IS NOT NULL) AS obtainedCharacters FROM Card c """ ) diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/daos/UserCharacterDao.kt b/app/src/main/java/com/github/nacabaro/vbhelper/daos/UserCharacterDao.kt index 00cc1df..42fb503 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/daos/UserCharacterDao.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/daos/UserCharacterDao.kt @@ -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.card.CharacterData +import com.github.nacabaro.vbhelper.domain.card.CardCharacter 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 @@ -47,7 +47,7 @@ interface UserCharacterDao { c.charaIndex AS monIndex, t.transformationDate AS transformationDate FROM TransformationHistory t - JOIN CharacterData c ON c.id = t.stageId + JOIN CardCharacter c ON c.id = t.stageId JOIN Sprite s ON s.id = c.spriteId WHERE monId = :monId """ @@ -70,7 +70,7 @@ interface UserCharacterDao { d.isBEm as isBemCard, a.characterId = uc.id as isInAdventure FROM UserCharacter uc - JOIN CharacterData c ON uc.charId = c.id + JOIN CardCharacter 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 @@ -94,7 +94,7 @@ interface UserCharacterDao { d.isBEm as isBemCard, a.characterId = uc.id as isInAdventure FROM UserCharacter uc - JOIN CharacterData c ON uc.charId = c.id + JOIN CardCharacter 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 @@ -131,7 +131,7 @@ interface UserCharacterDao { d.isBEm as isBemCard, a.characterId as isInAdventure FROM UserCharacter uc - JOIN CharacterData c ON uc.charId = c.id + JOIN CardCharacter 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 @@ -153,13 +153,13 @@ interface UserCharacterDao { @Query( """ SELECT c.* - FROM CharacterData c + FROM CardCharacter c join UserCharacter uc on c.id = uc.charId where uc.id = :charId LIMIT 1 """ ) - suspend fun getCharacterInfo(charId: Long): CharacterData + suspend fun getCharacterInfo(charId: Long): CardCharacter @Query( @@ -167,7 +167,7 @@ interface UserCharacterDao { INSERT INTO TransformationHistory(monId, stageId, transformationDate) VALUES (:monId, - (SELECT id FROM CharacterData WHERE charaIndex = :stage AND cardId = :dimId), + (SELECT id FROM CardCharacter WHERE charaIndex = :stage AND cardId = :dimId), :transformationDate) """ ) @@ -195,7 +195,7 @@ interface UserCharacterDao { d.isBEm as isBemCard, a.characterId = uc.id as isInAdventure FROM UserCharacter uc - JOIN CharacterData c ON uc.charId = c.id + JOIN CardCharacter 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 @@ -220,7 +220,7 @@ interface UserCharacterDao { d.isBEm as isBemCard, a.characterId = uc.id as isInAdventure FROM UserCharacter uc - JOIN CharacterData c ON uc.charId = c.id + JOIN CardCharacter 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 diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/database/AppDatabase.kt b/app/src/main/java/com/github/nacabaro/vbhelper/database/AppDatabase.kt index bec8d12..7aa784c 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/database/AppDatabase.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/database/AppDatabase.kt @@ -3,17 +3,21 @@ package com.github.nacabaro.vbhelper.database import androidx.room.Database import androidx.room.RoomDatabase import com.github.nacabaro.vbhelper.daos.AdventureDao +import com.github.nacabaro.vbhelper.daos.CardAdventureDao import com.github.nacabaro.vbhelper.daos.CharacterDao import com.github.nacabaro.vbhelper.daos.DexDao import com.github.nacabaro.vbhelper.daos.CardDao +import com.github.nacabaro.vbhelper.daos.CardFusionsDao import com.github.nacabaro.vbhelper.daos.CardProgressDao 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.card.Background -import com.github.nacabaro.vbhelper.domain.card.CharacterData +import com.github.nacabaro.vbhelper.domain.card.CardCharacter import com.github.nacabaro.vbhelper.domain.card.Card +import com.github.nacabaro.vbhelper.domain.card.CardAdventure +import com.github.nacabaro.vbhelper.domain.card.CardFusions import com.github.nacabaro.vbhelper.domain.card.CardProgress import com.github.nacabaro.vbhelper.domain.card.PossibleTransformations import com.github.nacabaro.vbhelper.domain.characters.Sprite @@ -32,7 +36,9 @@ import com.github.nacabaro.vbhelper.domain.items.Items entities = [ Card::class, CardProgress::class, - CharacterData::class, + CardCharacter::class, + CardAdventure::class, + CardFusions::class, Sprite::class, UserCharacter::class, BECharacterData::class, @@ -57,4 +63,6 @@ abstract class AppDatabase : RoomDatabase() { abstract fun adventureDao(): AdventureDao abstract fun spriteDao(): SpriteDao abstract fun specialMissionDao(): SpecialMissionDao + abstract fun cardAdventureDao(): CardAdventureDao + abstract fun cardFusionsDao(): CardFusionsDao } \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/domain/card/CardAdventure.kt b/app/src/main/java/com/github/nacabaro/vbhelper/domain/card/CardAdventure.kt new file mode 100644 index 0000000..b8f4f56 --- /dev/null +++ b/app/src/main/java/com/github/nacabaro/vbhelper/domain/card/CardAdventure.kt @@ -0,0 +1,32 @@ +package com.github.nacabaro.vbhelper.domain.card + +import androidx.room.Entity +import androidx.room.ForeignKey +import androidx.room.PrimaryKey + +@Entity( + foreignKeys = [ + ForeignKey( + entity = CardCharacter::class, + parentColumns = ["id"], + childColumns = ["characterId"], + onDelete = ForeignKey.CASCADE + ), + ForeignKey( + entity = Card::class, + parentColumns = ["id"], + childColumns = ["cardId"], + onDelete = ForeignKey.CASCADE + ) + ] +) +data class CardAdventure( + @PrimaryKey(autoGenerate = true) val id: Long = 0, + val cardId: Long, + val characterId: Long, + val steps: Int, + val bossHp: Int, + val bossAp: Int, + val bossDp: Int, + val bossBp: Int? +) \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/domain/card/CharacterData.kt b/app/src/main/java/com/github/nacabaro/vbhelper/domain/card/CardCharacter.kt similarity index 97% rename from app/src/main/java/com/github/nacabaro/vbhelper/domain/card/CharacterData.kt rename to app/src/main/java/com/github/nacabaro/vbhelper/domain/card/CardCharacter.kt index 0c3f99d..050cc9a 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/domain/card/CharacterData.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/domain/card/CardCharacter.kt @@ -28,7 +28,7 @@ import com.github.nacabaro.vbhelper.domain.characters.Sprite * and monIndex. * TODO: Customs will mean this should be unique per cardName and monIndex */ -data class CharacterData ( +data class CardCharacter ( @PrimaryKey(autoGenerate = true) val id: Long = 0, val cardId: Long, val spriteId: Long, diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/domain/card/CardFusions.kt b/app/src/main/java/com/github/nacabaro/vbhelper/domain/card/CardFusions.kt new file mode 100644 index 0000000..0ced483 --- /dev/null +++ b/app/src/main/java/com/github/nacabaro/vbhelper/domain/card/CardFusions.kt @@ -0,0 +1,48 @@ +package com.github.nacabaro.vbhelper.domain.card + +import androidx.room.Entity +import androidx.room.ForeignKey +import androidx.room.PrimaryKey + +@Entity( + foreignKeys = [ + ForeignKey( + entity = CardCharacter::class, + parentColumns = ["id"], + childColumns = ["fromCharaId"], + onDelete = ForeignKey.CASCADE + ), + ForeignKey( + entity = CardCharacter::class, + parentColumns = ["id"], + childColumns = ["attribute1Fusion"], + onDelete = ForeignKey.CASCADE + ), + ForeignKey( + entity = CardCharacter::class, + parentColumns = ["id"], + childColumns = ["attribute2Fusion"], + onDelete = ForeignKey.CASCADE + ), + ForeignKey( + entity = CardCharacter::class, + parentColumns = ["id"], + childColumns = ["attribute3Fusion"], + onDelete = ForeignKey.CASCADE + ), + ForeignKey( + entity = CardCharacter::class, + parentColumns = ["id"], + childColumns = ["attribute4Fusion"], + onDelete = ForeignKey.CASCADE + ) + ] +) +data class CardFusions( + @PrimaryKey(autoGenerate = true) val id: Long, + val fromCharaId: Long, + val attribute1Fusion: Long?, + val attribute2Fusion: Long?, + val attribute3Fusion: Long?, + val attribute4Fusion: Long? +) \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/domain/card/PossibleTransformations.kt b/app/src/main/java/com/github/nacabaro/vbhelper/domain/card/PossibleTransformations.kt index 1aabaed..2dc2e8b 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/domain/card/PossibleTransformations.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/domain/card/PossibleTransformations.kt @@ -7,13 +7,13 @@ import androidx.room.PrimaryKey @Entity( foreignKeys = [ ForeignKey( - entity = CharacterData::class, + entity = CardCharacter::class, parentColumns = ["id"], childColumns = ["charaId"], onDelete = ForeignKey.CASCADE ), ForeignKey( - entity = CharacterData::class, + entity = CardCharacter::class, parentColumns = ["id"], childColumns = ["toCharaId"], onDelete = ForeignKey.CASCADE diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/domain/characters/Dex.kt b/app/src/main/java/com/github/nacabaro/vbhelper/domain/characters/Dex.kt index a2f0430..14f0c09 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/domain/characters/Dex.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/domain/characters/Dex.kt @@ -3,12 +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 +import com.github.nacabaro.vbhelper.domain.card.CardCharacter @Entity( foreignKeys = [ ForeignKey( - entity = CharacterData::class, + entity = CardCharacter::class, parentColumns = ["id"], childColumns = ["id"], onDelete = ForeignKey.CASCADE diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/domain/device_data/TransformationHistory.kt b/app/src/main/java/com/github/nacabaro/vbhelper/domain/device_data/TransformationHistory.kt index 10912e0..0a560cc 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/domain/device_data/TransformationHistory.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/domain/device_data/TransformationHistory.kt @@ -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.card.CharacterData +import com.github.nacabaro.vbhelper.domain.card.CardCharacter @Entity( foreignKeys = [ @@ -14,7 +14,7 @@ import com.github.nacabaro.vbhelper.domain.card.CharacterData onDelete = ForeignKey.CASCADE ), ForeignKey( - entity = CharacterData::class, + entity = CardCharacter::class, parentColumns = ["id"], childColumns = ["stageId"], onDelete = ForeignKey.CASCADE diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/domain/device_data/UserCharacter.kt b/app/src/main/java/com/github/nacabaro/vbhelper/domain/device_data/UserCharacter.kt index 66eedf2..e34b57d 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/domain/device_data/UserCharacter.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/domain/device_data/UserCharacter.kt @@ -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.card.CharacterData +import com.github.nacabaro.vbhelper.domain.card.CardCharacter @Entity( foreignKeys = [ ForeignKey( - entity = CharacterData::class, + entity = CardCharacter::class, parentColumns = ["id"], childColumns = ["charId"], onDelete = ForeignKey.CASCADE diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/dtos/CardDtos.kt b/app/src/main/java/com/github/nacabaro/vbhelper/dtos/CardDtos.kt index 48e7c74..252b553 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/dtos/CardDtos.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/dtos/CardDtos.kt @@ -10,4 +10,18 @@ object CardDtos { val totalCharacters: Int, val obtainedCharacters: Int, ) + + data class CardAdventureWithSprites ( + val characterName: ByteArray, + val characterNameWidth: Int, + val characterNameHeight: Int, + val characterIdleSprite: ByteArray, + val characterIdleSpriteWidth: Int, + val characterIdleSpriteHeight: Int, + val characterAp: Int, + val characterBp: Int?, + val characterDp: Int, + val characterHp: Int, + val steps: Int, + ) } \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/dtos/CharacterDtos.kt b/app/src/main/java/com/github/nacabaro/vbhelper/dtos/CharacterDtos.kt index 463d923..b279432 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/dtos/CharacterDtos.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/dtos/CharacterDtos.kt @@ -35,7 +35,7 @@ object CharacterDtos { ) data class CardCharacterInfo( - val cardId: Int, + val cardId: Long, val charId: Int, val stage: Int, val attribute: NfcCharacter.Attribute diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/navigation/AppNavigation.kt b/app/src/main/java/com/github/nacabaro/vbhelper/navigation/AppNavigation.kt index 76d9009..013b083 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/navigation/AppNavigation.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/navigation/AppNavigation.kt @@ -34,6 +34,7 @@ import com.github.nacabaro.vbhelper.screens.itemsScreen.ItemsScreenControllerImp import com.github.nacabaro.vbhelper.screens.settingsScreen.SettingsScreenControllerImpl import com.github.nacabaro.vbhelper.screens.adventureScreen.AdventureScreen import com.github.nacabaro.vbhelper.screens.adventureScreen.AdventureScreenControllerImpl +import com.github.nacabaro.vbhelper.screens.cardScreen.CardAdventureScreen import com.github.nacabaro.vbhelper.screens.cardScreen.CardScreenControllerImpl import com.github.nacabaro.vbhelper.screens.settingsScreen.CreditsScreen import com.github.nacabaro.vbhelper.screens.spriteViewer.SpriteViewerControllerImpl @@ -145,7 +146,7 @@ fun AppNavigation( if (cardId != null) { CardViewScreen( navController = navController, - dimId = cardId.toLong() + cardId = cardId.toLong() ) } } @@ -177,6 +178,17 @@ fun AppNavigation( navController = navController ) } + composable(NavigationItems.CardAdventure.route) { + val cardId = it.arguments?.getString("cardId") + if (cardId != null) { + CardAdventureScreen( + navController = navController, + cardId = cardId.toLong(), + cardScreenController = applicationNavigationHandlers + .cardScreenController + ) + } + } } } } diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/navigation/NavigationItems.kt b/app/src/main/java/com/github/nacabaro/vbhelper/navigation/NavigationItems.kt index 623ae8a..ca187ea 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/navigation/NavigationItems.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/navigation/NavigationItems.kt @@ -11,6 +11,7 @@ sealed class NavigationItems ( object Battles : NavigationItems("Battle", R.drawable.baseline_swords_24, "Battle") object Home : NavigationItems("Home", R.drawable.baseline_cottage_24, "Home") object Dex : NavigationItems("Dex", R.drawable.baseline_menu_book_24, "Dex") + object CardAdventure : NavigationItems("CardAdventure/{cardId}", R.drawable.baseline_fort_24, "Card adventure") object Storage : NavigationItems("Storage", R.drawable.baseline_catching_pokemon_24, "Storage") object Settings : NavigationItems("Settings", R.drawable.baseline_settings_24, "Settings") object Viewer : NavigationItems("Viewer", R.drawable.baseline_image_24, "Viewer") diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardAdventureEntry.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardAdventureEntry.kt new file mode 100644 index 0000000..8927f26 --- /dev/null +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardAdventureEntry.kt @@ -0,0 +1,122 @@ +package com.github.nacabaro.vbhelper.screens.cardScreen + +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.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.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 com.github.nacabaro.vbhelper.dtos.CardDtos +import com.github.nacabaro.vbhelper.utils.BitmapData +import com.github.nacabaro.vbhelper.utils.getImageBitmap + +@Composable +fun CardAdventureEntry( + cardAdventureEntry: CardDtos.CardAdventureWithSprites, + obscure: Boolean +) { + val charaImageBitmapData = BitmapData( + bitmap = cardAdventureEntry.characterIdleSprite, + width = cardAdventureEntry.characterIdleSpriteWidth, + height = cardAdventureEntry.characterIdleSpriteHeight + ).getImageBitmap( + context = LocalContext.current, + multiplier = 4, + obscure = obscure + ) + + val nameImageBitmapData = BitmapData( + bitmap = cardAdventureEntry.characterName, + width = cardAdventureEntry.characterNameWidth, + height = cardAdventureEntry.characterNameHeight + ).getImageBitmap( + context = LocalContext.current, + multiplier = 3, + obscure = obscure + ) + + Card ( + modifier = Modifier + .fillMaxWidth() + .padding(8.dp) + ) { + Row ( + modifier = Modifier + .padding(8.dp) + ){ + 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(8.dp)) + + Column { + if (!obscure) { + Image( + bitmap = nameImageBitmapData.imageBitmap, + contentDescription = "Icon", + modifier = Modifier + .width(nameImageBitmapData.dpWidth) + .height(nameImageBitmapData.dpHeight), + filterQuality = FilterQuality.None + ) + + Spacer(modifier = Modifier.padding(4.dp)) + + Text( + text = "HP: ${cardAdventureEntry.characterHp}, DP: ${cardAdventureEntry.characterDp}, AP: ${cardAdventureEntry.characterAp}" + ) + if (cardAdventureEntry.characterBp != null) { + Text(text = "BP: ${cardAdventureEntry.characterBp}") + } + Text(text = "Steps: ${cardAdventureEntry.steps}") + } else { + Text(text = "????????????????") + Text( + text = "HP: -, BP: -, AP: -" + ) + if (cardAdventureEntry.characterBp != null) { + Text(text = "DP: -") + } + Text(text = "Steps: -") + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardAdventureScreen.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardAdventureScreen.kt new file mode 100644 index 0000000..016d935 --- /dev/null +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardAdventureScreen.kt @@ -0,0 +1,63 @@ +package com.github.nacabaro.vbhelper.screens.cardScreen + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.Scaffold +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.navigation.NavController +import com.github.nacabaro.vbhelper.components.TopBanner +import com.github.nacabaro.vbhelper.dtos.CardDtos +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext + +@Composable +fun CardAdventureScreen( + navController: NavController, + cardScreenController: CardScreenControllerImpl, + cardId: Long +) { + val cardAdventureMissions = remember { mutableStateOf(emptyList()) } + var currentCardAdventure = remember { 0 } + + LaunchedEffect(cardId) { + withContext(Dispatchers.IO) { + cardAdventureMissions.value = + cardScreenController + .getCardAdventureMissions(cardId) + + currentCardAdventure = + cardScreenController + .getCardProgress(cardId) + } + } + + Scaffold ( + topBar = { + TopBanner( + text = "Adventure missions", + onBackClick = { + navController.popBackStack() + } + ) + } + ) { contentPadding -> + Column ( + modifier = Modifier + .padding(top = contentPadding.calculateTopPadding()) + .verticalScroll(state = rememberScrollState()) + ) { + cardAdventureMissions.value.mapIndexed { index, it -> + CardAdventureEntry( + cardAdventureEntry = it, + obscure = index > currentCardAdventure + ) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardScreenController.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardScreenController.kt index d47d888..6e53a60 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardScreenController.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardScreenController.kt @@ -1,6 +1,10 @@ package com.github.nacabaro.vbhelper.screens.cardScreen +import com.github.nacabaro.vbhelper.dtos.CardDtos + interface CardScreenController { fun renameCard(cardId: Long, newName: String, onRenamed: (String) -> Unit) fun deleteCard(cardId: Long, onDeleted: () -> Unit) + suspend fun getCardAdventureMissions(cardId: Long): List + suspend fun getCardProgress(cardId: Long): Int } \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardScreenControllerImpl.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardScreenControllerImpl.kt index 0e54d15..0fe45b5 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardScreenControllerImpl.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardScreenControllerImpl.kt @@ -3,6 +3,7 @@ package com.github.nacabaro.vbhelper.screens.cardScreen import androidx.activity.ComponentActivity import androidx.lifecycle.lifecycleScope import com.github.nacabaro.vbhelper.di.VBHelper +import com.github.nacabaro.vbhelper.dtos.CardDtos import kotlinx.coroutines.launch class CardScreenControllerImpl( @@ -11,7 +12,6 @@ class CardScreenControllerImpl( private val application = componentActivity.applicationContext as VBHelper private val database = application.container.db - override fun renameCard(cardId: Long, newName: String, onRenamed: (String) -> Unit) { componentActivity.lifecycleScope.launch { database @@ -31,4 +31,16 @@ class CardScreenControllerImpl( onDeleted() } } + + override suspend fun getCardAdventureMissions(cardId: Long): List { + return database + .cardAdventureDao() + .getAdventureForCard(cardId) + } + + override suspend fun getCardProgress(cardId: Long): Int { + return database + .cardProgressDao() + .getCardProgress(cardId) + } } \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardViewScreen.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardViewScreen.kt index 330293b..2cd5838 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardViewScreen.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardViewScreen.kt @@ -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.navigation.NavigationItems import com.github.nacabaro.vbhelper.screens.cardScreen.dialogs.DexCharaDetailsDialog import com.github.nacabaro.vbhelper.source.DexRepository import kotlinx.coroutines.launch @@ -23,7 +24,7 @@ import kotlinx.coroutines.launch @Composable fun CardViewScreen( navController: NavController, - dimId: Long + cardId: Long ) { val coroutineScope = rememberCoroutineScope() val application = LocalContext.current.applicationContext as VBHelper @@ -36,10 +37,10 @@ fun CardViewScreen( LaunchedEffect(dexRepository) { coroutineScope.launch { - val newCharacterList = dexRepository.getCharactersByCardId(dimId) + val newCharacterList = dexRepository.getCharactersByCardId(cardId) characterList.value = newCharacterList - val newCardPossibleTransformations = dexRepository.getCardPossibleTransformations(dimId) + val newCardPossibleTransformations = dexRepository.getCardPossibleTransformations(cardId) cardPossibleTransformations.value = newCardPossibleTransformations } } @@ -50,6 +51,17 @@ fun CardViewScreen( text = "Discovered characters", onBackClick = { navController.popBackStack() + }, + onAdventureClick = { + navController + .navigate(route = NavigationItems + .CardAdventure + .route + .replace( + "{cardId}", + cardId.toString() + ) + ) } ) } diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/settingsScreen/SettingsScreenControllerImpl.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/settingsScreen/SettingsScreenControllerImpl.kt index 95b2437..cadae13 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/settingsScreen/SettingsScreenControllerImpl.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/settingsScreen/SettingsScreenControllerImpl.kt @@ -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.card.CharacterData +import com.github.nacabaro.vbhelper.domain.card.CardCharacter import com.github.nacabaro.vbhelper.source.ApkSecretsImporter import com.github.nacabaro.vbhelper.source.SecretsImporter import com.github.nacabaro.vbhelper.source.SecretsRepository @@ -192,7 +192,7 @@ class SettingsScreenControllerImpl( false -> 10 } - val domainCharacters = mutableListOf() + val domainCharacters = mutableListOf() val characters = card .characterStats @@ -242,7 +242,7 @@ class SettingsScreenControllerImpl( domainCharacters.add( - CharacterData( + CardCharacter( cardId = cardId, spriteId = spriteId, charaIndex = index, @@ -273,6 +273,66 @@ class SettingsScreenControllerImpl( .insertCharacter(*domainCharacters.toTypedArray()) } + private suspend fun importAdventureMissions( + cardId: Long, + card: com.github.cfogrady.vb.dim.card.Card<*, *, *, *, *, *> + ) { + Log.d("importAdventureMissions", "Importing adventure missions") + if (card is BemCard) { + card.adventureLevels.levels.forEach { + database + .cardAdventureDao() + .insertNewAdventure( + cardId = cardId, + characterId = it.bossCharacterIndex, + steps = it.steps, + bossAp = it.bossAp, + bossHp = it.bossHp, + bossDp = it.bossDp, + bossBp = it.bossBp + ) + } + } else if (card is DimCard) { + card.adventureLevels.levels.map { + database + .cardAdventureDao() + .insertNewAdventure( + cardId = cardId, + characterId = it.bossCharacterIndex, + steps = it.steps, + bossAp = it.bossAp, + bossHp = it.bossHp, + bossDp = it.bossDp, + bossBp = null + ) + } + } + } + + private suspend fun importCardFusions( + cardId: Long, + card: com.github.cfogrady.vb.dim.card.Card<*, *, *, *, *, *> + ) { + Log.d("importCardFusions", "Importing card fusions") + if (card is DimCard) { + card + .attributeFusions + .entries + .forEach { + database + .cardFusionsDao() + .insertNewFusion( + cardId = cardId, + fromCharaId = it.characterIndex, + toCharaIdAttr1 = it.attribute1Fusion, + toCharaIdAttr2 = it.attribute2Fusion, + toCharaIdAttr3 = it.attribute3Fusion, + toCharaIdAttr4 = it.attribute4Fusion + ) + } + } + } + private fun updateCardProgress( cardId: Long, ) { @@ -315,6 +375,10 @@ class SettingsScreenControllerImpl( importCharacterData(cardId, card) importEvoData(cardId, card) + + importAdventureMissions(cardId, card) + + importCardFusions(cardId, card) } inputStream?.close() From 73c393df64d9b967b26da6d6ad659dc2d0b2920f Mon Sep 17 00:00:00 2001 From: Nacho Date: Sun, 14 Sep 2025 00:34:05 +0200 Subject: [PATCH 2/8] Fixed bug syncing watch adventure missions progress with the app --- .../nacabaro/vbhelper/daos/CardProgressDao.kt | 17 ++++++++++++++--- .../nacabaro/vbhelper/daos/CharacterDao.kt | 4 +++- .../nacabaro/vbhelper/dtos/CharacterDtos.kt | 3 ++- .../screens/cardScreen/CardAdventureScreen.kt | 7 +++++-- .../scanScreen/converters/FromNfcConverter.kt | 12 +++++------- .../scanScreen/converters/ToNfcConverter.kt | 14 ++++---------- .../SettingsScreenControllerImpl.kt | 14 +++++++------- 7 files changed, 40 insertions(+), 31 deletions(-) diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/daos/CardProgressDao.kt b/app/src/main/java/com/github/nacabaro/vbhelper/daos/CardProgressDao.kt index b5c1d80..80d5bc8 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/daos/CardProgressDao.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/daos/CardProgressDao.kt @@ -1,17 +1,28 @@ package com.github.nacabaro.vbhelper.daos import androidx.room.Dao +import androidx.room.Insert import androidx.room.Query -import androidx.room.Upsert import com.github.nacabaro.vbhelper.domain.card.CardProgress +import com.github.nacabaro.vbhelper.dtos.CharacterDtos @Dao interface CardProgressDao { - @Upsert - fun updateDimProgress(vararg cardProgresses: CardProgress) + @Query(""" + UPDATE CardProgress + SET + currentStage = :currentStage, + unlocked = :unlocked + WHERE cardId = :cardId AND + currentStage < :currentStage + """) + fun updateCardProgress(currentStage: Int, cardId: Long, unlocked: Boolean) @Query( "SELECT currentStage FROM CardProgress WHERE cardId = :cardId" ) fun getCardProgress(cardId: Long): Int + + @Insert + fun insertCardProgress(cardProgress: CardProgress) } \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/daos/CharacterDao.kt b/app/src/main/java/com/github/nacabaro/vbhelper/daos/CharacterDao.kt index 56066c5..261fa00 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/daos/CharacterDao.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/daos/CharacterDao.kt @@ -24,10 +24,12 @@ interface CharacterDao { d.cardId as cardId, c.charaIndex as charId, c.stage as stage, - c.attribute as attribute + c.attribute as attribute, + cp.currentStage as currentStage FROM CardCharacter c JOIN UserCharacter uc ON c.id = uc.charId JOIN Card d ON c.cardId = d.id + JOIN CardProgress cp ON d.id = cp.cardId WHERE c.id = :charId """ ) diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/dtos/CharacterDtos.kt b/app/src/main/java/com/github/nacabaro/vbhelper/dtos/CharacterDtos.kt index b279432..dd4a6ab 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/dtos/CharacterDtos.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/dtos/CharacterDtos.kt @@ -38,7 +38,8 @@ object CharacterDtos { val cardId: Long, val charId: Int, val stage: Int, - val attribute: NfcCharacter.Attribute + val attribute: NfcCharacter.Attribute, + val currentStage: Int ) data class TransformationHistory( diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardAdventureScreen.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardAdventureScreen.kt index 016d935..084ea4e 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardAdventureScreen.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardAdventureScreen.kt @@ -7,8 +7,11 @@ import androidx.compose.foundation.verticalScroll import androidx.compose.material3.Scaffold import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.navigation.NavController import com.github.nacabaro.vbhelper.components.TopBanner @@ -23,7 +26,7 @@ fun CardAdventureScreen( cardId: Long ) { val cardAdventureMissions = remember { mutableStateOf(emptyList()) } - var currentCardAdventure = remember { 0 } + var currentCardAdventure by remember { mutableIntStateOf(0) } LaunchedEffect(cardId) { withContext(Dispatchers.IO) { @@ -55,7 +58,7 @@ fun CardAdventureScreen( cardAdventureMissions.value.mapIndexed { index, it -> CardAdventureEntry( cardAdventureEntry = it, - obscure = index > currentCardAdventure + obscure = index > currentCardAdventure - 1 ) } } diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/converters/FromNfcConverter.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/converters/FromNfcConverter.kt index a398aae..7acb137 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/converters/FromNfcConverter.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/converters/FromNfcConverter.kt @@ -152,15 +152,13 @@ class FromNfcConverter ( nfcCharacter: NfcCharacter, cardData: Card ) { - val currentCardProgress = CardProgress( - cardId = cardData.id, - currentStage = nfcCharacter.nextAdventureMissionStage.toInt(), - unlocked = nfcCharacter.nextAdventureMissionStage.toInt() > cardData.stageCount - ) - database .cardProgressDao() - .updateDimProgress(currentCardProgress) + .updateCardProgress( + currentStage = nfcCharacter.nextAdventureMissionStage.toInt(), + cardId = cardData.id, + unlocked = nfcCharacter.nextAdventureMissionStage.toInt() > cardData.stageCount, + ) } diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/converters/ToNfcConverter.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/converters/ToNfcConverter.kt index 624f87a..dbc58fc 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/converters/ToNfcConverter.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/converters/ToNfcConverter.kt @@ -38,14 +38,10 @@ class ToNfcConverter( .characterDao() .getCharacterInfo(userCharacter.charId) - val currentCardStage = database - .cardProgressDao() - .getCardProgress(characterInfo.cardId) - return if (userCharacter.characterType == DeviceType.BEDevice) - nfcToBENfc(characterId, characterInfo, currentCardStage, userCharacter) + nfcToBENfc(characterId, characterInfo, userCharacter) else - nfcToVBNfc(characterId, characterInfo, currentCardStage, userCharacter) + nfcToVBNfc(characterId, characterInfo, userCharacter) } @@ -53,7 +49,6 @@ class ToNfcConverter( private suspend fun nfcToVBNfc( characterId: Long, characterInfo: CharacterDtos.CardCharacterInfo, - currentCardStage: Int, userCharacter: UserCharacter ): VBNfcCharacter { val vbData = database @@ -70,7 +65,7 @@ class ToNfcConverter( stage = characterInfo.stage.toByte(), attribute = characterInfo.attribute, ageInDays = userCharacter.ageInDays.toByte(), - nextAdventureMissionStage = currentCardStage.toByte(), + nextAdventureMissionStage = characterInfo.currentStage.toByte(), mood = userCharacter.mood.toByte(), vitalPoints = userCharacter.vitalPoints.toUShort(), transformationCountdownInMinutes = userCharacter.transformationCountdown.toUShort(), @@ -173,7 +168,6 @@ class ToNfcConverter( private suspend fun nfcToBENfc( characterId: Long, characterInfo: CharacterDtos.CardCharacterInfo, - currentCardStage: Int, userCharacter: UserCharacter ): BENfcCharacter { val beData = database @@ -188,7 +182,7 @@ class ToNfcConverter( stage = characterInfo.stage.toByte(), attribute = characterInfo.attribute, ageInDays = userCharacter.ageInDays.toByte(), - nextAdventureMissionStage = currentCardStage.toByte(), + nextAdventureMissionStage = characterInfo.currentStage.toByte(), mood = userCharacter.mood.toByte(), vitalPoints = userCharacter.vitalPoints.toUShort(), itemEffectMentalStateValue = beData.itemEffectMentalStateValue.toByte(), diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/settingsScreen/SettingsScreenControllerImpl.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/settingsScreen/SettingsScreenControllerImpl.kt index cadae13..0e2a039 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/settingsScreen/SettingsScreenControllerImpl.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/settingsScreen/SettingsScreenControllerImpl.kt @@ -336,15 +336,15 @@ class SettingsScreenControllerImpl( private fun updateCardProgress( cardId: Long, ) { - val cardProgress = CardProgress( - cardId = cardId, - currentStage = 0, - unlocked = false - ) - database .cardProgressDao() - .updateDimProgress(cardProgress) + .insertCardProgress( + CardProgress( + cardId = cardId, + currentStage = 1, + unlocked = false + ) + ) } private fun importCard(uri: Uri) { From 69245be8ddaee260769f59f5f56c65df5cdad548 Mon Sep 17 00:00:00 2001 From: Nacho Date: Mon, 3 Nov 2025 13:20:16 +0100 Subject: [PATCH 3/8] Updated README.md --- README.md | 101 +++++++++++++++++++++++++++++++++++------------------- 1 file changed, 66 insertions(+), 35 deletions(-) diff --git a/README.md b/README.md index d9be74f..72298e4 100644 --- a/README.md +++ b/README.md @@ -1,38 +1,69 @@ # VBHelper -## Developer Setup +Application to interact with the Vital series, VB, VH, VBC and VBBE. -1. Clone vb-nfc-reader (https://github.com/cfogrady/lib-vb-nfc) -2. Run vb-nfc-reader/publishToMavenLocal gradle task in the lib-vb-nfc project. -3. Clone vb-dim-reader (https://github.com/cfogrady/vb-dim-reader) -4. Run publishToMavenLocal gradle task in the vb-dim-reader project. -5. Create res/values/keys.xml within the app module. -6. Populate with: -``` - - - beHmacKey1 - beHmacKey2 - aesKey - - 0 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 - 12 - 13 - 14 - 15 - - -``` -7. Replace the values in the keys.xml file with those extracted from the original app. -8. Run \ No newline at end of file +## Current state of the project + +Right now the project is still under development, and until further notice, any database updates will result in having to erase application data. + +This document will be updated once the application does not need any more database resets. + +## Features + +As of now, the project allows you to read characters, view characters stats, and send them back to your watch. + +You can also apply items to the characters read, such as special missions, or change timers, and store characters in the storage section. + +You also earn new items every time an item such as a special mission (VB only) or a character completes an in-app adventure mission. + +App also comes with a dex that will update every time a new character is added, and allows you to see evolution requirements and current adventure stage in the watch. + +## How to set up + +1. Download the latest version for VB Arena APK from a trustworthy source. If your download is a standalone APK, continue to step 2. Otherwise, if your download is an XAPK, do the following: + + 1. Using your phone file manager, rename the XAPK file to ZIP, and extract its contents. You can also do this with any other device, such as Windows, macOS and Linux. + + 2. Once the files are extracted, look for an APK called `com.bandai.vitalbraceletarena.apk`. Copy it somewhere else, you will need it. + +2. Install an APK release for VB Arena. You will find the releases [here](http://github.com/nacabaro/vbhelper/releases). Download the latest release and install its APK. + + Note, in the current stage of the project, you will have to delete the old application from your device. If the app keeps crashing after installing, clear application data and storage. + +3. Import secrets in the app. These secrets will allow the app to talk to the watch. On the main screen, click on the gear icon, then `Import secrets`. + + You will be prompted to choose a file. Choose the APK file that was previously obtained. + +4. Import cards. Due to copyright laws, we cannot offer the characters and sprites themselves in the application. In order to import the cards do the following. + + 1. Using your own DiM/BEm cards, dump the cards to your device. You can get an in-depth tutorial in [here](http://mrblinky.net/digimon/vb/dimcardtool/dimcardtool.html). You can download the dump tool from [here](http://mrblinky.net/digimon/vb/dimcardtool/) + + 2. Once installed the tool and drivers, open the tool, connect your DiM/BEm reader hardware to yout computer and click on Read card. + + 3. Transfer the resulting file to your mobile device. You can put them anywhere, as long as they are accessible. My recommendation is to put them under a folder called `Cards` in your `Internal storage` or `SD Card` + + 4. In the app, click on import card. Next choose the BIN file corresponding to the card you want to import. + + **Note: if you do not import the card, whenever you attempt to read a character from th watch, the character you read will get deleted.** + +5. App will now be ready to be used. + +## Planned features + +- Online battles, undegoing development by `lightheel`. + +- VitalWear compatibility, undergoing development by `cfogrady`. + +- Support for multiple languages, not yet started. + +- Database backup/restore. + +## Credits + +- `cyanic` for helping us understand more about the VB connection protocol. + +- `cfogrady` for making both [`VB-DIM-Reader`](https://github.com/cfogrady/VB-DIM-Reader) and [`lib-vb-nfc`](https://github.com/cfogrady/lib-vb-nfc) + +- `lightheel` for working on the online component in the application, both server and battle client. + +- `shvstrz` for the app icon. \ No newline at end of file From 3fa072ce1eee0f0fb966c1f27e902249a2592c17 Mon Sep 17 00:00:00 2001 From: Nacho Date: Thu, 13 Nov 2025 23:52:14 +0100 Subject: [PATCH 4/8] Updated the store - Items can now be purchased - Changed settings to make use of flows/stateflows from the database, which update the screen in real time after any changes. - Separated functions from files --- app/build.gradle.kts | 1 + .../github/nacabaro/vbhelper/daos/ItemDao.kt | 5 +- .../nacabaro/vbhelper/di/AppContainer.kt | 2 + .../vbhelper/di/DefaultAppContainer.kt | 10 +- .../AdventureScreenControllerImpl.kt | 2 + .../homeScreens/HomeScreenControllerImpl.kt | 2 + .../screens/itemsScreen/ItemDialog.kt | 164 ++++++++++++++++ .../screens/itemsScreen/ItemElement.kt | 175 +----------------- .../vbhelper/screens/itemsScreen/ItemIcons.kt | 40 ++++ .../screens/itemsScreen/ItemsStore.kt | 131 +++++++++---- .../vbhelper/screens/itemsScreen/MyItems.kt | 31 +--- .../vbhelper/source/CurrencyRepository.kt | 28 +++ .../vbhelper/source/ItemsRepository.kt | 6 +- gradle/libs.versions.toml | 2 +- 14 files changed, 367 insertions(+), 232 deletions(-) create mode 100644 app/src/main/java/com/github/nacabaro/vbhelper/screens/itemsScreen/ItemDialog.kt create mode 100644 app/src/main/java/com/github/nacabaro/vbhelper/screens/itemsScreen/ItemIcons.kt create mode 100644 app/src/main/java/com/github/nacabaro/vbhelper/source/CurrencyRepository.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index a5fea01..4532721 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -91,4 +91,5 @@ dependencies { implementation("com.google.android.material:material:1.2.0") implementation(libs.protobuf.javalite) implementation("androidx.compose.material:material") + implementation("androidx.datastore:datastore-preferences:1.1.7") } \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/daos/ItemDao.kt b/app/src/main/java/com/github/nacabaro/vbhelper/daos/ItemDao.kt index 1164815..076de9d 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/daos/ItemDao.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/daos/ItemDao.kt @@ -3,6 +3,7 @@ package com.github.nacabaro.vbhelper.daos import androidx.room.Dao import androidx.room.Query import com.github.nacabaro.vbhelper.dtos.ItemDtos +import kotlinx.coroutines.flow.Flow @Dao interface ItemDao { @@ -13,7 +14,7 @@ interface ItemDao { ORDER BY Items.itemIcon ASC """ ) - suspend fun getAllItems(): List + fun getAllItems(): Flow> @Query( """ @@ -22,7 +23,7 @@ interface ItemDao { WHERE quantity > 0 """ ) - suspend fun getAllUserItems(): List + fun getAllUserItems(): Flow> @Query( """ diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/di/AppContainer.kt b/app/src/main/java/com/github/nacabaro/vbhelper/di/AppContainer.kt index bc447f2..2d82b95 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/di/AppContainer.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/di/AppContainer.kt @@ -1,9 +1,11 @@ package com.github.nacabaro.vbhelper.di import com.github.nacabaro.vbhelper.database.AppDatabase +import com.github.nacabaro.vbhelper.source.CurrencyRepository import com.github.nacabaro.vbhelper.source.DataStoreSecretsRepository interface AppContainer { val db: AppDatabase val dataStoreSecretsRepository: DataStoreSecretsRepository + val currencyRepository: CurrencyRepository } \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/di/DefaultAppContainer.kt b/app/src/main/java/com/github/nacabaro/vbhelper/di/DefaultAppContainer.kt index 4724bc6..70305b2 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/di/DefaultAppContainer.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/di/DefaultAppContainer.kt @@ -1,20 +1,27 @@ import android.content.Context import androidx.datastore.core.DataStore import androidx.datastore.dataStore +import androidx.datastore.preferences.core.Preferences +import androidx.datastore.preferences.preferencesDataStore import androidx.room.Room import com.github.nacabaro.vbhelper.database.AppDatabase import com.github.nacabaro.vbhelper.di.AppContainer +import com.github.nacabaro.vbhelper.source.CurrencyRepository import com.github.nacabaro.vbhelper.source.DataStoreSecretsRepository import com.github.nacabaro.vbhelper.source.SecretsSerializer import com.github.nacabaro.vbhelper.source.proto.Secrets private const val SECRETS_DATA_STORE_NAME = "secrets.pb" - +private const val USER_PREFERENCES_NAME = "user_preferences" val Context.secretsStore: DataStore by dataStore( fileName = SECRETS_DATA_STORE_NAME, serializer = SecretsSerializer ) +val Context.currencyStore: DataStore by preferencesDataStore( + name = USER_PREFERENCES_NAME +) + class DefaultAppContainer(private val context: Context) : AppContainer { override val db: AppDatabase by lazy { @@ -29,5 +36,6 @@ class DefaultAppContainer(private val context: Context) : AppContainer { override val dataStoreSecretsRepository = DataStoreSecretsRepository(context.secretsStore) + override val currencyRepository = CurrencyRepository(context.currencyStore) } diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/adventureScreen/AdventureScreenControllerImpl.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/adventureScreen/AdventureScreenControllerImpl.kt index 535d1f4..50c9605 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/adventureScreen/AdventureScreenControllerImpl.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/adventureScreen/AdventureScreenControllerImpl.kt @@ -6,6 +6,7 @@ import androidx.lifecycle.lifecycleScope import com.github.nacabaro.vbhelper.di.VBHelper import com.github.nacabaro.vbhelper.dtos.ItemDtos import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch import kotlin.math.roundToInt import kotlin.random.Random @@ -77,6 +78,7 @@ class AdventureScreenControllerImpl( val randomItem = database .itemDao() .getAllItems() + .first() .random() val random = ((Random.nextFloat() * character.stage) + 3).roundToInt() diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/HomeScreenControllerImpl.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/HomeScreenControllerImpl.kt index 5169263..ded8422 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/HomeScreenControllerImpl.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/HomeScreenControllerImpl.kt @@ -5,6 +5,7 @@ import androidx.lifecycle.lifecycleScope import com.github.cfogrady.vbnfc.vb.SpecialMission import com.github.nacabaro.vbhelper.di.VBHelper import com.github.nacabaro.vbhelper.dtos.ItemDtos +import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch import java.time.Instant import kotlin.math.roundToInt @@ -41,6 +42,7 @@ class HomeScreenControllerImpl( val randomItem = database .itemDao() .getAllItems() + .first() .random() val randomItemAmount = (Random.nextFloat() * 5).roundToInt() diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/itemsScreen/ItemDialog.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/itemsScreen/ItemDialog.kt new file mode 100644 index 0000000..3bd3e2d --- /dev/null +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/itemsScreen/ItemDialog.kt @@ -0,0 +1,164 @@ +package com.github.nacabaro.vbhelper.screens.itemsScreen + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +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.padding +import androidx.compose.foundation.layout.size +import androidx.compose.material3.Button +import androidx.compose.material3.Card +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.window.Dialog +import androidx.compose.ui.window.DialogProperties +import com.github.nacabaro.vbhelper.R +import com.github.nacabaro.vbhelper.domain.items.ItemType +import com.github.nacabaro.vbhelper.dtos.ItemDtos +import com.github.nacabaro.vbhelper.ui.theme.VBHelperTheme + +@Composable +fun ItemDialog( + item: ItemDtos.ItemsWithQuantities, + onClickCancel: () -> Unit, + onClickUse: (() -> Unit)? = null, + onClickPurchase: (() -> Unit)? = null, +) { + Dialog( + onDismissRequest = onClickCancel, + properties = DialogProperties( + dismissOnBackPress = true, + dismissOnClickOutside = true + ) + ) { + Card ( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp) + ) { + Column ( + modifier = Modifier + .padding(16.dp) + ) { + Row { + Box(modifier = Modifier) { + // Background image (full size) + Icon( + painter = painterResource(id = getIconResource(item.itemIcon)), + contentDescription = null, + modifier = Modifier + .size(96.dp) + .align(Alignment.Center) + ) + Icon( + painter = painterResource(id = getLengthResource(item.itemLength)), + contentDescription = null, + tint = MaterialTheme.colorScheme.outline, + modifier = Modifier + .size(64.dp) // Set the size of the overlay image + .align(Alignment.BottomEnd) // Align to the top end (top-right corner) + ) + } + Column ( + modifier = Modifier + .padding(16.dp) + ) { + Text( + fontSize = MaterialTheme.typography.titleLarge.fontSize, + text = item.name, + modifier = Modifier + .fillMaxWidth() + ) + } + } + Text( + textAlign = TextAlign.Center, + fontSize = MaterialTheme.typography.bodyMedium.fontSize, + fontFamily = MaterialTheme.typography.bodyMedium.fontFamily, + text = item.description, + modifier = Modifier + .fillMaxWidth() + .padding(4.dp) + ) + if (onClickPurchase != null) { + Text( + textAlign = TextAlign.Center, + fontSize = MaterialTheme.typography.bodySmall.fontSize, + fontFamily = MaterialTheme.typography.bodySmall.fontFamily, + text = "Costs ${item.price} credits", + modifier = Modifier + .fillMaxWidth() + ) + } + Text( + textAlign = TextAlign.Center, + fontSize = MaterialTheme.typography.bodySmall.fontSize, + fontFamily = MaterialTheme.typography.bodySmall.fontFamily, + text = "You have ${item.quantity} of this item", + modifier = Modifier + .fillMaxWidth() + ) + + Row ( + horizontalArrangement = Arrangement.Center, + modifier = Modifier + .fillMaxWidth() + ) { + if (onClickUse != null) { + Button( + onClick = onClickUse + ) { + Text("Use item") + } + } + + if (onClickPurchase != null) { + Button( + onClick = onClickPurchase + ) { + Text("Purchase") + } + } + + Spacer(modifier = Modifier.size(8.dp)) + Button( + onClick = onClickCancel + ) { + Text("Cancel") + } + } + } + } + } +} + +@Composable +@Preview(showBackground = true) +fun PreviewItemDialog() { + VBHelperTheme { + ItemDialog( + item = ItemDtos.ItemsWithQuantities( + name = "AP Training x3 (60 min)", + description = "Boosts AP during training (for 60 minutes)", + itemIcon = R.drawable.baseline_attack_24, + itemLength = R.drawable.baseline_60_min_timer, + quantity = 19, + id = 1, + price = 500, + itemType = ItemType.BEITEM + ), + onClickUse = { }, + onClickCancel = { } + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/itemsScreen/ItemElement.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/itemsScreen/ItemElement.kt index 7822bc0..cd4645c 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/itemsScreen/ItemElement.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/itemsScreen/ItemElement.kt @@ -1,36 +1,23 @@ package com.github.nacabaro.vbhelper.screens.itemsScreen -import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.aspectRatio import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size -import androidx.compose.material3.Button import androidx.compose.material3.Card import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import androidx.compose.ui.window.Dialog -import androidx.compose.ui.window.DialogProperties -import com.github.nacabaro.vbhelper.R -import com.github.nacabaro.vbhelper.ui.theme.VBHelperTheme +import com.github.nacabaro.vbhelper.dtos.ItemDtos @Composable fun ItemElement( - itemIcon: Int, - lengthIcon: Int, + item: ItemDtos.ItemsWithQuantities, modifier: Modifier = Modifier, onClick: (() -> Unit) = { } ) { @@ -41,7 +28,7 @@ fun ItemElement( ) { Box(modifier = Modifier.fillMaxSize()) { Icon( - painter = painterResource(id = itemIcon), + painter = painterResource(id = getIconResource(item.itemIcon)), contentDescription = null, modifier = Modifier .size(96.dp) @@ -49,164 +36,14 @@ fun ItemElement( .padding(16.dp) ) Icon( - painter = painterResource(id = lengthIcon), + painter = painterResource(id = getLengthResource(item.itemLength)), contentDescription = null, tint = MaterialTheme.colorScheme.surfaceTint, modifier = Modifier - .size(48.dp) // Set the size of the overlay image - .align(Alignment.TopStart) // Align to the top end (top-right corner) + .size(48.dp) + .align(Alignment.TopStart) .padding(8.dp) ) } } } - -@Composable -fun ItemDialog( - name: String, - description: String, - itemIcon: Int, - lengthIcon: Int, - amount: Int, - onClickUse: () -> Unit, - onClickCancel: () -> Unit -) { - Dialog( - onDismissRequest = onClickCancel, - properties = DialogProperties( - dismissOnBackPress = true, - dismissOnClickOutside = true - ) - ) { - Card ( - modifier = Modifier - .fillMaxWidth() - .padding(16.dp) - ) { - Column ( - modifier = Modifier - .padding(16.dp) - ) { - Row { - Box(modifier = Modifier) { - // Background image (full size) - Icon( - painter = painterResource(id = itemIcon), - contentDescription = null, - modifier = Modifier - .size(96.dp) - .align(Alignment.Center) - ) - Icon( - painter = painterResource(id = lengthIcon), - contentDescription = null, - tint = MaterialTheme.colorScheme.outline, - modifier = Modifier - .size(64.dp) // Set the size of the overlay image - .align(Alignment.BottomEnd) // Align to the top end (top-right corner) - ) - } - Column ( - modifier = Modifier - .padding(16.dp) - ) { - Text( - fontSize = MaterialTheme.typography.titleLarge.fontSize, - text = name, - modifier = Modifier - .fillMaxWidth() - ) - } - } - Text( - textAlign = TextAlign.Center, - fontSize = MaterialTheme.typography.bodyMedium.fontSize, - fontFamily = MaterialTheme.typography.bodyMedium.fontFamily, - text = description, - modifier = Modifier - .fillMaxWidth() - .padding(4.dp) - ) - Text( - textAlign = TextAlign.Center, - fontSize = MaterialTheme.typography.bodySmall.fontSize, - fontFamily = MaterialTheme.typography.bodySmall.fontFamily, - text = "You have $amount of this item", - modifier = Modifier - .fillMaxWidth() - .padding(8.dp) - ) - Row ( - horizontalArrangement = Arrangement.Center, - modifier = Modifier - .fillMaxWidth() - ) { - Button( - onClick = onClickUse - ) { - Text("Use item") - } - Spacer(modifier = Modifier.size(8.dp)) - Button( - onClick = onClickCancel - ) { - Text("Cancel") - } - } - } - } - } -} - -fun getIconResource(index: Int): Int { - return when (index) { - ItemsScreenControllerImpl.ItemTypes.PPTraining.id -> R.drawable.baseline_agility_24 - ItemsScreenControllerImpl.ItemTypes.APTraining.id -> R.drawable.baseline_attack_24 - ItemsScreenControllerImpl.ItemTypes.HPTraining.id -> R.drawable.baseline_shield_24 - ItemsScreenControllerImpl.ItemTypes.BPTraining.id -> R.drawable.baseline_trophy_24 - ItemsScreenControllerImpl.ItemTypes.AllTraining.id -> R.drawable.baseline_arrow_up_24 - 6 -> R.drawable.baseline_timer_24 - 7 -> R.drawable.baseline_rank_24 - 8 -> R.drawable.baseline_vitals_24 - else -> R.drawable.baseline_question_mark_24 - } -} - -fun getLengthResource(index: Int): Int { - return when (index) { - 15 -> R.drawable.baseline_15_min_timer - 30 -> R.drawable.baseline_30_min_timer - 60 -> R.drawable.baseline_60_min_timer - -60 -> R.drawable.baseline_60_min_timer - 300 -> R.drawable.baseline_5_hour_timer - 600 -> R.drawable.baseline_10_hour_timer - -720 -> R.drawable.baseline_12_hour_timer - -1440 -> R.drawable.baseline_24_hour_timer - 6000 -> R.drawable.baseline_reset_24 - 1000 -> R.drawable.baseline_single_arrow_up - 2500 -> R.drawable.baseline_double_arrow_up - 5000 -> R.drawable.baseline_triple_arrow_up - 9999 -> R.drawable.baseline_health_24 - -500 -> R.drawable.baseline_single_arrow_down - -1000 -> R.drawable.baseline_double_arrow_down - -2500 -> R.drawable.baseline_triple_arrow_down - -9999 -> R.drawable.baseline_reset_24 - else -> R.drawable.baseline_question_mark_24 - } -} - -@Composable -@Preview(showBackground = true) -fun PreviewItemDialog() { - VBHelperTheme { - ItemDialog( - name = "AP Training x3 (60 min)", - description = "Boosts AP during training (for 60 minutes)", - itemIcon = R.drawable.baseline_attack_24, - lengthIcon = R.drawable.baseline_60_min_timer, - onClickUse = { }, - onClickCancel = { }, - amount = 19 - ) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/itemsScreen/ItemIcons.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/itemsScreen/ItemIcons.kt new file mode 100644 index 0000000..b0d9e8a --- /dev/null +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/itemsScreen/ItemIcons.kt @@ -0,0 +1,40 @@ +package com.github.nacabaro.vbhelper.screens.itemsScreen + +import com.github.nacabaro.vbhelper.R + +fun getIconResource(index: Int): Int { + return when (index) { + ItemsScreenControllerImpl.ItemTypes.PPTraining.id -> R.drawable.baseline_agility_24 + ItemsScreenControllerImpl.ItemTypes.APTraining.id -> R.drawable.baseline_attack_24 + ItemsScreenControllerImpl.ItemTypes.HPTraining.id -> R.drawable.baseline_shield_24 + ItemsScreenControllerImpl.ItemTypes.BPTraining.id -> R.drawable.baseline_trophy_24 + ItemsScreenControllerImpl.ItemTypes.AllTraining.id -> R.drawable.baseline_arrow_up_24 + 6 -> R.drawable.baseline_timer_24 + 7 -> R.drawable.baseline_rank_24 + 8 -> R.drawable.baseline_vitals_24 + else -> R.drawable.baseline_question_mark_24 + } +} + +fun getLengthResource(index: Int): Int { + return when (index) { + 15 -> R.drawable.baseline_15_min_timer + 30 -> R.drawable.baseline_30_min_timer + 60 -> R.drawable.baseline_60_min_timer + -60 -> R.drawable.baseline_60_min_timer + 300 -> R.drawable.baseline_5_hour_timer + 600 -> R.drawable.baseline_10_hour_timer + -720 -> R.drawable.baseline_12_hour_timer + -1440 -> R.drawable.baseline_24_hour_timer + 6000 -> R.drawable.baseline_reset_24 + 1000 -> R.drawable.baseline_single_arrow_up + 2500 -> R.drawable.baseline_double_arrow_up + 5000 -> R.drawable.baseline_triple_arrow_up + 9999 -> R.drawable.baseline_health_24 + -500 -> R.drawable.baseline_single_arrow_down + -1000 -> R.drawable.baseline_double_arrow_down + -2500 -> R.drawable.baseline_triple_arrow_down + -9999 -> R.drawable.baseline_reset_24 + else -> R.drawable.baseline_question_mark_24 + } +} \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/itemsScreen/ItemsStore.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/itemsScreen/ItemsStore.kt index 032303b..4d4533c 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/itemsScreen/ItemsStore.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/itemsScreen/ItemsStore.kt @@ -1,25 +1,35 @@ package com.github.nacabaro.vbhelper.screens.itemsScreen +import android.widget.Toast +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.LazyVerticalGrid import androidx.compose.foundation.lazy.grid.items +import androidx.compose.material3.Card import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.dp import androidx.navigation.NavController +import com.github.nacabaro.vbhelper.database.AppDatabase import com.github.nacabaro.vbhelper.di.VBHelper import com.github.nacabaro.vbhelper.dtos.ItemDtos +import com.github.nacabaro.vbhelper.source.CurrencyRepository import com.github.nacabaro.vbhelper.source.ItemsRepository -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.launch @Composable fun ItemsStore( @@ -27,45 +37,98 @@ fun ItemsStore( ) { val application = LocalContext.current.applicationContext as VBHelper val itemsRepository = ItemsRepository(application.container.db) - val myItems = remember { mutableStateOf(emptyList()) } + val myItems by itemsRepository.getAllItems().collectAsState(emptyList()) + var selectedElementIndex by remember { mutableStateOf(null) } - LaunchedEffect(itemsRepository) { - withContext(Dispatchers.IO) { - myItems.value = itemsRepository.getAllItems() - } - } + val currencyRepository = application.container.currencyRepository + val currentCurrency = currencyRepository.currencyValue.collectAsState(0) - if (myItems.value.isEmpty()) { - Text("No items") - } else { - LazyVerticalGrid( - columns = GridCells.Fixed(3), - modifier = Modifier + val scope = rememberCoroutineScope() + + if (myItems.isEmpty()) { + Column( + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier.fillMaxSize() ) { - items(myItems.value) { index -> - ItemElement( - itemIcon = getIconResource(index.itemIcon), - lengthIcon = getLengthResource(index.itemLength), + Text("No items") + } + } else { + Column() { + Card( + modifier = Modifier + .padding(8.dp) + .fillMaxWidth() + ) { + Text( + text = "${currentCurrency.value} credits", modifier = Modifier - .padding(8.dp), - onClick = { - selectedElementIndex = myItems.value.indexOf(index) - } + .padding(8.dp) ) } - } - if (selectedElementIndex != null) { - ItemDialog( - name = myItems.value[selectedElementIndex!!].name, - description = myItems.value[selectedElementIndex!!].description, - itemIcon = getIconResource(myItems.value[selectedElementIndex!!].itemIcon), - lengthIcon = getLengthResource(myItems.value[selectedElementIndex!!].itemLength), - amount = myItems.value[selectedElementIndex!!].quantity, - onClickUse = { }, - onClickCancel = { selectedElementIndex = null } - ) + LazyVerticalGrid( + columns = GridCells.Fixed(3), + modifier = Modifier + ) { + items(myItems) { index -> + ItemElement( + item = index, + modifier = Modifier + .padding(8.dp), + onClick = { + selectedElementIndex = myItems.indexOf(index) + } + ) + } + } } } + + + if (selectedElementIndex != null) { + ItemDialog( + item = myItems[selectedElementIndex!!], + onClickPurchase = { + scope.launch { + Toast.makeText( + application.applicationContext, + purchaseItem( + application.container.db, + myItems[selectedElementIndex!!], + currencyRepository + ), + Toast.LENGTH_SHORT + ).show( + ) + } + }, + onClickCancel = { selectedElementIndex = null } + ) + } +} + +suspend fun purchaseItem( + db: AppDatabase, + item: ItemDtos.ItemsWithQuantities, + currencyRepository: CurrencyRepository +): String { + if (currencyRepository.currencyValue.first() < item.price) { + return "Not enough credits" + } else { + db + .itemDao() + .purchaseItem( + item.id, + 1 + ) + + currencyRepository + .setCurrencyValue( + currencyRepository.currencyValue.first() - item.price + ) + + return "Purchase successful!" + } } \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/itemsScreen/MyItems.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/itemsScreen/MyItems.kt index 55b835f..39479a8 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/itemsScreen/MyItems.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/itemsScreen/MyItems.kt @@ -9,7 +9,7 @@ import androidx.compose.foundation.lazy.grid.LazyVerticalGrid import androidx.compose.foundation.lazy.grid.items import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -20,11 +20,8 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.dp import androidx.navigation.NavController import com.github.nacabaro.vbhelper.di.VBHelper -import com.github.nacabaro.vbhelper.dtos.ItemDtos import com.github.nacabaro.vbhelper.navigation.NavigationItems import com.github.nacabaro.vbhelper.source.ItemsRepository -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext @Composable fun MyItems( @@ -32,16 +29,11 @@ fun MyItems( ) { val application = LocalContext.current.applicationContext as VBHelper val itemsRepository = ItemsRepository(application.container.db) - val myItems = remember { mutableStateOf(emptyList()) } + val myItems by itemsRepository.getUserItems().collectAsState(emptyList()) + var selectedElementIndex by remember { mutableStateOf(null) } - LaunchedEffect(itemsRepository) { - withContext(Dispatchers.IO) { - myItems.value = itemsRepository.getUserItems() - } - } - - if (myItems.value.isEmpty()) { + if (myItems.isEmpty()) { Column( verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally, @@ -54,14 +46,13 @@ fun MyItems( columns = GridCells.Fixed(3), modifier = Modifier ) { - items(myItems.value) { index -> + items(myItems) { index -> ItemElement( - itemIcon = getIconResource(index.itemIcon), - lengthIcon = getLengthResource(index.itemLength), + item = index, modifier = Modifier .padding(8.dp), onClick = { - selectedElementIndex = myItems.value.indexOf(index) + selectedElementIndex = myItems.indexOf(index) } ) } @@ -69,11 +60,7 @@ fun MyItems( if (selectedElementIndex != null) { ItemDialog( - name = myItems.value[selectedElementIndex!!].name, - description = myItems.value[selectedElementIndex!!].description, - itemIcon = getIconResource(myItems.value[selectedElementIndex!!].itemIcon), - lengthIcon = getLengthResource(myItems.value[selectedElementIndex!!].itemLength), - amount = myItems.value[selectedElementIndex!!].quantity, + item = myItems[selectedElementIndex!!], onClickUse = { navController .navigate( @@ -81,7 +68,7 @@ fun MyItems( .ApplyItem.route .replace( "{itemId}", - myItems.value[selectedElementIndex!!].id.toString() + myItems[selectedElementIndex!!].id.toString() ) ) selectedElementIndex = null diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/source/CurrencyRepository.kt b/app/src/main/java/com/github/nacabaro/vbhelper/source/CurrencyRepository.kt new file mode 100644 index 0000000..42fc61a --- /dev/null +++ b/app/src/main/java/com/github/nacabaro/vbhelper/source/CurrencyRepository.kt @@ -0,0 +1,28 @@ +package com.github.nacabaro.vbhelper.source + +import androidx.datastore.core.DataStore +import androidx.datastore.preferences.core.Preferences +import androidx.datastore.preferences.core.edit +import androidx.datastore.preferences.core.intPreferencesKey +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map + +class CurrencyRepository ( + private val dataStore: DataStore +) { + + private companion object { + val CURRENCY_VALUE = intPreferencesKey("currency_value") + } + + val currencyValue: Flow = dataStore.data + .map { preferences -> + preferences[CURRENCY_VALUE] ?: 10000 + } + + suspend fun setCurrencyValue(newValue: Int) { + dataStore.edit { preferences -> + preferences[CURRENCY_VALUE] = newValue + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/source/ItemsRepository.kt b/app/src/main/java/com/github/nacabaro/vbhelper/source/ItemsRepository.kt index 8d6d705..e93a49b 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/source/ItemsRepository.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/source/ItemsRepository.kt @@ -1,17 +1,17 @@ package com.github.nacabaro.vbhelper.source import com.github.nacabaro.vbhelper.database.AppDatabase -import com.github.nacabaro.vbhelper.domain.items.Items import com.github.nacabaro.vbhelper.dtos.ItemDtos +import kotlinx.coroutines.flow.Flow class ItemsRepository( private val db: AppDatabase ) { - suspend fun getAllItems(): List { + fun getAllItems(): Flow> { return db.itemDao().getAllItems() } - suspend fun getUserItems(): List { + fun getUserItems(): Flow> { return db.itemDao().getAllUserItems() } } \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 2754f1b..1db28ca 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,5 +1,5 @@ [versions] -agp = "8.12.2" +agp = "8.13.1" datastore = "1.1.2" kotlin = "2.0.0" coreKtx = "1.15.0" From ffa6958a8967bc55e4c288fcd46a3f4deb830d52 Mon Sep 17 00:00:00 2001 From: Nacho Date: Sat, 15 Nov 2025 20:10:17 +0100 Subject: [PATCH 5/8] Few things - Finished working with the card fusions - Added the GUI part to access the data - Cleaned up a bit of the code, separated a few things from the SettingsScreenControllerImpl.kt into separate classes. - Removed a few LaunchedEffect in exchange of Flows (UI now updates automatically when an action happens) --- .../vbhelper/daos/CardAdventureDao.kt | 5 +- .../nacabaro/vbhelper/daos/CardFusionsDao.kt | 40 +- .../nacabaro/vbhelper/daos/CardProgressDao.kt | 3 +- .../nacabaro/vbhelper/daos/CharacterDao.kt | 5 +- .../github/nacabaro/vbhelper/daos/DexDao.kt | 5 +- .../vbhelper/domain/card/CardFusions.kt | 27 +- .../nacabaro/vbhelper/dtos/CharacterDtos.kt | 10 + .../screens/cardScreen/CardAdventureScreen.kt | 31 +- .../cardScreen/CardScreenController.kt | 7 +- .../cardScreen/CardScreenControllerImpl.kt | 13 +- .../screens/cardScreen/CardViewScreen.kt | 22 +- .../screens/cardScreen/CardsScreen.kt | 16 +- .../dialogs/DexCharaDetailsDialog.kt | 58 ++- .../dialogs/DexCharaFusionsDialog.kt | 191 +++++++++ .../screens/homeScreens/HomeScreen.kt | 3 - .../SettingsScreenControllerImpl.kt | 384 +----------------- .../controllers/CardImportController.kt | 320 +++++++++++++++ .../DatabaseManagementController.kt | 115 ++++++ .../nacabaro/vbhelper/source/DexRepository.kt | 13 +- 19 files changed, 781 insertions(+), 487 deletions(-) create mode 100644 app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/dialogs/DexCharaFusionsDialog.kt create mode 100644 app/src/main/java/com/github/nacabaro/vbhelper/screens/settingsScreen/controllers/CardImportController.kt create mode 100644 app/src/main/java/com/github/nacabaro/vbhelper/screens/settingsScreen/controllers/DatabaseManagementController.kt diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/daos/CardAdventureDao.kt b/app/src/main/java/com/github/nacabaro/vbhelper/daos/CardAdventureDao.kt index 03db5ac..3496ec2 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/daos/CardAdventureDao.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/daos/CardAdventureDao.kt @@ -3,6 +3,7 @@ package com.github.nacabaro.vbhelper.daos import androidx.room.Dao import androidx.room.Query import com.github.nacabaro.vbhelper.dtos.CardDtos +import kotlinx.coroutines.flow.Flow @Dao interface CardAdventureDao { @@ -52,7 +53,7 @@ interface CardAdventureDao { WHERE cc.cardId = :cardId """) - suspend fun getAdventureForCard( + fun getAdventureForCard( cardId: Long - ): List + ): Flow> } \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/daos/CardFusionsDao.kt b/app/src/main/java/com/github/nacabaro/vbhelper/daos/CardFusionsDao.kt index e9f7e34..86d6a9a 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/daos/CardFusionsDao.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/daos/CardFusionsDao.kt @@ -2,6 +2,9 @@ package com.github.nacabaro.vbhelper.daos import androidx.room.Dao import androidx.room.Query +import com.github.cfogrady.vbnfc.data.NfcCharacter +import com.github.nacabaro.vbhelper.dtos.CharacterDtos +import kotlinx.coroutines.flow.Flow @Dao interface CardFusionsDao { @@ -9,24 +12,37 @@ interface CardFusionsDao { INSERT INTO CardFusions ( fromCharaId, - attribute1Fusion, - attribute2Fusion, - attribute3Fusion, - attribute4Fusion + attribute, + toCharaId ) SELECT (SELECT id FROM CardCharacter WHERE cardId = :cardId AND charaIndex = :fromCharaId), - (SELECT id FROM CardCharacter WHERE cardId = :cardId AND charaIndex = :toCharaIdAttr1), - (SELECT id FROM CardCharacter WHERE cardId = :cardId AND charaIndex = :toCharaIdAttr2), - (SELECT id FROM CardCharacter WHERE cardId = :cardId AND charaIndex = :toCharaIdAttr3), - (SELECT id FROM CardCharacter WHERE cardId = :cardId AND charaIndex = :toCharaIdAttr4) + :attribute, + (SELECT id FROM CardCharacter WHERE cardId = :cardId AND charaIndex = :toCharaId) """) suspend fun insertNewFusion( cardId: Long, fromCharaId: Int, - toCharaIdAttr1: Int, - toCharaIdAttr2: Int, - toCharaIdAttr3: Int, - toCharaIdAttr4: Int + attribute: NfcCharacter.Attribute, + toCharaId: Int ) + + @Query(""" + SELECT + cf.toCharaId as charaId, + cf.fromCharaId as fromCharaId, + s.spriteIdle1 as spriteIdle, + cc.attribute as attribute, + s.width as spriteWidth, + s.height as spriteHeight, + d.discoveredOn as discoveredOn, + cf.attribute as fusionAttribute + FROM CardFusions cf + JOIN CardCharacter cc ON cc.id = cf.toCharaId + JOIN Sprite s ON s.id = cc.id + LEFT JOIN Dex d ON d.id = cc.id + WHERE cf.fromCharaId = :charaId + ORDER BY cc.charaIndex + """) + fun getFusionsForCharacter(charaId: Long): Flow> } \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/daos/CardProgressDao.kt b/app/src/main/java/com/github/nacabaro/vbhelper/daos/CardProgressDao.kt index 80d5bc8..3fde614 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/daos/CardProgressDao.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/daos/CardProgressDao.kt @@ -5,6 +5,7 @@ import androidx.room.Insert import androidx.room.Query import com.github.nacabaro.vbhelper.domain.card.CardProgress import com.github.nacabaro.vbhelper.dtos.CharacterDtos +import kotlinx.coroutines.flow.Flow @Dao interface CardProgressDao { @@ -21,7 +22,7 @@ interface CardProgressDao { @Query( "SELECT currentStage FROM CardProgress WHERE cardId = :cardId" ) - fun getCardProgress(cardId: Long): Int + fun getCardProgress(cardId: Long): Flow @Insert fun insertCardProgress(cardProgress: CardProgress) diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/daos/CharacterDao.kt b/app/src/main/java/com/github/nacabaro/vbhelper/daos/CharacterDao.kt index 261fa00..378e92f 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/daos/CharacterDao.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/daos/CharacterDao.kt @@ -6,6 +6,7 @@ import androidx.room.Query import com.github.nacabaro.vbhelper.domain.card.CardCharacter import com.github.nacabaro.vbhelper.domain.characters.Sprite import com.github.nacabaro.vbhelper.dtos.CharacterDtos +import kotlinx.coroutines.flow.Flow @Dao interface CharacterDao { @@ -82,8 +83,8 @@ interface CharacterDao { JOIN Sprite s ON s.id = c.spriteId LEFT JOIN Dex d ON d.id = pt.toCharaId WHERE - c.cardId = :cardId + pt.charaId = :characterId """ ) - suspend fun getEvolutionRequirementsForCard(cardId: Long): List + fun getEvolutionRequirementsForCard(characterId: Long): Flow> } \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/daos/DexDao.kt b/app/src/main/java/com/github/nacabaro/vbhelper/daos/DexDao.kt index f31b365..c343796 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/daos/DexDao.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/daos/DexDao.kt @@ -4,6 +4,7 @@ import androidx.room.Dao import androidx.room.Query import com.github.nacabaro.vbhelper.dtos.CardDtos import com.github.nacabaro.vbhelper.dtos.CharacterDtos +import kotlinx.coroutines.flow.Flow @Dao interface DexDao { @@ -40,7 +41,7 @@ interface DexDao { WHERE c.cardId = :cardId """ ) - suspend fun getSingleCardProgress(cardId: Long): List + fun getSingleCardProgress(cardId: Long): Flow> @Query( """ @@ -55,5 +56,5 @@ interface DexDao { FROM Card c """ ) - suspend fun getCardsWithProgress(): List + fun getCardsWithProgress(): Flow> } \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/domain/card/CardFusions.kt b/app/src/main/java/com/github/nacabaro/vbhelper/domain/card/CardFusions.kt index 0ced483..1769eda 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/domain/card/CardFusions.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/domain/card/CardFusions.kt @@ -3,6 +3,7 @@ package com.github.nacabaro.vbhelper.domain.card import androidx.room.Entity import androidx.room.ForeignKey import androidx.room.PrimaryKey +import com.github.cfogrady.vbnfc.data.NfcCharacter @Entity( foreignKeys = [ @@ -15,25 +16,7 @@ import androidx.room.PrimaryKey ForeignKey( entity = CardCharacter::class, parentColumns = ["id"], - childColumns = ["attribute1Fusion"], - onDelete = ForeignKey.CASCADE - ), - ForeignKey( - entity = CardCharacter::class, - parentColumns = ["id"], - childColumns = ["attribute2Fusion"], - onDelete = ForeignKey.CASCADE - ), - ForeignKey( - entity = CardCharacter::class, - parentColumns = ["id"], - childColumns = ["attribute3Fusion"], - onDelete = ForeignKey.CASCADE - ), - ForeignKey( - entity = CardCharacter::class, - parentColumns = ["id"], - childColumns = ["attribute4Fusion"], + childColumns = ["toCharaId"], onDelete = ForeignKey.CASCADE ) ] @@ -41,8 +24,6 @@ import androidx.room.PrimaryKey data class CardFusions( @PrimaryKey(autoGenerate = true) val id: Long, val fromCharaId: Long, - val attribute1Fusion: Long?, - val attribute2Fusion: Long?, - val attribute3Fusion: Long?, - val attribute4Fusion: Long? + val attribute: NfcCharacter.Attribute, + val toCharaId: Long ) \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/dtos/CharacterDtos.kt b/app/src/main/java/com/github/nacabaro/vbhelper/dtos/CharacterDtos.kt index dd4a6ab..0837dbb 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/dtos/CharacterDtos.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/dtos/CharacterDtos.kt @@ -114,4 +114,14 @@ object CharacterDtos { val changeTimerHours: Int, val requiredAdventureLevelCompleted: Int ) + + data class FusionsWithSpritesAndObtained( + val charaId: Long, + val fromCharaId: Long, + val spriteIdle: ByteArray, + val spriteWidth: Int, + val spriteHeight: Int, + val discoveredOn: Long?, + val fusionAttribute: NfcCharacter.Attribute + ) } \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardAdventureScreen.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardAdventureScreen.kt index 084ea4e..6f5527a 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardAdventureScreen.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardAdventureScreen.kt @@ -6,18 +6,11 @@ import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material3.Scaffold import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableIntStateOf -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.navigation.NavController import com.github.nacabaro.vbhelper.components.TopBanner -import com.github.nacabaro.vbhelper.dtos.CardDtos -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext @Composable fun CardAdventureScreen( @@ -25,20 +18,12 @@ fun CardAdventureScreen( cardScreenController: CardScreenControllerImpl, cardId: Long ) { - val cardAdventureMissions = remember { mutableStateOf(emptyList()) } - var currentCardAdventure by remember { mutableIntStateOf(0) } - - LaunchedEffect(cardId) { - withContext(Dispatchers.IO) { - cardAdventureMissions.value = - cardScreenController - .getCardAdventureMissions(cardId) - - currentCardAdventure = - cardScreenController - .getCardProgress(cardId) - } - } + val cardAdventureMissions by cardScreenController + .getCardAdventureMissions(cardId) + .collectAsState(emptyList()) + val currentCardAdventure by cardScreenController + .getCardProgress(cardId) + .collectAsState(0) Scaffold ( topBar = { @@ -55,7 +40,7 @@ fun CardAdventureScreen( .padding(top = contentPadding.calculateTopPadding()) .verticalScroll(state = rememberScrollState()) ) { - cardAdventureMissions.value.mapIndexed { index, it -> + cardAdventureMissions.mapIndexed { index, it -> CardAdventureEntry( cardAdventureEntry = it, obscure = index > currentCardAdventure - 1 diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardScreenController.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardScreenController.kt index 6e53a60..836746f 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardScreenController.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardScreenController.kt @@ -1,10 +1,13 @@ package com.github.nacabaro.vbhelper.screens.cardScreen import com.github.nacabaro.vbhelper.dtos.CardDtos +import com.github.nacabaro.vbhelper.dtos.CharacterDtos +import kotlinx.coroutines.flow.Flow interface CardScreenController { fun renameCard(cardId: Long, newName: String, onRenamed: (String) -> Unit) fun deleteCard(cardId: Long, onDeleted: () -> Unit) - suspend fun getCardAdventureMissions(cardId: Long): List - suspend fun getCardProgress(cardId: Long): Int + fun getCardAdventureMissions(cardId: Long): Flow> + fun getCardProgress(cardId: Long): Flow + fun getFusionsForCharacters(characterId: Long): Flow> } \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardScreenControllerImpl.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardScreenControllerImpl.kt index 0fe45b5..ded0375 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardScreenControllerImpl.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardScreenControllerImpl.kt @@ -4,6 +4,8 @@ import androidx.activity.ComponentActivity import androidx.lifecycle.lifecycleScope import com.github.nacabaro.vbhelper.di.VBHelper import com.github.nacabaro.vbhelper.dtos.CardDtos +import com.github.nacabaro.vbhelper.dtos.CharacterDtos +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.launch class CardScreenControllerImpl( @@ -32,15 +34,22 @@ class CardScreenControllerImpl( } } - override suspend fun getCardAdventureMissions(cardId: Long): List { + override fun getCardAdventureMissions(cardId: Long): Flow> { return database .cardAdventureDao() .getAdventureForCard(cardId) } - override suspend fun getCardProgress(cardId: Long): Int { + override fun getCardProgress(cardId: Long): Flow { return database .cardProgressDao() .getCardProgress(cardId) } + + override fun getFusionsForCharacters(characterId: Long): Flow> { + return database + .cardFusionsDao() + .getFusionsForCharacter(characterId) + } + } \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardViewScreen.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardViewScreen.kt index 2cd5838..da95460 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardViewScreen.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardViewScreen.kt @@ -5,10 +5,10 @@ import androidx.compose.foundation.lazy.grid.LazyVerticalGrid import androidx.compose.foundation.lazy.grid.items import androidx.compose.material3.Scaffold import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.platform.LocalContext import androidx.navigation.NavController import com.github.nacabaro.vbhelper.utils.BitmapData @@ -19,32 +19,19 @@ import com.github.nacabaro.vbhelper.dtos.CharacterDtos import com.github.nacabaro.vbhelper.navigation.NavigationItems import com.github.nacabaro.vbhelper.screens.cardScreen.dialogs.DexCharaDetailsDialog import com.github.nacabaro.vbhelper.source.DexRepository -import kotlinx.coroutines.launch @Composable fun CardViewScreen( navController: NavController, cardId: Long ) { - val coroutineScope = rememberCoroutineScope() val application = LocalContext.current.applicationContext as VBHelper val dexRepository = DexRepository(application.container.db) - val characterList = remember { mutableStateOf>(emptyList()) } - val cardPossibleTransformations = remember { mutableStateOf>(emptyList()) } + val characterList by dexRepository.getCharactersByCardId(cardId).collectAsState(emptyList()) val selectedCharacter = remember { mutableStateOf(null) } - LaunchedEffect(dexRepository) { - coroutineScope.launch { - val newCharacterList = dexRepository.getCharactersByCardId(cardId) - characterList.value = newCharacterList - - val newCardPossibleTransformations = dexRepository.getCardPossibleTransformations(cardId) - cardPossibleTransformations.value = newCardPossibleTransformations - } - } - Scaffold ( topBar = { TopBanner( @@ -70,7 +57,7 @@ fun CardViewScreen( columns = GridCells.Fixed(3), contentPadding = contentPadding ) { - items(characterList.value) { character -> + items(characterList) { character -> CharacterEntry( onClick = { selectedCharacter.value = character @@ -88,7 +75,6 @@ fun CardViewScreen( if (selectedCharacter.value != null) { DexCharaDetailsDialog( currentChara = selectedCharacter.value!!, - possibleTransformations = cardPossibleTransformations.value, obscure = selectedCharacter.value!!.discoveredOn == null, onClickClose = { selectedCharacter.value = null diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardsScreen.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardsScreen.kt index e639444..fcb24b5 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardsScreen.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardsScreen.kt @@ -7,11 +7,10 @@ import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.material3.Scaffold import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext @@ -25,17 +24,15 @@ import com.github.nacabaro.vbhelper.navigation.NavigationItems import com.github.nacabaro.vbhelper.screens.cardScreen.dialogs.CardDeleteDialog import com.github.nacabaro.vbhelper.screens.cardScreen.dialogs.CardRenameDialog import com.github.nacabaro.vbhelper.source.DexRepository -import kotlinx.coroutines.launch @Composable fun CardsScreen( navController: NavController, cardScreenController: CardScreenControllerImpl ) { - val coroutineScope = rememberCoroutineScope() val application = LocalContext.current.applicationContext as VBHelper val dexRepository = DexRepository(application.container.db) - val cardList = remember { mutableStateOf>(emptyList()) } + val cardList by dexRepository.getAllDims().collectAsState(emptyList()) val selectedCard = remember { mutableStateOf(null) } var clickedDelete by remember { mutableStateOf(false) } @@ -43,13 +40,6 @@ fun CardsScreen( var modifyCards by remember { mutableStateOf(false) } - LaunchedEffect(dexRepository) { - coroutineScope.launch { - val newDimList = dexRepository.getAllDims() - cardList.value = newDimList - } - } - Scaffold ( topBar = { TopBanner( @@ -64,7 +54,7 @@ fun CardsScreen( modifier = Modifier .padding(top = contentPadding.calculateTopPadding()) ) { - items(cardList.value) { + items(cardList) { CardEntry( name = it.cardName, logo = BitmapData( diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/dialogs/DexCharaDetailsDialog.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/dialogs/DexCharaDetailsDialog.kt index 92beb74..364982e 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/dialogs/DexCharaDetailsDialog.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/dialogs/DexCharaDetailsDialog.kt @@ -16,6 +16,11 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.material3.contentColorFor import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.ColorFilter @@ -23,7 +28,9 @@ 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.di.VBHelper import com.github.nacabaro.vbhelper.dtos.CharacterDtos +import com.github.nacabaro.vbhelper.source.DexRepository import com.github.nacabaro.vbhelper.utils.BitmapData import com.github.nacabaro.vbhelper.utils.getImageBitmap @@ -31,14 +38,25 @@ import com.github.nacabaro.vbhelper.utils.getImageBitmap @Composable fun DexCharaDetailsDialog( currentChara: CharacterDtos.CardCharaProgress, - possibleTransformations: List, obscure: Boolean, onClickClose: () -> Unit ) { val nameMultiplier = 3 val charaMultiplier = 4 - val currentCharaPossibleTransformations = possibleTransformations.filter { it.fromCharaId == currentChara.id } + val application = LocalContext.current.applicationContext as VBHelper + val database = application.container.db + val dexRepository = DexRepository(database) + + var showFusions by remember { mutableStateOf(false) } + + val currentCharaPossibleTransformations by dexRepository + .getCharacterPossibleTransformations(currentChara.id) + .collectAsState(emptyList()) + + val currentCharaPossibleFusions by dexRepository + .getCharacterPossibleFusions(currentChara.id) + .collectAsState(emptyList()) val romanNumeralsStage = when (currentChara.stage) { 1 -> "II" @@ -204,12 +222,40 @@ fun DexCharaDetailsDialog( } } - Button( - onClick = onClickClose - ) { - Text("Close") + Row { + if (currentCharaPossibleFusions.isNotEmpty()) { + Button( + onClick = { + showFusions = true + } + ) { + Text("Fusions") + } + } + + Spacer( + modifier = Modifier + .padding(4.dp) + ) + + Button( + onClick = onClickClose + ) { + Text("Close") + } } } } } + + if (showFusions) { + DexCharaFusionsDialog( + currentChara = currentChara, + currentCharaPossibleFusions = currentCharaPossibleFusions, + onClickDismiss = { + showFusions = false + }, + obscure = obscure + ) + } } \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/dialogs/DexCharaFusionsDialog.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/dialogs/DexCharaFusionsDialog.kt new file mode 100644 index 0000000..be2296d --- /dev/null +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/dialogs/DexCharaFusionsDialog.kt @@ -0,0 +1,191 @@ +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 com.github.nacabaro.vbhelper.dtos.CharacterDtos +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.utils.BitmapData +import com.github.nacabaro.vbhelper.utils.getImageBitmap + +@Composable +fun DexCharaFusionsDialog( + currentChara: CharacterDtos.CardCharaProgress, + currentCharaPossibleFusions: List, + obscure: Boolean, + onClickDismiss: () -> Unit, +) { + val nameMultiplier = 3 + val charaMultiplier = 4 + + 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 = onClickDismiss, + ) { + Card( + modifier = Modifier + .fillMaxWidth() + ) { + Column( + modifier = Modifier + .padding(16.dp) + ) { + Row { + 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 + ) + } + } else { + Column { + Text(text = "????????????????") + } + } + } + + Spacer(modifier = Modifier.padding(16.dp)) + Column { + currentCharaPossibleFusions.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("Combine with ${it.fusionAttribute}") + } + } + } + } + } + + Button( + onClick = onClickDismiss + ) { + Text("Close") + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/HomeScreen.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/HomeScreen.kt index 4a5d3a0..53dd40f 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/HomeScreen.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/HomeScreen.kt @@ -1,6 +1,5 @@ package com.github.nacabaro.vbhelper.screens.homeScreens -import android.util.Log import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize @@ -92,7 +91,6 @@ fun HomeScreen( } ) { contentPadding -> if (activeMon.value == null || (beData.value == null && vbData.value == null) || transformationHistory.value == null) { - Log.d("TetTet", "Something is null") Column ( horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center, @@ -103,7 +101,6 @@ fun HomeScreen( Text(text = "Nothing to see here") } } else { - Log.d("TetTet", "Something is not null") if (activeMon.value!!.isBemCard) { BEBEmHomeScreen( activeMon = activeMon.value!!, diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/settingsScreen/SettingsScreenControllerImpl.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/settingsScreen/SettingsScreenControllerImpl.kt index 0e2a039..dd78c14 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/settingsScreen/SettingsScreenControllerImpl.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/settingsScreen/SettingsScreenControllerImpl.kt @@ -3,36 +3,24 @@ package com.github.nacabaro.vbhelper.screens.settingsScreen import androidx.lifecycle.lifecycleScope import kotlinx.coroutines.launch import android.net.Uri -import android.provider.OpenableColumns -import android.util.Log import android.widget.Toast import androidx.activity.ComponentActivity import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.contract.ActivityResultContracts -import com.github.cfogrady.vb.dim.card.BemCard -import com.github.cfogrady.vb.dim.card.DimCard -import com.github.cfogrady.vb.dim.card.DimReader -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.card.Card -import com.github.nacabaro.vbhelper.domain.card.CardProgress -import com.github.nacabaro.vbhelper.domain.card.CardCharacter +import com.github.nacabaro.vbhelper.screens.settingsScreen.controllers.CardImportController +import com.github.nacabaro.vbhelper.screens.settingsScreen.controllers.DatabaseManagementController import com.github.nacabaro.vbhelper.source.ApkSecretsImporter import com.github.nacabaro.vbhelper.source.SecretsImporter import com.github.nacabaro.vbhelper.source.SecretsRepository import com.github.nacabaro.vbhelper.source.proto.Secrets import kotlinx.coroutines.Dispatchers -import java.io.File -import java.io.InputStream -import java.io.OutputStream class SettingsScreenControllerImpl( private val context: ComponentActivity, ): SettingsScreenController { - private val roomDbName = "internalDb" private val filePickerLauncher: ActivityResultLauncher private val filePickerOpenerLauncher: ActivityResultLauncher> private val filePickerApk: ActivityResultLauncher> @@ -41,13 +29,17 @@ class SettingsScreenControllerImpl( private val application = context.applicationContext as VBHelper private val secretsRepository: SecretsRepository = application.container.dataStoreSecretsRepository private val database: AppDatabase = application.container.db + private val databaseManagementController = DatabaseManagementController( + componentActivity = context, + application = application + ) init { filePickerLauncher = context.registerForActivityResult( ActivityResultContracts.CreateDocument("application/octet-stream") ) { uri -> if (uri != null) { - exportDatabase(uri) + databaseManagementController.exportDatabase(uri) } else { context.runOnUiThread { Toast.makeText(context, "No destination selected", Toast.LENGTH_SHORT) @@ -60,7 +52,7 @@ class SettingsScreenControllerImpl( ActivityResultContracts.OpenDocument() ) { uri -> if (uri != null) { - importDatabase(uri) + databaseManagementController.importDatabase(uri) } else { context.runOnUiThread { Toast.makeText(context, "No source selected", Toast.LENGTH_SHORT).show() @@ -109,276 +101,14 @@ 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() - - 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( - CardCharacter( - 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 suspend fun importAdventureMissions( - cardId: Long, - card: com.github.cfogrady.vb.dim.card.Card<*, *, *, *, *, *> - ) { - Log.d("importAdventureMissions", "Importing adventure missions") - if (card is BemCard) { - card.adventureLevels.levels.forEach { - database - .cardAdventureDao() - .insertNewAdventure( - cardId = cardId, - characterId = it.bossCharacterIndex, - steps = it.steps, - bossAp = it.bossAp, - bossHp = it.bossHp, - bossDp = it.bossDp, - bossBp = it.bossBp - ) - } - } else if (card is DimCard) { - card.adventureLevels.levels.map { - database - .cardAdventureDao() - .insertNewAdventure( - cardId = cardId, - characterId = it.bossCharacterIndex, - steps = it.steps, - bossAp = it.bossAp, - bossHp = it.bossHp, - bossDp = it.bossDp, - bossBp = null - ) - } - } - } - - private suspend fun importCardFusions( - cardId: Long, - card: com.github.cfogrady.vb.dim.card.Card<*, *, *, *, *, *> - ) { - Log.d("importCardFusions", "Importing card fusions") - if (card is DimCard) { - card - .attributeFusions - .entries - .forEach { - database - .cardFusionsDao() - .insertNewFusion( - cardId = cardId, - fromCharaId = it.characterIndex, - toCharaIdAttr1 = it.attribute1Fusion, - toCharaIdAttr2 = it.attribute2Fusion, - toCharaIdAttr3 = it.attribute3Fusion, - toCharaIdAttr4 = it.attribute4Fusion - ) - } - } - } - - private fun updateCardProgress( - cardId: Long, - ) { - database - .cardProgressDao() - .insertCardProgress( - CardProgress( - cardId = cardId, - currentStage = 1, - unlocked = false - ) - ) - } - 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) - - val cardModel = Card( - cardId = card.header.dimId, - logo = card.spriteData.sprites[0].pixelData, - 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 cardId = database - .cardDao() - .insertNewCard(cardModel) - - updateCardProgress(cardId = cardId) - - importCharacterData(cardId, card) - - importEvoData(cardId, card) - - importAdventureMissions(cardId, card) - - importCardFusions(cardId, card) + val cardImportController = CardImportController(database) + cardImportController.importCard(fileReader) } inputStream?.close() @@ -388,100 +118,6 @@ class SettingsScreenControllerImpl( } } - private fun exportDatabase(destinationUri: Uri) { - context.lifecycleScope.launch(Dispatchers.IO) { - try { - val dbFile = File(context.getDatabasePath(roomDbName).absolutePath) - if (!dbFile.exists()) { - throw IllegalStateException("Database file does not exist!") - } - - application.container.db.close() - - context.contentResolver.openOutputStream(destinationUri)?.use { outputStream -> - dbFile.inputStream().use { inputStream -> - copyFile(inputStream, outputStream) - } - } ?: throw IllegalArgumentException("Unable to open destination Uri for writing") - - context.runOnUiThread { - Toast.makeText(context, "Database exported successfully!", Toast.LENGTH_SHORT).show() - Toast.makeText(context, "Closing application to avoid changes.", Toast.LENGTH_LONG).show() - context.finishAffinity() - } - } catch (e: Exception) { - Log.e("ScanScreenController", "Error exporting database $e") - context.runOnUiThread { - Toast.makeText(context, "Error exporting database: ${e.message}", Toast.LENGTH_LONG).show() - } - } - } - } - - private fun importDatabase(sourceUri: Uri) { - context.lifecycleScope.launch(Dispatchers.IO) { - try { - if (!getFileNameFromUri(sourceUri)!!.endsWith(".vbhelper")) { - context.runOnUiThread { - Toast.makeText(context, "Invalid file format", Toast.LENGTH_SHORT).show() - } - return@launch - } - - application.container.db.close() - - val dbPath = context.getDatabasePath(roomDbName) - val shmFile = File(dbPath.parent, "$roomDbName-shm") - val walFile = File(dbPath.parent, "$roomDbName-wal") - - // Delete existing database files - if (dbPath.exists()) dbPath.delete() - if (shmFile.exists()) shmFile.delete() - if (walFile.exists()) walFile.delete() - - val dbFile = File(dbPath.absolutePath) - - context.contentResolver.openInputStream(sourceUri)?.use { inputStream -> - dbFile.outputStream().use { outputStream -> - copyFile(inputStream, outputStream) - } - } ?: throw IllegalArgumentException("Unable to open source Uri for reading") - - context.runOnUiThread { - Toast.makeText(context, "Database imported successfully!", Toast.LENGTH_SHORT).show() - Toast.makeText(context, "Reopen the app to finish import process!", Toast.LENGTH_LONG).show() - context.finishAffinity() - } - } catch (e: Exception) { - Log.e("ScanScreenController", "Error importing database $e") - context.runOnUiThread { - Toast.makeText(context, "Error importing database: ${e.message}", Toast.LENGTH_LONG).show() - } - } - } - } - - private fun getFileNameFromUri(uri: Uri): String? { - var fileName: String? = null - val cursor = context.contentResolver.query(uri, null, null, null, null) - cursor?.use { - if (it.moveToFirst()) { - val nameIndex = it.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME) - fileName = it.getString(nameIndex) - } - } - return fileName - } - - private fun copyFile(inputStream: InputStream, outputStream: OutputStream) { - val buffer = ByteArray(1024) - var bytesRead: Int - while (inputStream.read(buffer).also { bytesRead = it } != -1) { - outputStream.write(buffer, 0, bytesRead) - } - outputStream.flush() - } - private fun importApk(uri: Uri) { context.lifecycleScope.launch(Dispatchers.IO) { context.contentResolver.openInputStream(uri).use { diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/settingsScreen/controllers/CardImportController.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/settingsScreen/controllers/CardImportController.kt new file mode 100644 index 0000000..1626405 --- /dev/null +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/settingsScreen/controllers/CardImportController.kt @@ -0,0 +1,320 @@ +package com.github.nacabaro.vbhelper.screens.settingsScreen.controllers + +import android.util.Log +import com.github.cfogrady.vb.dim.card.BemCard +import com.github.cfogrady.vb.dim.card.DimCard +import com.github.cfogrady.vb.dim.card.DimReader +import com.github.cfogrady.vbnfc.data.NfcCharacter +import com.github.nacabaro.vbhelper.database.AppDatabase +import com.github.nacabaro.vbhelper.domain.card.Card +import com.github.nacabaro.vbhelper.domain.card.CardCharacter +import com.github.nacabaro.vbhelper.domain.card.CardProgress +import com.github.nacabaro.vbhelper.domain.characters.Sprite +import java.io.InputStream + +class CardImportController( + private val database: AppDatabase +) { + suspend fun importCard( + fileReader: InputStream? + ) { + val dimReader = DimReader() + val card = dimReader.readCard(fileReader, false) + + val cardModel = Card( + cardId = card.header.dimId, + logo = card.spriteData.sprites[0].pixelData, + 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 cardId = database + .cardDao() + .insertNewCard(cardModel) + + updateCardProgress(cardId = cardId) + + importCharacterData(cardId, card) + + importEvoData(cardId, card) + + importAdventureMissions(cardId, card) + + importCardFusions(cardId, card) + } + + private fun updateCardProgress( + cardId: Long, + ) { + database + .cardProgressDao() + .insertCardProgress( + CardProgress( + cardId = cardId, + currentStage = 1, + unlocked = false + ) + ) + } + + 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() + + 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( + CardCharacter( + 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 suspend fun importAdventureMissions( + cardId: Long, + card: com.github.cfogrady.vb.dim.card.Card<*, *, *, *, *, *> + ) { + Log.d("importAdventureMissions", "Importing adventure missions") + if (card is BemCard) { + card.adventureLevels.levels.forEach { + database + .cardAdventureDao() + .insertNewAdventure( + cardId = cardId, + characterId = it.bossCharacterIndex, + steps = it.steps, + bossAp = it.bossAp, + bossHp = it.bossHp, + bossDp = it.bossDp, + bossBp = it.bossBp + ) + } + } else if (card is DimCard) { + card.adventureLevels.levels.map { + database + .cardAdventureDao() + .insertNewAdventure( + cardId = cardId, + characterId = it.bossCharacterIndex, + steps = it.steps, + bossAp = it.bossAp, + bossHp = it.bossHp, + bossDp = it.bossDp, + bossBp = null + ) + } + } + } + + private suspend fun importCardFusions( + cardId: Long, + card: com.github.cfogrady.vb.dim.card.Card<*, *, *, *, *, *> + ) { + Log.d("importCardFusions", "Importing card fusions") + if (card is DimCard) { + card + .attributeFusions + .entries + .forEach { + Log.d("importCardFusions", "Importing fusion: ${it.attribute1Fusion}") + if (it.attribute1Fusion != 65535 && it.characterIndex != 65535) { + database + .cardFusionsDao() + .insertNewFusion( + cardId = cardId, + fromCharaId = it.characterIndex, + attribute = NfcCharacter.Attribute.Virus, + toCharaId = it.attribute1Fusion, + ) + } + + if (it.attribute2Fusion != 65535 && it.characterIndex != 65535) { + database + .cardFusionsDao() + .insertNewFusion( + cardId = cardId, + fromCharaId = it.characterIndex, + attribute = NfcCharacter.Attribute.Data, + toCharaId = it.attribute2Fusion, + ) + } + + if (it.attribute3Fusion != 65535 && it.characterIndex != 65535) { + database + .cardFusionsDao() + .insertNewFusion( + cardId = cardId, + fromCharaId = it.characterIndex, + attribute = NfcCharacter.Attribute.Vaccine, + toCharaId = it.attribute3Fusion, + ) + } + + if (it.attribute4Fusion != 65535 && it.characterIndex != 65535) { + database + .cardFusionsDao() + .insertNewFusion( + cardId = cardId, + fromCharaId = it.characterIndex, + attribute = NfcCharacter.Attribute.Free, + toCharaId = it.attribute4Fusion, + ) + } + } + } + } + + 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 + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/settingsScreen/controllers/DatabaseManagementController.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/settingsScreen/controllers/DatabaseManagementController.kt new file mode 100644 index 0000000..f3fb931 --- /dev/null +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/settingsScreen/controllers/DatabaseManagementController.kt @@ -0,0 +1,115 @@ +package com.github.nacabaro.vbhelper.screens.settingsScreen.controllers + +import android.net.Uri +import android.provider.OpenableColumns +import android.util.Log +import android.widget.Toast +import androidx.activity.ComponentActivity +import androidx.lifecycle.lifecycleScope +import com.github.nacabaro.vbhelper.di.VBHelper +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import java.io.File +import java.io.InputStream +import java.io.OutputStream + +class DatabaseManagementController( + val componentActivity: ComponentActivity, + val application: VBHelper +) { + private val roomDbName = "internalDb" + + fun exportDatabase( destinationUri: Uri) { + componentActivity.lifecycleScope.launch(Dispatchers.IO) { + try { + val dbFile = File(componentActivity.getDatabasePath(roomDbName).absolutePath) + if (!dbFile.exists()) { + throw IllegalStateException("Database file does not exist!") + } + + application.container.db.close() + + componentActivity.contentResolver.openOutputStream(destinationUri)?.use { outputStream -> + dbFile.inputStream().use { inputStream -> + copyFile(inputStream, outputStream) + } + } ?: throw IllegalArgumentException("Unable to open destination Uri for writing") + + componentActivity.runOnUiThread { + Toast.makeText(componentActivity, "Database exported successfully!", Toast.LENGTH_SHORT).show() + Toast.makeText(componentActivity, "Closing application to avoid changes.", Toast.LENGTH_LONG).show() + componentActivity.finishAffinity() + } + } catch (e: Exception) { + Log.e("ScanScreenController", "Error exporting database $e") + componentActivity.runOnUiThread { + Toast.makeText(componentActivity, "Error exporting database: ${e.message}", Toast.LENGTH_LONG).show() + } + } + } + } + + fun importDatabase(sourceUri: Uri) { + componentActivity.lifecycleScope.launch(Dispatchers.IO) { + try { + if (!getFileNameFromUri(sourceUri)!!.endsWith(".vbhelper")) { + componentActivity.runOnUiThread { + Toast.makeText(componentActivity, "Invalid file format", Toast.LENGTH_SHORT).show() + } + return@launch + } + + application.container.db.close() + + val dbPath = componentActivity.getDatabasePath(roomDbName) + val shmFile = File(dbPath.parent, "$roomDbName-shm") + val walFile = File(dbPath.parent, "$roomDbName-wal") + + // Delete existing database files + if (dbPath.exists()) dbPath.delete() + if (shmFile.exists()) shmFile.delete() + if (walFile.exists()) walFile.delete() + + val dbFile = File(dbPath.absolutePath) + + componentActivity.contentResolver.openInputStream(sourceUri)?.use { inputStream -> + dbFile.outputStream().use { outputStream -> + copyFile(inputStream, outputStream) + } + } ?: throw IllegalArgumentException("Unable to open source Uri for reading") + + componentActivity.runOnUiThread { + Toast.makeText(componentActivity, "Database imported successfully!", Toast.LENGTH_SHORT).show() + Toast.makeText(componentActivity, "Reopen the app to finish import process!", Toast.LENGTH_LONG).show() + componentActivity.finishAffinity() + } + } catch (e: Exception) { + Log.e("ScanScreenController", "Error importing database $e") + componentActivity.runOnUiThread { + Toast.makeText(componentActivity, "Error importing database: ${e.message}", Toast.LENGTH_LONG).show() + } + } + } + } + + private fun copyFile(inputStream: InputStream, outputStream: OutputStream) { + val buffer = ByteArray(1024) + var bytesRead: Int + while (inputStream.read(buffer).also { bytesRead = it } != -1) { + outputStream.write(buffer, 0, bytesRead) + } + outputStream.flush() + } + + private fun getFileNameFromUri(uri: Uri): String? { + var fileName: String? = null + val cursor = componentActivity.contentResolver.query(uri, null, null, null, null) + cursor?.use { + if (it.moveToFirst()) { + val nameIndex = it.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME) + fileName = it.getString(nameIndex) + } + } + return fileName + } +} \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/source/DexRepository.kt b/app/src/main/java/com/github/nacabaro/vbhelper/source/DexRepository.kt index dcec614..585392c 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/source/DexRepository.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/source/DexRepository.kt @@ -3,19 +3,24 @@ package com.github.nacabaro.vbhelper.source import com.github.nacabaro.vbhelper.database.AppDatabase import com.github.nacabaro.vbhelper.dtos.CardDtos import com.github.nacabaro.vbhelper.dtos.CharacterDtos +import kotlinx.coroutines.flow.Flow class DexRepository ( private val db: AppDatabase ) { - suspend fun getAllDims(): List { + fun getAllDims(): Flow> { return db.dexDao().getCardsWithProgress() } - suspend fun getCharactersByCardId(cardId: Long): List { + fun getCharactersByCardId(cardId: Long): Flow> { return db.dexDao().getSingleCardProgress(cardId) } - suspend fun getCardPossibleTransformations(cardId: Long): List { - return db.characterDao().getEvolutionRequirementsForCard(cardId) + fun getCharacterPossibleTransformations(characterId: Long): Flow> { + return db.characterDao().getEvolutionRequirementsForCard(characterId) + } + + fun getCharacterPossibleFusions(characterId: Long): Flow> { + return db.cardFusionsDao().getFusionsForCharacter(characterId) } } \ No newline at end of file From dce186737dcdcfc6ca8d3ae5a9d22d938e5af0b1 Mon Sep 17 00:00:00 2001 From: Nacho Date: Sun, 16 Nov 2025 01:34:31 +0100 Subject: [PATCH 6/8] Other few things - I changed more things to flows from the database - Cleaned up the logic coming from the scan screen - Added a delete button to a character. CAREFUL, IT HAS NO CONFIRMATION YET! - Fixed a few things, now scanning is more stable and will fix the second whoops thing. - Quick patch, should improve stability when writing to the watch --- .../github/nacabaro/vbhelper/daos/CardDao.kt | 3 +- .../vbhelper/daos/UserCharacterDao.kt | 3 +- .../itemsScreen/ChooseCharacterScreen.kt | 5 +- .../scanScreen/ChooseConnectionScreen.kt | 78 ++++++ .../vbhelper/screens/scanScreen/ScanScreen.kt | 232 ++---------------- .../scanScreen/converters/FromNfcConverter.kt | 7 +- .../scanScreen/converters/ToNfcConverter.kt | 2 + .../ActionScreen.kt} | 9 +- .../scanScreen/screens/ReadCharacterScreen.kt | 59 +++++ .../scanScreen/screens/ReadingScreen.kt | 109 ++++++++ .../scanScreen/screens/WriteCardScreen.kt | 132 ++++++++++ .../screens/WriteCharacterScreen.kt | 135 ++++++++++ .../scanScreen/screens/WritingScreen.kt | 140 +++++++++++ .../screens/storageScreen/StorageDialog.kt | 8 + .../screens/storageScreen/StorageScreen.kt | 31 +-- .../storageScreen/StorageScreenController.kt | 1 + .../StorageScreenControllerImpl.kt | 17 ++ .../vbhelper/source/ScanRepository.kt | 13 + .../vbhelper/source/StorageRepository.kt | 3 +- 19 files changed, 752 insertions(+), 235 deletions(-) create mode 100644 app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/ChooseConnectionScreen.kt rename app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/{ReadingCharacter.kt => screens/ActionScreen.kt} (85%) create mode 100644 app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/screens/ReadCharacterScreen.kt create mode 100644 app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/screens/ReadingScreen.kt create mode 100644 app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/screens/WriteCardScreen.kt create mode 100644 app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/screens/WriteCharacterScreen.kt create mode 100644 app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/screens/WritingScreen.kt create mode 100644 app/src/main/java/com/github/nacabaro/vbhelper/source/ScanRepository.kt diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/daos/CardDao.kt b/app/src/main/java/com/github/nacabaro/vbhelper/daos/CardDao.kt index 0401f9e..e9511f2 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/daos/CardDao.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/daos/CardDao.kt @@ -5,6 +5,7 @@ import androidx.room.Insert import androidx.room.OnConflictStrategy import androidx.room.Query import com.github.nacabaro.vbhelper.domain.card.Card +import kotlinx.coroutines.flow.Flow @Dao interface CardDao { @@ -26,7 +27,7 @@ interface CardDao { WHERE uc.id = :id """ ) - suspend fun getCardByCharacterId(id: Long): Card + fun getCardByCharacterId(id: Long): Flow @Query("UPDATE Card SET name = :newName WHERE id = :id") suspend fun renameCard(id: Int, newName: String) diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/daos/UserCharacterDao.kt b/app/src/main/java/com/github/nacabaro/vbhelper/daos/UserCharacterDao.kt index 42fb503..02f9293 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/daos/UserCharacterDao.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/daos/UserCharacterDao.kt @@ -13,6 +13,7 @@ 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 +import kotlinx.coroutines.flow.Flow @Dao interface UserCharacterDao { @@ -76,7 +77,7 @@ interface UserCharacterDao { LEFT JOIN Adventure a ON a.characterId = uc.id """ ) - suspend fun getAllCharacters(): List + fun getAllCharacters(): Flow> @Query( """ diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/itemsScreen/ChooseCharacterScreen.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/itemsScreen/ChooseCharacterScreen.kt index 26be571..5b2b884 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/itemsScreen/ChooseCharacterScreen.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/itemsScreen/ChooseCharacterScreen.kt @@ -24,6 +24,7 @@ import com.github.nacabaro.vbhelper.dtos.CharacterDtos import com.github.nacabaro.vbhelper.dtos.ItemDtos import com.github.nacabaro.vbhelper.source.StorageRepository import com.github.nacabaro.vbhelper.utils.BitmapData +import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch @@ -35,7 +36,7 @@ fun ChooseCharacterScreen( ) { val coroutineScope = rememberCoroutineScope() val application = LocalContext.current.applicationContext as VBHelper - val storageRepository = StorageRepository(application.container.db) + val storageRepository = StorageRepository(application.container.db, ) val characterList = remember { mutableStateOf>(emptyList()) } @@ -57,7 +58,7 @@ fun ChooseCharacterScreen( characterList.value = storageRepository.getVBCharacters() } else -> { - characterList.value = storageRepository.getAllCharacters() + characterList.value = storageRepository.getAllCharacters().first() } } } diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/ChooseConnectionScreen.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/ChooseConnectionScreen.kt new file mode 100644 index 0000000..34180ba --- /dev/null +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/ChooseConnectionScreen.kt @@ -0,0 +1,78 @@ +package com.github.nacabaro.vbhelper.screens.scanScreen + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Button +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.navigation.NavController +import com.github.nacabaro.vbhelper.components.TopBanner + +@Composable +fun ChooseConnectOption( + onClickRead: (() -> Unit)? = null, + onClickWrite: (() -> Unit)? = null, + navController: NavController +) { + Scaffold( + topBar = { + TopBanner( + text = "Scan a Vital Bracelet", + onBackClick = { + navController.popBackStack() + } + ) + } + ) { contentPadding -> + Column( + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier + .fillMaxSize() + .padding(contentPadding) + ) { + ScanButton( + text = "Vital Bracelet to App", + disabled = onClickRead == null, + onClick = onClickRead?: { }, + ) + Spacer(modifier = Modifier.height(16.dp)) + ScanButton( + text = "App to Vital Bracelet", + disabled = onClickWrite == null, + onClick = onClickWrite?: { }, + ) + } + } +} + + +@Composable +fun ScanButton( + text: String, + onClick: () -> Unit, + modifier: Modifier = Modifier, + disabled: Boolean = false, +) { + Button( + onClick = onClick, + modifier = modifier, + enabled = !disabled, + ) { + Text( + text = text, + fontSize = 16.sp, + modifier = Modifier + .padding(4.dp) + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/ScanScreen.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/ScanScreen.kt index a58b2b2..8dec3b6 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/ScanScreen.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/ScanScreen.kt @@ -1,38 +1,24 @@ package com.github.nacabaro.vbhelper.screens.scanScreen import android.widget.Toast -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.material3.Button -import androidx.compose.material3.Scaffold -import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp import androidx.navigation.NavController import androidx.navigation.compose.rememberNavController import com.github.cfogrady.vbnfc.data.NfcCharacter import com.github.nacabaro.vbhelper.ActivityLifecycleListener -import com.github.nacabaro.vbhelper.components.TopBanner import com.github.nacabaro.vbhelper.di.VBHelper import com.github.nacabaro.vbhelper.domain.card.Card import com.github.nacabaro.vbhelper.navigation.NavigationItems -import com.github.nacabaro.vbhelper.screens.cardScreen.ChooseCard +import com.github.nacabaro.vbhelper.screens.scanScreen.screens.ReadingScreen +import com.github.nacabaro.vbhelper.screens.scanScreen.screens.WritingScreen import com.github.nacabaro.vbhelper.source.StorageRepository import com.github.nacabaro.vbhelper.source.isMissingSecrets import com.github.nacabaro.vbhelper.source.proto.Secrets @@ -55,8 +41,6 @@ fun ScanScreen( val storageRepository = StorageRepository(application.container.db) var nfcCharacter by remember { mutableStateOf(null) } - var cardsRead by remember { mutableStateOf?>(null) } - val context = LocalContext.current LaunchedEffect(storageRepository) { @@ -73,143 +57,33 @@ fun ScanScreen( } } - var readingScreen by remember { mutableStateOf(false) } var writingScreen by remember { mutableStateOf(false) } - var cardSelectScreen by remember { mutableStateOf(false) } - var isDoneReadingCharacter by remember { mutableStateOf(false) } - var isDoneSendingCard by remember { mutableStateOf(false) } - var isDoneWritingCharacter by remember { mutableStateOf(false) } + var readingScreen by remember { mutableStateOf(false) } - DisposableEffect(readingScreen) { - if(readingScreen) { - scanScreenController.registerActivityLifecycleListener( - SCAN_SCREEN_ACTIVITY_LIFECYCLE_LISTENER, - object: ActivityLifecycleListener { - override fun onPause() { - scanScreenController.cancelRead() - } - - override fun onResume() { - scanScreenController.onClickRead( - secrets = secrets!!, - onComplete = { - isDoneReadingCharacter = true - }, - onMultipleCards = { cards -> - cardsRead = cards - readingScreen = false - cardSelectScreen = true - isDoneReadingCharacter = true - } - ) - } - } - ) - scanScreenController.onClickRead( - secrets = secrets!!, - onComplete = { - isDoneReadingCharacter = true - }, - onMultipleCards = { cards -> - cardsRead = cards - readingScreen = false - cardSelectScreen = true - isDoneReadingCharacter = true - } - ) - } - 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 { - override fun onPause() { - scanScreenController.cancelRead() - } - - override fun onResume() { - if (!isDoneSendingCard) { - scanScreenController.onClickCheckCard(secrets!!, nfcCharacter!!) { - isDoneSendingCard = true - } - } else if (!isDoneWritingCharacter) { - scanScreenController.onClickWrite(secrets!!, nfcCharacter!!) { - isDoneWritingCharacter = true - } - } - } - } - ) - } - - if (secrets != null && nfcCharacter != null) { - if (!isDoneSendingCard) { - scanScreenController.onClickCheckCard(secrets!!, nfcCharacter!!) { - isDoneSendingCard = true - } - } else if (!isDoneWritingCharacter) { - scanScreenController.onClickWrite(secrets!!, nfcCharacter!!) { - isDoneWritingCharacter = true - } - } - } - - onDispose { - if(writingScreen) { - scanScreenController.unregisterActivityLifecycleListener(SCAN_SCREEN_ACTIVITY_LIFECYCLE_LISTENER) - scanScreenController.cancelRead() - } - } - } - - if (isDoneReadingCharacter && !cardSelectScreen) { - readingScreen = false - navController.navigate(NavigationItems.Home.route) - } else if (isDoneSendingCard && isDoneWritingCharacter) { - writingScreen = false - navController.navigate(NavigationItems.Home.route) - LaunchedEffect(storageRepository) { - withContext(Dispatchers.IO) { - storageRepository - .deleteCharacter(characterId!!) - } - } - } - - if (readingScreen) { - ReadingCharacterScreen("Reading character") { - readingScreen = false - scanScreenController.cancelRead() - } - } else if (writingScreen) { - if (!isDoneSendingCard) { - ReadingCharacterScreen("Sending card") { + if (writingScreen && nfcCharacter != null && characterId != null) { + WritingScreen( + scanScreenController = scanScreenController, + nfcCharacter = nfcCharacter!!, + characterId = characterId, + onComplete = { writingScreen = false - scanScreenController.cancelRead() - } - } else if (!isDoneWritingCharacter) { - ReadingCharacterScreen("Writing character") { - isDoneSendingCard = false + navController.navigate(NavigationItems.Home.route) + }, + onCancel = { writingScreen = false - scanScreenController.cancelRead() + navController.navigate(NavigationItems.Home.route) } - } - } else if (cardSelectScreen) { - ChooseCard( - cards = cardsRead!!, - onCardSelected = { card -> - cardSelectScreen = false - scanScreenController.flushCharacter(card.id) + ) + } else if (readingScreen) { + ReadingScreen( + scanScreenController = scanScreenController, + onCancel = { + readingScreen = false + navController.navigate(NavigationItems.Home.route) + }, + onComplete = { + readingScreen = false + navController.navigate(NavigationItems.Home.route) } ) } else { @@ -247,66 +121,8 @@ fun ScanScreen( } } -@Composable -fun ChooseConnectOption( - onClickRead: (() -> Unit)? = null, - onClickWrite: (() -> Unit)? = null, - navController: NavController -) { - Scaffold( - topBar = { - TopBanner( - text = "Scan a Vital Bracelet", - onBackClick = { - navController.popBackStack() - } - ) - } - ) { contentPadding -> - Column( - verticalArrangement = Arrangement.Center, - horizontalAlignment = Alignment.CenterHorizontally, - modifier = Modifier - .fillMaxSize() - .padding(contentPadding) - ) { - ScanButton( - text = "Vital Bracelet to App", - disabled = onClickRead == null, - onClick = onClickRead?: { }, - ) - Spacer(modifier = Modifier.height(16.dp)) - ScanButton( - text = "App to Vital Bracelet", - disabled = onClickWrite == null, - onClick = onClickWrite?: { }, - ) - } - } -} -@Composable -fun ScanButton( - text: String, - onClick: () -> Unit, - modifier: Modifier = Modifier, - disabled: Boolean = false, -) { - Button( - onClick = onClick, - modifier = modifier, - enabled = !disabled, - ) { - Text( - text = text, - fontSize = 16.sp, - modifier = Modifier - .padding(4.dp) - ) - } -} - @Preview(showBackground = true) @Composable fun ScanScreenPreview() { diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/converters/FromNfcConverter.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/converters/FromNfcConverter.kt index 7acb137..dcc85a3 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/converters/FromNfcConverter.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/converters/FromNfcConverter.kt @@ -1,13 +1,11 @@ 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 @@ -250,10 +248,11 @@ class FromNfcConverter ( ) { val vitalsHistoryWatch = nfcCharacter.vitalHistory val vitalsHistory = vitalsHistoryWatch.map { historyElement -> - Log.d("VitalsHistory", "${historyElement.year.toInt()} ${historyElement.month.toInt()} ${historyElement.day.toInt()}") + val year = if (historyElement.year.toInt() !in 2021 .. 2035) 0 else historyElement.year.toInt() + VitalsHistory( charId = characterId, - year = historyElement.year.toInt(), + year = year, month = historyElement.month.toInt(), day = historyElement.day.toInt(), vitalPoints = historyElement.vitalsGained.toInt() diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/converters/ToNfcConverter.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/converters/ToNfcConverter.kt index dbc58fc..2078238 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/converters/ToNfcConverter.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/converters/ToNfcConverter.kt @@ -14,6 +14,7 @@ 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 kotlinx.coroutines.flow.first import java.util.Date class ToNfcConverter( @@ -96,6 +97,7 @@ class ToNfcConverter( val cardData = database .cardDao() .getCardByCharacterId(userCharacter.id) + .first() val appReserved = Array(3) { 0u diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/ReadingCharacter.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/screens/ActionScreen.kt similarity index 85% rename from app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/ReadingCharacter.kt rename to app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/screens/ActionScreen.kt index 89ce2ed..8d216f2 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/ReadingCharacter.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/screens/ActionScreen.kt @@ -1,4 +1,4 @@ -package com.github.nacabaro.vbhelper.screens.scanScreen +package com.github.nacabaro.vbhelper.screens.scanScreen.screens import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -14,13 +14,16 @@ import androidx.compose.ui.unit.dp import com.github.nacabaro.vbhelper.components.TopBanner @Composable -fun ReadingCharacterScreen( +fun ActionScreen( topBannerText: String, onClickCancel: () -> Unit, ) { Scaffold ( topBar = { - TopBanner(topBannerText) + TopBanner( + text = topBannerText, + onBackClick = onClickCancel + ) } ) { innerPadding -> Column ( diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/screens/ReadCharacterScreen.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/screens/ReadCharacterScreen.kt new file mode 100644 index 0000000..757cc49 --- /dev/null +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/screens/ReadCharacterScreen.kt @@ -0,0 +1,59 @@ +package com.github.nacabaro.vbhelper.screens.scanScreen.screens + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Button +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import com.github.nacabaro.vbhelper.components.TopBanner + +@Composable +fun ReadCharacterScreen( + onClickCancel: () -> Unit, + onClickConfirm: () -> Unit +) { + Scaffold( + topBar = { + TopBanner( + text = "Read character", + onBackClick = onClickCancel + ) + } + ) { innerPadding -> + Column( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center, + modifier = Modifier + .padding(innerPadding) + .fillMaxSize() + ) { + Text( + text = "Prepare your device!", + textAlign = TextAlign.Center + ) + + Text( + text = "Go to connect and when ready press confirm!", + textAlign = TextAlign.Center + ) + + Spacer( + modifier = Modifier.padding(8.dp) + ) + + Button( + onClick = onClickConfirm, + ) { + Text("Confirm") + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/screens/ReadingScreen.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/screens/ReadingScreen.kt new file mode 100644 index 0000000..58432ce --- /dev/null +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/screens/ReadingScreen.kt @@ -0,0 +1,109 @@ +package com.github.nacabaro.vbhelper.screens.scanScreen.screens + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import com.github.nacabaro.vbhelper.ActivityLifecycleListener +import com.github.nacabaro.vbhelper.domain.card.Card +import com.github.nacabaro.vbhelper.screens.cardScreen.ChooseCard +import com.github.nacabaro.vbhelper.screens.scanScreen.SCAN_SCREEN_ACTIVITY_LIFECYCLE_LISTENER +import com.github.nacabaro.vbhelper.screens.scanScreen.ScanScreenController + +@Composable +fun ReadingScreen( + scanScreenController: ScanScreenController, + onCancel: () -> Unit, + onComplete: () -> Unit +) { + val secrets by scanScreenController.secretsFlow.collectAsState(null) + + var cardsRead by remember { mutableStateOf?>(null) } + + var readingScreen by remember { mutableStateOf(false) } + var isDoneReadingCharacter by remember { mutableStateOf(false) } + var cardSelectScreen by remember { mutableStateOf(false) } + + DisposableEffect(readingScreen) { + if(readingScreen) { + scanScreenController.registerActivityLifecycleListener( + SCAN_SCREEN_ACTIVITY_LIFECYCLE_LISTENER, + object: ActivityLifecycleListener { + override fun onPause() { + scanScreenController.cancelRead() + } + + override fun onResume() { + scanScreenController.onClickRead( + secrets = secrets!!, + onComplete = { + isDoneReadingCharacter = true + }, + onMultipleCards = { cards -> + cardsRead = cards + readingScreen = false + cardSelectScreen = true + isDoneReadingCharacter = true + } + ) + } + } + ) + scanScreenController.onClickRead( + secrets = secrets!!, + onComplete = { + isDoneReadingCharacter = true + }, + onMultipleCards = { cards -> + cardsRead = cards + readingScreen = false + cardSelectScreen = true + isDoneReadingCharacter = true + } + ) + } + onDispose { + if(readingScreen) { + scanScreenController.unregisterActivityLifecycleListener( + SCAN_SCREEN_ACTIVITY_LIFECYCLE_LISTENER + ) + scanScreenController.cancelRead() + } + } + } + + if (isDoneReadingCharacter && !cardSelectScreen) { + readingScreen = false + onComplete() + } + + if (!readingScreen) { + ReadCharacterScreen( + onClickConfirm = { + readingScreen = true + }, + onClickCancel = { + onCancel() + } + ) + } + + if (readingScreen) { + ActionScreen("Reading character") { + readingScreen = false + scanScreenController.cancelRead() + onCancel() + } + } else if (cardSelectScreen) { + ChooseCard( + cards = cardsRead!!, + onCardSelected = { card -> + cardSelectScreen = false + scanScreenController.flushCharacter(card.id) + } + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/screens/WriteCardScreen.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/screens/WriteCardScreen.kt new file mode 100644 index 0000000..db47745 --- /dev/null +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/screens/WriteCardScreen.kt @@ -0,0 +1,132 @@ +package com.github.nacabaro.vbhelper.screens.scanScreen.screens + +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +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.Scaffold +import androidx.compose.material3.Text +import androidx.compose.material3.contentColorFor +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.FilterQuality +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.unit.dp +import com.github.nacabaro.vbhelper.components.TopBanner +import com.github.nacabaro.vbhelper.di.VBHelper +import com.github.nacabaro.vbhelper.domain.card.Card +import com.github.nacabaro.vbhelper.source.ScanRepository +import com.github.nacabaro.vbhelper.utils.BitmapData +import com.github.nacabaro.vbhelper.utils.getImageBitmap + +@Composable +fun WriteCardScreen( + characterId: Long, + onClickCancel: () -> Unit, + onClickConfirm: () -> Unit +) { + val application = LocalContext.current.applicationContext as VBHelper + val database = application.container.db + val scanRepository = ScanRepository(database) + val cardDetails by scanRepository.getCardDetails(characterId).collectAsState(Card( + id = 0, + cardId = 0, + name = "", + logo = byteArrayOf(), + logoHeight = 0, + logoWidth = 0, + stageCount = 0, + isBEm = false + )) + + Scaffold( + topBar = { + TopBanner( + text = "Writing card details", + onBackClick = onClickCancel + ) + } + ) { innerPadding -> + Column( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center, + modifier = Modifier + .padding(innerPadding) + .fillMaxSize() + ) { + Card ( + modifier = Modifier + .fillMaxWidth() + .padding(8.dp) + ) { + Row ( + modifier = Modifier.padding(16.dp), + ){ + if (cardDetails.logoHeight > 0 && cardDetails.logoWidth > 0) { + val charaBitmapData = BitmapData( + bitmap = cardDetails.logo, + width = cardDetails.logoWidth, + height = cardDetails.logoHeight + ) + val charaImageBitmapData = charaBitmapData.getImageBitmap( + context = LocalContext.current, + multiplier = 4, + obscure = false + ) + + 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), + filterQuality = FilterQuality.None + ) + } + } + + Spacer( + modifier = Modifier.width(8.dp) + ) + + Column { + Text("Get your device Ready!") + Text("You will need ${cardDetails.name} card!") + } + } + + } + + Button( + onClick = onClickConfirm, + ) { + Text("Confirm") + } + } + } +} diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/screens/WriteCharacterScreen.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/screens/WriteCharacterScreen.kt new file mode 100644 index 0000000..abd2606 --- /dev/null +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/screens/WriteCharacterScreen.kt @@ -0,0 +1,135 @@ +package com.github.nacabaro.vbhelper.screens.scanScreen.screens + +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +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.Scaffold +import androidx.compose.material3.Text +import androidx.compose.material3.contentColorFor +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.FilterQuality +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.unit.dp +import com.github.nacabaro.vbhelper.components.TopBanner +import com.github.nacabaro.vbhelper.di.VBHelper +import com.github.nacabaro.vbhelper.domain.card.Card +import com.github.nacabaro.vbhelper.source.ScanRepository +import com.github.nacabaro.vbhelper.utils.BitmapData +import com.github.nacabaro.vbhelper.utils.getImageBitmap + +@Composable +fun WriteCharacterScreen( + characterId: Long, + onClickCancel: () -> Unit, + onClickConfirm: () -> Unit +) { + val application = LocalContext.current.applicationContext as VBHelper + val database = application.container.db + val scanRepository = ScanRepository(database) + val cardDetails by scanRepository.getCardDetails(characterId).collectAsState(Card( + id = 0, + cardId = 0, + name = "", + logo = byteArrayOf(), + logoHeight = 0, + logoWidth = 0, + stageCount = 0, + isBEm = false + )) + + Scaffold( + topBar = { + TopBanner( + text = "Writing character", + onBackClick = onClickCancel + ) + } + ) { innerPadding -> + Column( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center, + modifier = Modifier + .padding(innerPadding) + .fillMaxSize() + ) { + Card ( + modifier = Modifier + .fillMaxWidth() + .padding(8.dp) + ) { + Row ( + modifier = Modifier.padding(16.dp), + ){ + if (cardDetails.logoHeight > 0 && cardDetails.logoWidth > 0) { + val charaBitmapData = BitmapData( + bitmap = cardDetails.logo, + width = cardDetails.logoWidth, + height = cardDetails.logoHeight + ) + val charaImageBitmapData = charaBitmapData.getImageBitmap( + context = LocalContext.current, + multiplier = 4, + obscure = false + ) + + 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), + filterQuality = FilterQuality.None + ) + } + } + + Spacer( + modifier = Modifier.width(8.dp) + ) + + Column { + Text("Card installed successfully!!") + Text("Wait until your device is ready, then tap 'Confirm'") + } + } + + } + + Spacer(modifier = Modifier.height(8.dp)) + + Button( + onClick = onClickConfirm, + ) { + Text("Confirm") + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/screens/WritingScreen.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/screens/WritingScreen.kt new file mode 100644 index 0000000..9680f41 --- /dev/null +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/screens/WritingScreen.kt @@ -0,0 +1,140 @@ +package com.github.nacabaro.vbhelper.screens.scanScreen.screens + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.platform.LocalContext +import com.github.cfogrady.vbnfc.data.NfcCharacter +import com.github.nacabaro.vbhelper.ActivityLifecycleListener +import com.github.nacabaro.vbhelper.di.VBHelper +import com.github.nacabaro.vbhelper.screens.scanScreen.SCAN_SCREEN_ACTIVITY_LIFECYCLE_LISTENER +import com.github.nacabaro.vbhelper.screens.scanScreen.ScanScreenController +import com.github.nacabaro.vbhelper.source.StorageRepository +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext + +@Composable +fun WritingScreen( + scanScreenController: ScanScreenController, + characterId: Long, + nfcCharacter: NfcCharacter, + onComplete: () -> Unit, + onCancel: () -> Unit, +) { + val secrets by scanScreenController.secretsFlow.collectAsState(null) + + val application = LocalContext.current.applicationContext as VBHelper + val storageRepository = StorageRepository(application.container.db) + + var writing by remember { mutableStateOf(false) } + var writingScreen by remember { mutableStateOf(false) } + var writingConfirmScreen by remember { mutableStateOf(false) } + var isDoneSendingCard by remember { mutableStateOf(false) } + var isDoneWritingCharacter by remember { mutableStateOf(false) } + + DisposableEffect(writing) { + if (writing) { + scanScreenController.registerActivityLifecycleListener( + SCAN_SCREEN_ACTIVITY_LIFECYCLE_LISTENER, + object : ActivityLifecycleListener { + override fun onPause() { + scanScreenController.cancelRead() + } + + override fun onResume() { + if (!isDoneSendingCard) { + scanScreenController.onClickCheckCard(secrets!!, nfcCharacter) { + isDoneSendingCard = true + } + } else if (!isDoneWritingCharacter) { + scanScreenController.onClickWrite(secrets!!, nfcCharacter) { + isDoneWritingCharacter = true + } + } + } + } + ) + + if (secrets != null) { + if (!isDoneSendingCard) { + scanScreenController.onClickCheckCard(secrets!!, nfcCharacter) { + isDoneSendingCard = true + } + } else if (!isDoneWritingCharacter) { + scanScreenController.onClickWrite(secrets!!, nfcCharacter) { + isDoneWritingCharacter = true + } + } + } + } + + onDispose { + if (writing) { + scanScreenController.unregisterActivityLifecycleListener( + SCAN_SCREEN_ACTIVITY_LIFECYCLE_LISTENER + ) + scanScreenController.cancelRead() + } + } + } + + if (!writingScreen) { + writing = false + WriteCardScreen ( + characterId = characterId, + onClickCancel = { + scanScreenController.cancelRead() + onCancel() + }, + onClickConfirm = { + writingScreen = true + } + ) + } else if (!isDoneSendingCard) { + writing = true + ActionScreen("Sending card") { + scanScreenController.cancelRead() + onCancel() + } + } else if (!writingConfirmScreen) { + writing = false + WriteCharacterScreen ( + characterId = characterId, + onClickCancel = { + scanScreenController.cancelRead() + onCancel() + }, + onClickConfirm = { + writingConfirmScreen = true + } + ) + } else if (!isDoneWritingCharacter) { + writing = true + ActionScreen("Writing character") { + isDoneSendingCard = false + scanScreenController.cancelRead() + onCancel() + } + } + + var completedWriting by remember { mutableStateOf(false) } + + LaunchedEffect(isDoneSendingCard, isDoneWritingCharacter) { + withContext(Dispatchers.IO) { + if (isDoneSendingCard && isDoneWritingCharacter) { + storageRepository + .deleteCharacter(characterId) + completedWriting = true + } + } + } + + if (completedWriting) { + onComplete() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/storageScreen/StorageDialog.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/storageScreen/StorageDialog.kt index 9b0b1db..eebb1e6 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/storageScreen/StorageDialog.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/storageScreen/StorageDialog.kt @@ -38,6 +38,7 @@ import kotlinx.coroutines.launch fun StorageDialog( characterId: Long, onDismissRequest: () -> Unit, + onClickDelete: () -> Unit, onSendToBracelet: () -> Unit, onClickSetActive: () -> Unit, onClickSendToAdventure: (time: Long) -> Unit @@ -141,6 +142,13 @@ fun StorageDialog( ) { Text(text = "Send on adventure") } + Button( + modifier = Modifier + .fillMaxWidth(), + onClick = onClickDelete + ) { + Text(text = "Delete character") + } Button( modifier = Modifier .fillMaxWidth(), diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/storageScreen/StorageScreen.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/storageScreen/StorageScreen.kt index b5736bb..b57a542 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/storageScreen/StorageScreen.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/storageScreen/StorageScreen.kt @@ -1,5 +1,6 @@ package com.github.nacabaro.vbhelper.screens.storageScreen +import android.util.Log import android.widget.Toast import androidx.compose.foundation.gestures.Orientation import androidx.compose.foundation.gestures.scrollable @@ -14,11 +15,10 @@ import androidx.compose.foundation.rememberScrollState import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -28,12 +28,10 @@ import androidx.navigation.NavController 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.navigation.NavigationItems import com.github.nacabaro.vbhelper.screens.adventureScreen.AdventureScreenControllerImpl import com.github.nacabaro.vbhelper.source.StorageRepository import com.github.nacabaro.vbhelper.utils.BitmapData -import kotlinx.coroutines.launch @Composable @@ -42,18 +40,11 @@ fun StorageScreen( storageScreenController: StorageScreenControllerImpl, adventureScreenController: AdventureScreenControllerImpl ) { - val coroutineScope = rememberCoroutineScope() val application = LocalContext.current.applicationContext as VBHelper val storageRepository = StorageRepository(application.container.db) - val monList = remember { mutableStateOf>(emptyList()) } - var selectedCharacter by remember { mutableStateOf(null) } + val characterList by storageRepository.getAllCharacters().collectAsState(initial = emptyList()) - LaunchedEffect(storageRepository, selectedCharacter) { - coroutineScope.launch { - val characterList = storageRepository.getAllCharacters() - monList.value = characterList - } - } + var selectedCharacter by remember { mutableStateOf(null) } Scaffold ( topBar = { @@ -65,7 +56,7 @@ fun StorageScreen( ) } ) { contentPadding -> - if (monList.value.isEmpty()) { + if (characterList.isEmpty()) { Column ( horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center, @@ -86,7 +77,7 @@ fun StorageScreen( .scrollable(state = rememberScrollState(), orientation = Orientation.Vertical) .padding(top = contentPadding.calculateTopPadding()) ) { - items(monList.value) { index -> + items(characterList) { index -> CharacterEntry( icon = BitmapData( bitmap = index.spriteIdle, @@ -138,6 +129,16 @@ fun StorageScreen( timeInMinutes = time ) selectedCharacter = null + }, + onClickDelete = { + storageScreenController + .deleteCharacter( + characterId = selectedCharacter!!, + onCompletion = { + Log.d("StorageScreen", "Character deleted") + } + ) + selectedCharacter = null } ) } diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/storageScreen/StorageScreenController.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/storageScreen/StorageScreenController.kt index b958c18..a37cbb0 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/storageScreen/StorageScreenController.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/storageScreen/StorageScreenController.kt @@ -2,4 +2,5 @@ package com.github.nacabaro.vbhelper.screens.storageScreen interface StorageScreenController { fun setActive(characterId: Long, onCompletion: () -> Unit) + fun deleteCharacter(characterId: Long, onCompletion: () -> Unit) } \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/storageScreen/StorageScreenControllerImpl.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/storageScreen/StorageScreenControllerImpl.kt index 4473d4d..3eaf31d 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/storageScreen/StorageScreenControllerImpl.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/storageScreen/StorageScreenControllerImpl.kt @@ -28,4 +28,21 @@ class StorageScreenControllerImpl( } } } + + override fun deleteCharacter(characterId: Long, onCompletion: () -> Unit) { + componentActivity.lifecycleScope.launch(Dispatchers.IO) { + database + .userCharacterDao() + .deleteCharacterById(characterId) + + componentActivity.runOnUiThread { + Toast.makeText( + componentActivity, + "Character deleted!", + Toast.LENGTH_SHORT + ).show() + onCompletion() + } + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/source/ScanRepository.kt b/app/src/main/java/com/github/nacabaro/vbhelper/source/ScanRepository.kt new file mode 100644 index 0000000..4bc05f4 --- /dev/null +++ b/app/src/main/java/com/github/nacabaro/vbhelper/source/ScanRepository.kt @@ -0,0 +1,13 @@ +package com.github.nacabaro.vbhelper.source + +import com.github.nacabaro.vbhelper.database.AppDatabase +import com.github.nacabaro.vbhelper.domain.card.Card +import kotlinx.coroutines.flow.Flow + +class ScanRepository( + val database: AppDatabase +) { + fun getCardDetails(characterId: Long): Flow { + return database.cardDao().getCardByCharacterId(characterId) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/source/StorageRepository.kt b/app/src/main/java/com/github/nacabaro/vbhelper/source/StorageRepository.kt index 61aff4a..2ad6da8 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/source/StorageRepository.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/source/StorageRepository.kt @@ -6,11 +6,12 @@ import com.github.nacabaro.vbhelper.domain.device_data.SpecialMissions import com.github.nacabaro.vbhelper.domain.device_data.VBCharacterData import com.github.nacabaro.vbhelper.dtos.CharacterDtos import com.github.nacabaro.vbhelper.dtos.ItemDtos +import kotlinx.coroutines.flow.Flow class StorageRepository ( private val db: AppDatabase ) { - suspend fun getAllCharacters(): List { + fun getAllCharacters(): Flow> { return db.userCharacterDao().getAllCharacters() } From 6be167bbed593c40188d4a32b6592cdf95554137 Mon Sep 17 00:00:00 2001 From: Nacho Date: Sun, 16 Nov 2025 21:13:28 +0100 Subject: [PATCH 7/8] Might be incomplete uhhh --- .../vbhelper/components/CharacterEntry.kt | 7 ++++++- .../nacabaro/vbhelper/daos/AdventureDao.kt | 3 ++- .../nacabaro/vbhelper/daos/UserCharacterDao.kt | 15 ++++++++++----- .../nacabaro/vbhelper/dtos/CharacterDtos.kt | 3 ++- .../screens/adventureScreen/AdventureScreen.kt | 18 +++++------------- .../homeScreens/HomeScreenControllerImpl.kt | 1 + .../screens/settingsScreen/SettingsScreen.kt | 14 +++++++++++--- .../screens/storageScreen/StorageScreen.kt | 3 +++ .../vbhelper/source/StorageRepository.kt | 2 +- 9 files changed, 41 insertions(+), 25 deletions(-) diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/components/CharacterEntry.kt b/app/src/main/java/com/github/nacabaro/vbhelper/components/CharacterEntry.kt index a6ffa91..9b929d5 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/components/CharacterEntry.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/components/CharacterEntry.kt @@ -12,6 +12,7 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.material3.Card +import androidx.compose.material3.CardColors import androidx.compose.material3.CardDefaults import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme @@ -44,6 +45,9 @@ fun CharacterEntry( disabled: Boolean = false, shape: Shape = MaterialTheme.shapes.medium, multiplier: Int = 4, + cardColors: CardColors = CardDefaults.cardColors( + containerColor = MaterialTheme.colorScheme.surfaceContainerHighest + ), onClick: () -> Unit = { } ) { val bitmap = remember (icon.bitmap) { @@ -61,7 +65,8 @@ fun CharacterEntry( }, modifier = modifier .aspectRatio(1f) - .padding(8.dp) + .padding(8.dp), + colors = cardColors ) { Box( contentAlignment = Alignment.BottomCenter, diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/daos/AdventureDao.kt b/app/src/main/java/com/github/nacabaro/vbhelper/daos/AdventureDao.kt index a1db8f2..7167b97 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/daos/AdventureDao.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/daos/AdventureDao.kt @@ -3,6 +3,7 @@ package com.github.nacabaro.vbhelper.daos import androidx.room.Dao import androidx.room.Query import com.github.nacabaro.vbhelper.dtos.CharacterDtos +import kotlinx.coroutines.flow.Flow @Dao @@ -37,7 +38,7 @@ interface AdventureDao { JOIN Adventure a ON uc.id = a.characterId """ ) - suspend fun getAdventureCharacters(): List + fun getAdventureCharacters(): Flow> @Query(""" DELETE FROM Adventure diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/daos/UserCharacterDao.kt b/app/src/main/java/com/github/nacabaro/vbhelper/daos/UserCharacterDao.kt index 02f9293..1bc97fc 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/daos/UserCharacterDao.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/daos/UserCharacterDao.kt @@ -69,7 +69,8 @@ interface UserCharacterDao { c.nameWidth as nameSpriteWidth, c.nameHeight as nameSpriteHeight, d.isBEm as isBemCard, - a.characterId = uc.id as isInAdventure + a.characterId = uc.id as isInAdventure, + uc.isActive as active FROM UserCharacter uc JOIN CardCharacter c ON uc.charId = c.id JOIN Card d ON d.id = c.cardId @@ -93,7 +94,8 @@ interface UserCharacterDao { c.nameWidth as nameSpriteWidth, c.nameHeight as nameSpriteHeight, d.isBEm as isBemCard, - a.characterId = uc.id as isInAdventure + a.characterId = uc.id as isInAdventure, + uc.isActive as active FROM UserCharacter uc JOIN CardCharacter c ON uc.charId = c.id JOIN Card d ON c.cardId = d.id @@ -130,7 +132,8 @@ interface UserCharacterDao { c.nameWidth as nameSpriteWidth, c.nameHeight as nameSpriteHeight, d.isBEm as isBemCard, - a.characterId as isInAdventure + a.characterId as isInAdventure, + uc.isActive as active FROM UserCharacter uc JOIN CardCharacter c ON uc.charId = c.id JOIN Card d ON c.cardId = d.id @@ -194,7 +197,8 @@ interface UserCharacterDao { c.nameWidth as nameSpriteWidth, c.nameHeight as nameSpriteHeight, d.isBEm as isBemCard, - a.characterId = uc.id as isInAdventure + a.characterId = uc.id as isInAdventure, + uc.isActive as active FROM UserCharacter uc JOIN CardCharacter c ON uc.charId = c.id JOIN Card d ON d.id = c.cardId @@ -219,7 +223,8 @@ interface UserCharacterDao { c.nameWidth as nameSpriteWidth, c.nameHeight as nameSpriteHeight, d.isBEm as isBemCard, - a.characterId = uc.id as isInAdventure + a.characterId = uc.id as isInAdventure, + uc.isActive as active FROM UserCharacter uc JOIN CardCharacter c ON uc.charId = c.id JOIN Card d ON d.id = c.cardId diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/dtos/CharacterDtos.kt b/app/src/main/java/com/github/nacabaro/vbhelper/dtos/CharacterDtos.kt index 0837dbb..3cb7831 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/dtos/CharacterDtos.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/dtos/CharacterDtos.kt @@ -31,7 +31,8 @@ object CharacterDtos { val nameSpriteWidth: Int, val nameSpriteHeight: Int, val isBemCard: Boolean, - val isInAdventure: Boolean + val isInAdventure: Boolean, + val active: Boolean ) data class CardCharacterInfo( diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/adventureScreen/AdventureScreen.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/adventureScreen/AdventureScreen.kt index 98f67cf..6540999 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/adventureScreen/AdventureScreen.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/adventureScreen/AdventureScreen.kt @@ -10,6 +10,7 @@ import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.produceState @@ -37,13 +38,11 @@ fun AdventureScreen( navController: NavController, storageScreenController: AdventureScreenControllerImpl ) { - val coroutineScope = rememberCoroutineScope() val application = LocalContext.current.applicationContext as VBHelper val database = application.container.db val storageRepository = StorageRepository(database) - val characterList = remember { - mutableStateOf>(emptyList()) - } + val characterList by storageRepository.getAdventureCharacters().collectAsState(emptyList()) + var obtainedItem by remember { mutableStateOf(null) } @@ -59,13 +58,6 @@ fun AdventureScreen( mutableStateOf(null) } - LaunchedEffect(storageRepository) { - coroutineScope.launch { - characterList.value = storageRepository - .getAdventureCharacters() - } - } - Scaffold( topBar = { TopBanner( @@ -76,7 +68,7 @@ fun AdventureScreen( ) } ) { contentPadding -> - if (characterList.value.isEmpty()) { + if (characterList.isEmpty()) { Column( verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally, @@ -91,7 +83,7 @@ fun AdventureScreen( modifier = Modifier .padding(top = contentPadding.calculateTopPadding()) ) { - items(characterList.value) { + items(characterList) { AdventureEntry( icon = BitmapData( bitmap = it.spriteIdle, diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/HomeScreenControllerImpl.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/HomeScreenControllerImpl.kt index ded8422..01856ca 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/HomeScreenControllerImpl.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/HomeScreenControllerImpl.kt @@ -23,6 +23,7 @@ class HomeScreenControllerImpl( val adventureCharacters = database .adventureDao() .getAdventureCharacters() + .first() val finishedAdventureCharacters = adventureCharacters.filter { character -> character.finishesAdventure <= currentTime diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/settingsScreen/SettingsScreen.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/settingsScreen/SettingsScreen.kt index 47ad33f..691e610 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/settingsScreen/SettingsScreen.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/settingsScreen/SettingsScreen.kt @@ -1,5 +1,7 @@ package com.github.nacabaro.vbhelper.screens.settingsScreen +import android.content.Intent +import android.net.Uri import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -13,6 +15,7 @@ import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp @@ -25,6 +28,8 @@ fun SettingsScreen( navController: NavController, settingsScreenController: SettingsScreenControllerImpl, ) { + val context = LocalContext.current + Scaffold ( topBar = { TopBanner( @@ -44,19 +49,22 @@ fun SettingsScreen( .verticalScroll(rememberScrollState()) ) { SettingsSection("NFC Communication") - SettingsEntry(title = "Import APK", description = "Import Secrets From Vital Arean 2.1.0 APK") { + SettingsEntry(title = "Import APK", description = "Import Secrets From Vital Arena 2.1.0 APK") { settingsScreenController.onClickImportApk() } SettingsSection("DiM/BEm management") SettingsEntry(title = "Import card", description = "Import DiM/BEm card file") { settingsScreenController.onClickImportCard() } - SettingsEntry(title = "Rename DiM/BEm", description = "Set card name") { } SettingsSection("About and credits") SettingsEntry(title = "Credits", description = "Credits") { navController.navigate(NavigationItems.Credits.route) } - SettingsEntry(title = "About", description = "About") { } + SettingsEntry(title = "About", description = "About") { + val browserIntent = Intent( + Intent.ACTION_VIEW, Uri.parse("https://github.com/nacabaro/vbhelper/")) + context.startActivity(browserIntent) + } SettingsSection("Data management") SettingsEntry(title = "Export data", description = "Export application database") { settingsScreenController.onClickOpenDirectory() diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/storageScreen/StorageScreen.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/storageScreen/StorageScreen.kt index b57a542..da1c36a 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/storageScreen/StorageScreen.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/storageScreen/StorageScreen.kt @@ -12,6 +12,7 @@ import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.LazyVerticalGrid import androidx.compose.foundation.lazy.grid.items import androidx.compose.foundation.rememberScrollState +import androidx.compose.material3.CardColors import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -98,6 +99,8 @@ fun StorageScreen( ) } }, + cardColors = CardColors + ) } } diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/source/StorageRepository.kt b/app/src/main/java/com/github/nacabaro/vbhelper/source/StorageRepository.kt index 2ad6da8..f74da0d 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/source/StorageRepository.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/source/StorageRepository.kt @@ -47,7 +47,7 @@ class StorageRepository ( return db.userCharacterDao().deleteCharacterById(id) } - suspend fun getAdventureCharacters(): List { + fun getAdventureCharacters(): Flow> { return db.adventureDao().getAdventureCharacters() } From 489e27b038f09fc5e1206ffb9642fc24b56348ce Mon Sep 17 00:00:00 2001 From: nacabaro Date: Sun, 16 Nov 2025 21:59:08 +0000 Subject: [PATCH 8/8] More things - Active characters display different - Adventure missions award credits (same as special missions) - Clicking on about will open the github page - Corrected from arean to arena - Removed rename from the settings (it was not used) --- app/build.gradle.kts | 2 +- .../screens/adventureScreen/AdventureScreen.kt | 10 ++++++---- .../adventureScreen/AdventureScreenController.kt | 2 +- .../AdventureScreenControllerImpl.kt | 14 ++++++++++++-- .../vbhelper/screens/homeScreens/HomeScreen.kt | 6 +++++- .../screens/homeScreens/HomeScreenController.kt | 2 +- .../homeScreens/HomeScreenControllerImpl.kt | 10 +++++++--- .../screens/homeScreens/screens/VBDiMHomeScreen.kt | 2 +- .../screens/itemsScreen/ObtainedItemDialog.kt | 12 +++++++++++- .../screens/storageScreen/StorageScreen.kt | 13 +++++++++++-- 10 files changed, 56 insertions(+), 17 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 4532721..7e4a241 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -15,7 +15,7 @@ android { minSdk = 28 targetSdk = 35 versionCode = 1 - versionName = "Alpha 0.6.1" + versionName = "Alpha 0.6.2" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/adventureScreen/AdventureScreen.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/adventureScreen/AdventureScreen.kt index 6540999..d7511ae 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/adventureScreen/AdventureScreen.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/adventureScreen/AdventureScreen.kt @@ -9,13 +9,11 @@ import androidx.compose.foundation.lazy.items import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.produceState import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -30,7 +28,6 @@ import com.github.nacabaro.vbhelper.navigation.NavigationItems import com.github.nacabaro.vbhelper.source.StorageRepository import com.github.nacabaro.vbhelper.utils.BitmapData import kotlinx.coroutines.delay -import kotlinx.coroutines.launch import java.time.Instant @Composable @@ -46,6 +43,9 @@ fun AdventureScreen( var obtainedItem by remember { mutableStateOf(null) } + var obtainedCurrency by remember { + mutableStateOf(0) + } val currentTime by produceState(initialValue = Instant.now().epochSecond) { while (true) { @@ -94,8 +94,9 @@ fun AdventureScreen( onClick = { if (it.finishesAdventure < currentTime) { storageScreenController - .getItemFromAdventure(it.id) { adventureResult -> + .getItemFromAdventure(it.id) { adventureResult, generatedCurrency -> obtainedItem = adventureResult + obtainedCurrency = generatedCurrency } } else { cancelAdventureDialog = it @@ -110,6 +111,7 @@ fun AdventureScreen( if (obtainedItem != null) { ObtainedItemDialog( obtainedItem = obtainedItem!!, + obtainedCurrency = obtainedCurrency, onClickDismiss = { obtainedItem = null } diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/adventureScreen/AdventureScreenController.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/adventureScreen/AdventureScreenController.kt index 11d70f6..ed29746 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/adventureScreen/AdventureScreenController.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/adventureScreen/AdventureScreenController.kt @@ -4,6 +4,6 @@ import com.github.nacabaro.vbhelper.dtos.ItemDtos interface AdventureScreenController { fun sendCharacterToAdventure(characterId: Long, timeInMinutes: Long) - fun getItemFromAdventure(characterId: Long, onResult: (ItemDtos.PurchasedItem) -> Unit) + fun getItemFromAdventure(characterId: Long, onResult: (ItemDtos.PurchasedItem, Int) -> Unit) fun cancelAdventure(characterId: Long, onResult: () -> Unit) } \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/adventureScreen/AdventureScreenControllerImpl.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/adventureScreen/AdventureScreenControllerImpl.kt index 50c9605..1e9bb05 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/adventureScreen/AdventureScreenControllerImpl.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/adventureScreen/AdventureScreenControllerImpl.kt @@ -38,19 +38,29 @@ class AdventureScreenControllerImpl( override fun getItemFromAdventure( characterId: Long, - onResult: (ItemDtos.PurchasedItem) -> Unit + onResult: (ItemDtos.PurchasedItem, Int) -> Unit ) { componentActivity.lifecycleScope.launch(Dispatchers.IO) { database .adventureDao() .deleteAdventure(characterId) + val generatedCurrency = generateRandomCurrency() + val generatedItem = generateItem(characterId) - onResult(generatedItem) + onResult(generatedItem, generatedCurrency) } } + private suspend fun generateRandomCurrency(): Int { + val currentValue = application.container.currencyRepository.currencyValue.first() + val random = (2..6).random() * 1000 + application.container.currencyRepository.setCurrencyValue(currentValue + random) + + return random + } + override fun cancelAdventure(characterId: Long, onResult: () -> Unit) { componentActivity.lifecycleScope.launch(Dispatchers.IO) { database diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/HomeScreen.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/HomeScreen.kt index 53dd40f..7cd4515 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/HomeScreen.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/HomeScreen.kt @@ -55,6 +55,7 @@ fun HomeScreen( var adventureMissionsFinished by rememberSaveable { mutableStateOf(false) } var betaWarning by rememberSaveable { mutableStateOf(true) } var collectedItem by remember { mutableStateOf(null) } + var collectedCurrency by remember { mutableStateOf(null) } LaunchedEffect(storageRepository, activeMon, collectedItem) { withContext(Dispatchers.IO) { @@ -123,8 +124,9 @@ fun HomeScreen( contentPadding = contentPadding, specialMissions = vbSpecialMissions.value, homeScreenController = homeScreenController, - onClickCollect = { item -> + onClickCollect = { item, currency -> collectedItem = item + collectedCurrency = currency } ) } @@ -134,8 +136,10 @@ fun HomeScreen( if (collectedItem != null) { ObtainedItemDialog( obtainedItem = collectedItem!!, + obtainedCurrency = collectedCurrency!!, onClickDismiss = { collectedItem = null + collectedCurrency = null } ) } diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/HomeScreenController.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/HomeScreenController.kt index b24f8b2..1c0093b 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/HomeScreenController.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/HomeScreenController.kt @@ -5,5 +5,5 @@ import com.github.nacabaro.vbhelper.dtos.ItemDtos interface HomeScreenController { fun didAdventureMissionsFinish(onCompletion: (Boolean) -> Unit) - fun clearSpecialMission(missionId: Long, missionCompletion: SpecialMission.Status, onCleared: (ItemDtos.PurchasedItem?) -> Unit) + fun clearSpecialMission(missionId: Long, missionCompletion: SpecialMission.Status, onCleared: (ItemDtos.PurchasedItem?, Int?) -> Unit) } \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/HomeScreenControllerImpl.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/HomeScreenControllerImpl.kt index 01856ca..e8824e8 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/HomeScreenControllerImpl.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/HomeScreenControllerImpl.kt @@ -33,7 +33,7 @@ class HomeScreenControllerImpl( } } - override fun clearSpecialMission(missionId: Long, missionCompletion: SpecialMission.Status, onCleared: (ItemDtos.PurchasedItem?) -> Unit) { + override fun clearSpecialMission(missionId: Long, missionCompletion: SpecialMission.Status, onCleared: (ItemDtos.PurchasedItem?, Int?) -> Unit) { componentActivity.lifecycleScope.launch { database .specialMissionDao() @@ -65,9 +65,13 @@ class HomeScreenControllerImpl( itemType = randomItem.itemType ) - onCleared(purchasedItem) + val randomAmount = (2..6).random() * 1000 + val currentCurrency = application.container.currencyRepository.currencyValue.first() + application.container.currencyRepository.setCurrencyValue(currentCurrency + randomAmount) + + onCleared(purchasedItem, randomAmount) } else { - onCleared(null) + onCleared(null, null) } } diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/screens/VBDiMHomeScreen.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/screens/VBDiMHomeScreen.kt index 74dd98d..eeb9062 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/screens/VBDiMHomeScreen.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/screens/VBDiMHomeScreen.kt @@ -34,7 +34,7 @@ fun VBDiMHomeScreen( homeScreenController: HomeScreenControllerImpl, transformationHistory: List, contentPadding: PaddingValues, - onClickCollect: (ItemDtos.PurchasedItem?) -> Unit + onClickCollect: (ItemDtos.PurchasedItem?, Int?) -> Unit ) { Column( modifier = Modifier diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/itemsScreen/ObtainedItemDialog.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/itemsScreen/ObtainedItemDialog.kt index c740018..9fbe416 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/itemsScreen/ObtainedItemDialog.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/itemsScreen/ObtainedItemDialog.kt @@ -23,6 +23,7 @@ import com.github.nacabaro.vbhelper.dtos.ItemDtos @Composable fun ObtainedItemDialog( obtainedItem: ItemDtos.PurchasedItem, + obtainedCurrency: Int, onClickDismiss: () -> Unit ) { Dialog( @@ -84,7 +85,16 @@ fun ObtainedItemDialog( text = "You have obtained ${obtainedItem.itemAmount} of this item", modifier = Modifier .fillMaxWidth() - .padding(5.dp) + .padding(top = 4.dp) + ) + Text( + textAlign = TextAlign.Center, + fontSize = MaterialTheme.typography.bodySmall.fontSize, + fontFamily = MaterialTheme.typography.bodySmall.fontFamily, + text = "You also got $obtainedCurrency credits", + modifier = Modifier + .fillMaxWidth() + .padding(bottom = 4.dp) ) Button( onClick = onClickDismiss, diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/storageScreen/StorageScreen.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/storageScreen/StorageScreen.kt index da1c36a..9a7bde8 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/storageScreen/StorageScreen.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/storageScreen/StorageScreen.kt @@ -12,7 +12,8 @@ import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.LazyVerticalGrid import androidx.compose.foundation.lazy.grid.items import androidx.compose.foundation.rememberScrollState -import androidx.compose.material3.CardColors +import androidx.compose.material3.CardDefaults +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -99,7 +100,15 @@ fun StorageScreen( ) } }, - cardColors = CardColors + cardColors = if (index.active) { + CardDefaults.cardColors( + containerColor = MaterialTheme.colorScheme.primary + ) + } else { + CardDefaults.cardColors( + containerColor = MaterialTheme.colorScheme.surfaceContainerHighest + ) + } ) }