Compare commits

...
Author SHA1 Message Date
Nacho fd76c46f39 Not much
- Imported card backgrounds (not necessary but future proofing just in case)
- Readded the sprite viewer just to verify everything is working as intended, will probably get rid of it again? or maybe I should keep it since it holds a copy of all the sprites in the card...
- TODO: Remove old unused tables
2025-09-14 02:39:52 +02:00
Nacho e85e1f45bf More name changes 2025-09-14 01:09:33 +02:00
Nacho 630cccefe4 Increased AGP version and changed some terminology 2025-09-14 00:49:17 +02:00
Nacho 7160eb792a Adventure Missions progress
- Viewing the adventure missions completed in a card is now possible.
2025-09-10 02:09:15 +02:00
Nacho cb9fe8e716 Merge remote-tracking branch 'origin/main' 2025-09-05 02:22:09 +02:00
Nacho 540766eefb VERSION UP 2025-09-05 02:21:47 +02:00
nacabaroandGitHub af2c852bac Merge pull request #38 from nacabaro/vb/special_missions
Small fix related to special missions
2025-09-05 02:19:36 +02:00
Nacho 8dc5bbbdde Small fix related to special missions
- Now you can clear failed special missions. When a special mission fails, you will not get an item after clearing it.

- Huge note: I have not tested this fix, but it should work... At least that's what my intuition is telling me!

Jokes aside, this should work, but the home screen will not update. I should update this to make use of StateFlows and Flows to keep the home screen updated at all times.
2025-09-05 02:19:09 +02:00
nacabaroandGitHub aac05a3b1c Merge pull request #37 from nacabaro/card/import_more
Agarrate, que vienen curvas
2025-09-05 02:10:05 +02:00
Nacho b827fdccbe Agarrate, que vienen curvas
- Importing more data from the card, this time it's to enhance the functionality of the dex. Things that I'm importing:
  - Tropies
  - Vitals
  - WinRate
  - Battles needed
  - Adventure level cleared
- This commit also includes displaying the data, modifying the relational model and modifying the underlying import functions.

- I've also changed the name from Character to CharacterData, since there is another class that is also called Character. I wanted to move it into the card package, since it's more related to Cards, while character is more designed for user raised characters.

- Another small addition was the ability to construct ImageBitmaps from the BitmapData.kt class. reduces code and makes it more simple, will refactor the old code to use this later on.

- Things I might do
  - Move out of the SettingsScreenControllerImpl.kt file all the card import stuff, since it's slowly growing a lot, and it could benefit from some independence
2025-09-05 02:08:44 +02:00
Nacho 2586190e5c Update libraries 2025-09-02 18:01:11 +02:00
Nacho 1a6753d2aa Fixed date issues with TransformationHistory 2025-09-02 04:54:08 +02:00
Nacho f8ec899335 Version up and credits 2025-08-09 12:59:01 +02:00
Nacho b95edfa2a1 New icon 2025-08-09 12:55:38 +02:00
Nacho 1a570170ca Merge remote-tracking branch 'origin/main' 2025-08-06 20:18:43 +02:00
Nacho 1566876106 Version up 2025-08-06 20:18:32 +02:00
nacabaroandGitHub 019f07d827 Merge pull request #36 from nacabaro/card/card_management
Few things here and there
2025-08-06 20:13:09 +02:00
Nacho 4474e6689d Few things here and there
- Card management, you can now modify the name of the cards and remove cards too.
- Also, support for multiple cards with the same ID works too. When scanning for the first time, if a repeat card exists, the user will be asked to choose which card the character comes from. Future scans will not ask since it is stored in the watch app reserved area.
2025-08-06 20:10:21 +02:00
Nacho fb09004d13 Version up, again 2025-08-06 01:32:10 +02:00
Nacho dd1a62d184 Made a mistake
Accidentally locked BE watch items to only BEm characters. I have modified it to also include DiM characters in the BE.
2025-08-06 01:31:18 +02:00
Nacho f96f00e05a Version up
And change the original pop up to mention this works with VB, my bad
2025-08-06 01:22:43 +02:00
nacabaroandGitHub 3c17e93d0f Merge pull request #35 from nacabaro/vb/special_missions
Special missions
2025-08-06 01:21:00 +02:00
Nacho f8ce81e932 Special missions
- Basic implementation is complete
- Added 8 different special missions, 4 are easy and more expensive, and 4 are cheaper and more difficult
- Added 9 missions of each so people can test themselves
- Also added checks to disallow BE digimon to have special missions
- UI elements to display the status of the missions
- Finishing a mission awards a random price (TODO: Make the price be based on the mission difficulty)
2025-08-06 01:19:19 +02:00
Nacho 7bb7693876 Version Up 2025-08-04 19:57:39 +02:00
nacabaroandGitHub 2b33042fe0 Merge pull request #34 from nacabaro/vb/nfc_compat
Added basic VB support
2025-08-04 19:56:12 +02:00
Nacho 4d8dcde26e Forgot to add all the UI methods to preview VB characters 2025-08-04 13:15:07 +02:00
Nacho 0b1eed7da9 Increase version 2025-07-29 23:08:45 +02:00
88 changed files with 2618 additions and 465 deletions
+1 -1
View File
@@ -15,7 +15,7 @@ android {
minSdk = 28 minSdk = 28
targetSdk = 35 targetSdk = 35
versionCode = 1 versionCode = 1
versionName = "Alpha 0.1" versionName = "Alpha 0.6.1"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
} }
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

@@ -14,6 +14,7 @@ import com.github.nacabaro.vbhelper.screens.itemsScreen.ItemsScreenControllerImp
import com.github.nacabaro.vbhelper.screens.scanScreen.ScanScreenControllerImpl import com.github.nacabaro.vbhelper.screens.scanScreen.ScanScreenControllerImpl
import com.github.nacabaro.vbhelper.screens.settingsScreen.SettingsScreenControllerImpl import com.github.nacabaro.vbhelper.screens.settingsScreen.SettingsScreenControllerImpl
import com.github.nacabaro.vbhelper.screens.adventureScreen.AdventureScreenControllerImpl import com.github.nacabaro.vbhelper.screens.adventureScreen.AdventureScreenControllerImpl
import com.github.nacabaro.vbhelper.screens.cardScreen.CardScreenControllerImpl
import com.github.nacabaro.vbhelper.screens.spriteViewer.SpriteViewerControllerImpl import com.github.nacabaro.vbhelper.screens.spriteViewer.SpriteViewerControllerImpl
import com.github.nacabaro.vbhelper.screens.storageScreen.StorageScreenControllerImpl import com.github.nacabaro.vbhelper.screens.storageScreen.StorageScreenControllerImpl
import com.github.nacabaro.vbhelper.ui.theme.VBHelperTheme import com.github.nacabaro.vbhelper.ui.theme.VBHelperTheme
@@ -47,6 +48,7 @@ class MainActivity : ComponentActivity() {
val storageScreenController = StorageScreenControllerImpl(this) val storageScreenController = StorageScreenControllerImpl(this)
val homeScreenController = HomeScreenControllerImpl(this) val homeScreenController = HomeScreenControllerImpl(this)
val spriteViewerController = SpriteViewerControllerImpl(this) val spriteViewerController = SpriteViewerControllerImpl(this)
val cardScreenController = CardScreenControllerImpl(this)
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@@ -61,7 +63,8 @@ class MainActivity : ComponentActivity() {
adventureScreenController = adventureScreenController, adventureScreenController = adventureScreenController,
homeScreenController = homeScreenController, homeScreenController = homeScreenController,
storageScreenController = storageScreenController, storageScreenController = storageScreenController,
spriteViewerController = spriteViewerController spriteViewerController = spriteViewerController,
cardScreenController = cardScreenController
) )
} }
} }
@@ -93,8 +96,9 @@ class MainActivity : ComponentActivity() {
adventureScreenController: AdventureScreenControllerImpl, adventureScreenController: AdventureScreenControllerImpl,
storageScreenController: StorageScreenControllerImpl, storageScreenController: StorageScreenControllerImpl,
homeScreenController: HomeScreenControllerImpl, homeScreenController: HomeScreenControllerImpl,
spriteViewerController: SpriteViewerControllerImpl spriteViewerController: SpriteViewerControllerImpl,
) { cardScreenController: CardScreenControllerImpl
) {
AppNavigation( AppNavigation(
applicationNavigationHandlers = AppNavigationHandlers( applicationNavigationHandlers = AppNavigationHandlers(
settingsScreenController, settingsScreenController,
@@ -103,7 +107,8 @@ class MainActivity : ComponentActivity() {
adventureScreenController, adventureScreenController,
storageScreenController, storageScreenController,
homeScreenController, homeScreenController,
spriteViewerController spriteViewerController,
cardScreenController
) )
) )
} }
@@ -5,11 +5,14 @@ import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.aspectRatio import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.material3.Card import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
@@ -28,6 +31,9 @@ import com.github.nacabaro.vbhelper.utils.getBitmap
import androidx.compose.ui.graphics.Shape import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import com.github.cfogrady.vbnfc.vb.SpecialMission
import com.github.nacabaro.vbhelper.R
import com.github.nacabaro.vbhelper.domain.device_data.SpecialMissions
import com.github.nacabaro.vbhelper.utils.getObscuredBitmap import com.github.nacabaro.vbhelper.utils.getObscuredBitmap
@Composable @Composable
@@ -114,4 +120,90 @@ fun ItemDisplay(
) )
} }
} }
}
@Composable
fun SpecialMissionsEntry(
specialMission: SpecialMissions,
modifier: Modifier = Modifier,
onClickCard: () -> Unit = { },
) {
val textValue = when (specialMission.missionType) {
SpecialMission.Type.NONE -> "No mission selected"
SpecialMission.Type.STEPS -> "Walk ${specialMission.goal} steps"
SpecialMission.Type.BATTLES -> "Battle ${specialMission.goal} times"
SpecialMission.Type.WINS -> "Win ${specialMission.goal} battles"
SpecialMission.Type.VITALS -> "Earn ${specialMission.goal} vitals"
}
val progress = if (specialMission.status == SpecialMission.Status.COMPLETED) {
specialMission.goal
} else {
specialMission.progress
}
val completion = when (specialMission.missionType) {
SpecialMission.Type.NONE -> ""
SpecialMission.Type.STEPS -> "Walked $progress steps"
SpecialMission.Type.BATTLES -> "Battled $progress times"
SpecialMission.Type.WINS -> "Won $progress battles"
SpecialMission.Type.VITALS -> "Earned $progress vitals"
}
val icon = when (specialMission.missionType) {
SpecialMission.Type.NONE -> R.drawable.baseline_free_24
SpecialMission.Type.STEPS -> R.drawable.baseline_agility_24
SpecialMission.Type.BATTLES -> R.drawable.baseline_swords_24
SpecialMission.Type.WINS -> R.drawable.baseline_trophy_24
SpecialMission.Type.VITALS -> R.drawable.baseline_vitals_24
}
val color = when (specialMission.status)
{
SpecialMission.Status.IN_PROGRESS -> MaterialTheme.colorScheme.secondary
SpecialMission.Status.COMPLETED -> MaterialTheme.colorScheme.primary
SpecialMission.Status.FAILED -> MaterialTheme.colorScheme.error
else -> MaterialTheme.colorScheme.surfaceContainerHighest
}
Card(
modifier = modifier,
shape = androidx.compose.material.MaterialTheme.shapes.small,
onClick = if (specialMission.status == SpecialMission.Status.COMPLETED || specialMission.status == SpecialMission.Status.FAILED) {
onClickCard
} else {
{ }
},
colors = CardDefaults.cardColors(
containerColor = color
)
) {
Row (
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp),
modifier = Modifier
.fillMaxSize()
.padding(16.dp)
) {
Icon(
painter = painterResource(icon),
contentDescription = "Vitals",
modifier = Modifier
.fillMaxHeight()
.padding(16.dp)
)
Column {
Text(
text = textValue,
fontFamily = MaterialTheme.typography.titleLarge.fontFamily,
fontWeight = FontWeight.Bold,
)
Text(
text = completion,
fontFamily = MaterialTheme.typography.titleSmall.fontFamily,
)
}
}
}
} }
@@ -24,7 +24,8 @@ fun TopBanner(
onGearClick: (() -> Unit)? = null, onGearClick: (() -> Unit)? = null,
onBackClick: (() -> Unit)? = null, onBackClick: (() -> Unit)? = null,
onScanClick: (() -> Unit)? = null, onScanClick: (() -> Unit)? = null,
onAdventureClick: (() -> Unit)? = null onAdventureClick: (() -> Unit)? = null,
onModifyClick: (() -> Unit)? = null
) { ) {
Box( // Use Box to overlay elements Box( // Use Box to overlay elements
modifier = modifier modifier = modifier
@@ -37,16 +38,16 @@ fun TopBanner(
textAlign = TextAlign.Center, textAlign = TextAlign.Center,
fontSize = 24.sp, fontSize = 24.sp,
modifier = Modifier modifier = Modifier
.align(Alignment.Center) // Center the text .align(Alignment.Center)
) )
if (onGearClick != null) { if (onGearClick != null) {
IconButton( IconButton(
onClick = onGearClick, onClick = onGearClick,
modifier = Modifier modifier = Modifier
.align(Alignment.CenterEnd) // Place gear icon at the end .align(Alignment.CenterEnd)
) { ) {
Icon( Icon(
painter = painterResource(R.drawable.baseline_settings_24), // Use a gear icon painter = painterResource(R.drawable.baseline_settings_24),
contentDescription = "Settings" contentDescription = "Settings"
) )
} }
@@ -54,23 +55,34 @@ fun TopBanner(
IconButton( IconButton(
onClick = onAdventureClick, onClick = onAdventureClick,
modifier = Modifier modifier = Modifier
.align(Alignment.CenterEnd) // Place gear icon at the end .align(Alignment.CenterEnd)
) { ) {
Icon( Icon(
painter = painterResource(R.drawable.baseline_fort_24), // Use a gear icon painter = painterResource(R.drawable.baseline_fort_24),
contentDescription = "Adventure" contentDescription = "Adventure"
) )
} }
} else if (onModifyClick != null) {
IconButton(
onClick = onModifyClick,
modifier = Modifier
.align(Alignment.CenterEnd)
) {
Icon(
painter = painterResource(R.drawable.baseline_edit_24),
contentDescription = "Adventure"
)
}
} }
if (onScanClick != null) { if (onScanClick != null) {
IconButton( IconButton(
onClick = onScanClick, onClick = onScanClick,
modifier = Modifier modifier = Modifier
.align(Alignment.CenterStart) // Place gear icon at the end .align(Alignment.CenterStart)
) { ) {
Icon( Icon(
painter = painterResource(R.drawable.baseline_nfc_24), // Use a gear icon painter = painterResource(R.drawable.baseline_nfc_24),
contentDescription = "Scan" contentDescription = "Scan"
) )
} }
@@ -78,10 +90,10 @@ fun TopBanner(
IconButton( IconButton(
onClick = onBackClick, onClick = onBackClick,
modifier = Modifier modifier = Modifier
.align(Alignment.CenterStart) // Place gear icon at the end .align(Alignment.CenterStart)
) { ) {
Icon( Icon(
painter = painterResource(R.drawable.baseline_arrow_back_24), // Use a gear icon painter = painterResource(R.drawable.baseline_arrow_back_24),
contentDescription = "Settings" contentDescription = "Settings"
) )
} }
@@ -18,7 +18,8 @@ interface AdventureDao {
""") """)
fun getAdventureCount(): Int fun getAdventureCount(): Int
@Query(""" @Query(
"""
SELECT SELECT
uc.*, uc.*,
c.stage, c.stage,
@@ -30,11 +31,12 @@ interface AdventureDao {
a.finishesAdventure AS finishesAdventure, a.finishesAdventure AS finishesAdventure,
a.originalDuration AS originalTimeInMinutes a.originalDuration AS originalTimeInMinutes
FROM UserCharacter uc FROM UserCharacter uc
JOIN Character c ON uc.charId = c.id JOIN CardCharacter c ON uc.charId = c.id
JOIN Sprite s ON s.id = c.spriteId JOIN Sprite s ON s.id = c.spriteId
JOIN Card d ON c.dimId = d.id JOIN Card d ON c.cardId = d.id
JOIN Adventure a ON uc.id = a.characterId JOIN Adventure a ON uc.id = a.characterId
""") """
)
suspend fun getAdventureCharacters(): List<CharacterDtos.AdventureCharacterWithSprites> suspend fun getAdventureCharacters(): List<CharacterDtos.AdventureCharacterWithSprites>
@Query(""" @Query("""
@@ -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<CardDtos.CardAdventureWithSprites>
}
@@ -0,0 +1,24 @@
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.CardBackground
@Dao
interface CardBackgroundDao {
@Insert
suspend fun insertCardBackground(vararg cardBackground: CardBackground)
@Query("""
SELECT * FROM CardBackground
WHERE cardId = :cardId
""")
suspend fun getCardBackground(cardId: Long): CardBackground
@Query("""
SELECT * FROM CardBackground
""")
suspend fun getBackgrounds(): List<CardBackground>
}
@@ -5,12 +5,40 @@ import androidx.room.Insert
import androidx.room.OnConflictStrategy import androidx.room.OnConflictStrategy
import androidx.room.Query import androidx.room.Query
import com.github.nacabaro.vbhelper.domain.card.Card import com.github.nacabaro.vbhelper.domain.card.Card
import com.github.nacabaro.vbhelper.domain.card.CardSprites
@Dao @Dao
interface CardDao { interface CardDao {
@Insert(onConflict = OnConflictStrategy.IGNORE) @Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun insertNewDim(card: Card): Long suspend fun insertNewCard(card: Card): Long
@Query("SELECT * FROM Card WHERE cardId = :id") @Query("SELECT * FROM Card WHERE cardId = :id")
fun getDimById(id: Int): Card? fun getCardByCardId(id: Int): List<Card>
@Query("SELECT * FROM Card WHERE id = :id")
fun getCardById(id: Long): Card?
@Query(
"""
SELECT ca.*
FROM Card ca
JOIN CardCharacter ch ON ca.id = ch.cardId
JOIN UserCharacter uc ON ch.id = uc.charId
WHERE uc.id = :id
"""
)
suspend fun getCardByCharacterId(id: Long): Card
@Query("UPDATE Card SET name = :newName WHERE id = :id")
suspend fun renameCard(id: Int, newName: String)
@Query("DELETE FROM Card WHERE id = :id")
suspend fun deleteCard(id: Long)
@Insert
suspend fun insertSprites(vararg sprites: CardSprites)
@Query("SELECT * FROM CardSprites")
suspend fun getSprites(): List<CardSprites>
} }
@@ -0,0 +1,34 @@
package com.github.nacabaro.vbhelper.daos
import androidx.room.Dao
import androidx.room.Query
@Dao
interface CardFusionsDao {
@Query(
"""
INSERT INTO
CardFusions (
fromCharaId,
toVirusFusion,
toDataFusion,
toVaccineFusion,
toFreeFusion
)
SELECT
(SELECT id FROM CardCharacter WHERE cardId = :cardId AND charaIndex = :fromCharaId),
(SELECT id FROM CardCharacter WHERE cardId = :cardId AND charaIndex = :toCharaIdVirus),
(SELECT id FROM CardCharacter WHERE cardId = :cardId AND charaIndex = :toCharaIdData),
(SELECT id FROM CardCharacter WHERE cardId = :cardId AND charaIndex = :toCharaIdVaccine),
(SELECT id FROM CardCharacter WHERE cardId = :cardId AND charaIndex = :toCharaIdFree)
"""
)
suspend fun insertNewFusion(
cardId: Long,
fromCharaId: Int,
toCharaIdVirus: Int,
toCharaIdData: Int,
toCharaIdVaccine: Int,
toCharaIdFree: Int
)
}
@@ -13,5 +13,5 @@ interface CardProgressDao {
@Query( @Query(
"SELECT currentStage FROM CardProgress WHERE cardId = :cardId" "SELECT currentStage FROM CardProgress WHERE cardId = :cardId"
) )
fun getCardProgress(cardId: Int): Int fun getCardProgress(cardId: Long): Int
} }
@@ -3,17 +3,17 @@ package com.github.nacabaro.vbhelper.daos
import androidx.room.Dao import androidx.room.Dao
import androidx.room.Insert import androidx.room.Insert
import androidx.room.Query import androidx.room.Query
import com.github.nacabaro.vbhelper.domain.characters.Character import com.github.nacabaro.vbhelper.domain.card.CardCharacter
import com.github.nacabaro.vbhelper.domain.characters.Sprite import com.github.nacabaro.vbhelper.domain.characters.Sprite
import com.github.nacabaro.vbhelper.dtos.CharacterDtos import com.github.nacabaro.vbhelper.dtos.CharacterDtos
@Dao @Dao
interface CharacterDao { interface CharacterDao {
@Insert @Insert
suspend fun insertCharacter(vararg characterData: Character) suspend fun insertCharacter(vararg characterData: CardCharacter)
@Query("SELECT * FROM Character WHERE monIndex = :monIndex AND dimId = :dimId LIMIT 1") @Query("SELECT * FROM CardCharacter WHERE charaIndex = :monIndex AND cardId = :dimId LIMIT 1")
fun getCharacterByMonIndex(monIndex: Int, dimId: Long): Character fun getCharacterByMonIndex(monIndex: Int, dimId: Long): CardCharacter
@Insert @Insert
suspend fun insertSprite(vararg sprite: Sprite) suspend fun insertSprite(vararg sprite: Sprite)
@@ -22,14 +22,66 @@ interface CharacterDao {
""" """
SELECT SELECT
d.cardId as cardId, d.cardId as cardId,
c.monIndex as charId, c.charaIndex as charId,
c.stage as stage, c.stage as stage,
c.attribute as attribute c.attribute as attribute
FROM Character c FROM CardCharacter c
JOIN UserCharacter uc ON c.id = uc.charId JOIN UserCharacter uc ON c.id = uc.charId
JOIN Card d ON c.dimId = d.id JOIN Card d ON c.cardId = d.id
WHERE c.id = :charId WHERE c.id = :charId
""" """
) )
suspend fun getCharacterInfo(charId: Long): CharacterDtos.CardCharacterInfo suspend fun getCharacterInfo(charId: Long): CharacterDtos.CardCharacterInfo
@Query(
"""
INSERT INTO CardTransformations (charaId, requiredVitals, requiredTrophies, requiredBattles, requiredWinRate, changeTimerHours, requiredAdventureLevelCompleted, toCharaId)
SELECT
(SELECT id FROM CardCharacter WHERE charaIndex = :fromChraraIndex AND cardId = :cardId),
:requiredVitals,
:requiredTrophies,
:requiredBattles,
:requiredWinRate,
:changeTimerHours,
:requiredAdventureLevelCompleted,
(SELECT id FROM CardCharacter WHERE charaIndex = :toChraraIndex AND cardId = :cardId)
"""
)
suspend fun insertPossibleTransformation(
fromChraraIndex: Int,
toChraraIndex: Int,
cardId: Long,
requiredVitals: Int,
requiredTrophies: Int,
requiredBattles: Int,
changeTimerHours: Int,
requiredWinRate: Int,
requiredAdventureLevelCompleted: Int
)
@Query(
"""
SELECT
pt.charaId as fromCharaId,
pt.toCharaId as charaId,
s.spriteIdle1 as spriteIdle,
s.width as spriteWidth,
s.height as spriteHeight,
d.discoveredOn as discoveredOn,
pt.requiredTrophies as requiredTrophies,
pt.requiredVitals as requiredVitals,
pt.requiredBattles as requiredBattles,
pt.requiredWinRate as requiredWinRate,
pt.changeTimerHours as changeTimerHours,
pt.requiredAdventureLevelCompleted as requiredAdventureLevelCompleted
FROM
CardTransformations pt
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
c.cardId = :cardId
"""
)
suspend fun getEvolutionRequirementsForCard(cardId: Long): List<CharacterDtos.EvolutionRequirementsWithSpritesAndObtained>
} }
@@ -7,39 +7,53 @@ import com.github.nacabaro.vbhelper.dtos.CharacterDtos
@Dao @Dao
interface DexDao { interface DexDao {
@Query(""" @Query(
"""
INSERT OR IGNORE INTO Dex(id, discoveredOn) INSERT OR IGNORE INTO Dex(id, discoveredOn)
VALUES ( VALUES (
(SELECT id FROM Character WHERE monIndex = :charIndex AND dimId = :cardId), (SELECT id FROM CardCharacter WHERE charaIndex = :charIndex AND cardId = :cardId),
:discoveredOn :discoveredOn
) )
""") """
)
fun insertCharacter(charIndex: Int, cardId: Long, discoveredOn: Long) fun insertCharacter(charIndex: Int, cardId: Long, discoveredOn: Long)
@Query(""" @Query(
"""
SELECT SELECT
c.id AS id, c.id AS id,
s.spriteIdle1 AS spriteIdle, s.spriteIdle1 AS spriteIdle,
s.width AS spriteWidth, s.width AS spriteWidth,
s.height AS spriteHeight, s.height AS spriteHeight,
d.discoveredOn AS discoveredOn c.nameSprite AS nameSprite,
FROM Character c c.nameWidth AS nameSpriteWidth,
c.nameHeight AS nameSpriteHeight,
d.discoveredOn AS discoveredOn,
c.baseHp as baseHp,
c.baseBp as baseBp,
c.baseAp as baseAp,
c.stage as stage,
c.attribute as attribute
FROM CardCharacter c
JOIN Sprite s ON c.spriteId = s.id JOIN Sprite s ON c.spriteId = s.id
LEFT JOIN dex d ON c.id = d.id LEFT JOIN dex d ON c.id = d.id
WHERE c.dimId = :cardId WHERE c.cardId = :cardId
""") """
suspend fun getSingleCardProgress(cardId: Long): List<CharacterDtos.CardProgress> )
suspend fun getSingleCardProgress(cardId: Long): List<CharacterDtos.CardCharaProgress>
@Query(""" @Query(
"""
SELECT SELECT
c.id as cardId, c.id as cardId,
c.name as cardName, c.name as cardName,
c.logo as cardLogo, c.logo as cardLogo,
c.logoWidth as logoWidth, c.logoWidth as logoWidth,
c.logoHeight as logoHeight, c.logoHeight as logoHeight,
(SELECT COUNT(*) FROM Character cc WHERE cc.dimId = c.id) AS totalCharacters, (SELECT COUNT(*) FROM CardCharacter cc WHERE cc.cardId = c.id) AS totalCharacters,
(SELECT COUNT(*) FROM Dex d JOIN Character cc ON d.id = cc.id WHERE cc.dimId = c.id AND d.discoveredOn IS NOT NULL) AS obtainedCharacters (SELECT COUNT(*) FROM 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 FROM Card c
""") """
)
suspend fun getCardsWithProgress(): List<CardDtos.CardProgress> suspend fun getCardsWithProgress(): List<CardDtos.CardProgress>
} }
@@ -31,7 +31,7 @@ interface ItemDao {
WHERE Items.id = :itemId WHERE Items.id = :itemId
""" """
) )
fun getItem(itemId: Long): ItemDtos.ItemsWithQuantities suspend fun getItem(itemId: Long): ItemDtos.ItemsWithQuantities
@Query( @Query(
""" """
@@ -40,7 +40,7 @@ interface ItemDao {
WHERE id = :itemId WHERE id = :itemId
""" """
) )
fun useItem(itemId: Long) suspend fun useItem(itemId: Long)
@Query( @Query(
""" """
@@ -0,0 +1,15 @@
package com.github.nacabaro.vbhelper.daos
import androidx.room.Dao
import androidx.room.Query
@Dao
interface SpecialMissionDao {
@Query("""
UPDATE SpecialMissions SET
missionType = "NONE",
status = "UNAVAILABLE"
WHERE id = :id
""")
suspend fun clearSpecialMission(id: Long)
}
@@ -5,7 +5,7 @@ import androidx.room.Insert
import androidx.room.OnConflictStrategy import androidx.room.OnConflictStrategy
import androidx.room.Query import androidx.room.Query
import androidx.room.Upsert import androidx.room.Upsert
import com.github.nacabaro.vbhelper.domain.characters.Character 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.UserCharacter
import com.github.nacabaro.vbhelper.domain.device_data.BECharacterData 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.SpecialMissions
@@ -34,22 +34,24 @@ interface UserCharacterDao {
@Insert(onConflict = OnConflictStrategy.REPLACE) @Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertTransformationHistory(vararg transformationHistory: TransformationHistory) fun insertTransformationHistory(vararg transformationHistory: TransformationHistory)
@Insert @Upsert
fun insertSpecialMissions(vararg specialMissions: SpecialMissions) fun insertSpecialMissions(vararg specialMissions: SpecialMissions)
@Query(""" @Query(
"""
SELECT SELECT
c.id AS id, c.id AS id,
s.spriteIdle1 AS spriteIdle, s.spriteIdle1 AS spriteIdle,
s.width AS spriteWidth, s.width AS spriteWidth,
s.height AS spriteHeight, s.height AS spriteHeight,
c.monIndex AS monIndex, c.charaIndex AS monIndex,
t.transformationDate AS transformationDate t.transformationDate AS transformationDate
FROM TransformationHistory t FROM TransformationHistory t
JOIN Character c ON c.id = t.stageId JOIN CardCharacter c ON c.id = t.stageId
JOIN Sprite s ON s.id = c.spriteId JOIN Sprite s ON s.id = c.spriteId
WHERE monId = :monId WHERE monId = :monId
""") """
)
suspend fun getTransformationHistory(monId: Long): List<CharacterDtos.TransformationHistory>? suspend fun getTransformationHistory(monId: Long): List<CharacterDtos.TransformationHistory>?
@Query( @Query(
@@ -62,14 +64,14 @@ interface UserCharacterDao {
s.spriteIdle2 AS spriteIdle2, s.spriteIdle2 AS spriteIdle2,
s.width AS spriteWidth, s.width AS spriteWidth,
s.height AS spriteHeight, s.height AS spriteHeight,
c.name as nameSprite, c.nameSprite as nameSprite,
c.nameWidth as nameSpriteWidth, c.nameWidth as nameSpriteWidth,
c.nameHeight as nameSpriteHeight, c.nameHeight as nameSpriteHeight,
d.isBEm as isBemCard, d.isBEm as isBemCard,
a.characterId = uc.id as isInAdventure a.characterId = uc.id as isInAdventure
FROM UserCharacter uc FROM UserCharacter uc
JOIN Character c ON uc.charId = c.id JOIN CardCharacter c ON uc.charId = c.id
JOIN Card d ON d.id = c.dimId JOIN Card d ON d.id = c.cardId
JOIN Sprite s ON s.id = c.spriteId JOIN Sprite s ON s.id = c.spriteId
LEFT JOIN Adventure a ON a.characterId = uc.id LEFT JOIN Adventure a ON a.characterId = uc.id
""" """
@@ -86,18 +88,19 @@ interface UserCharacterDao {
s.spriteIdle2 AS spriteIdle2, s.spriteIdle2 AS spriteIdle2,
s.width AS spriteWidth, s.width AS spriteWidth,
s.height AS spriteHeight, s.height AS spriteHeight,
c.name as nameSprite, c.nameSprite as nameSprite,
c.nameWidth as nameSpriteWidth, c.nameWidth as nameSpriteWidth,
c.nameHeight as nameSpriteHeight, c.nameHeight as nameSpriteHeight,
d.isBEm as isBemCard, d.isBEm as isBemCard,
a.characterId = uc.id as isInAdventure a.characterId = uc.id as isInAdventure
FROM UserCharacter uc FROM UserCharacter uc
JOIN Character c ON uc.charId = c.id JOIN CardCharacter c ON uc.charId = c.id
JOIN Card d ON c.dimId = d.id JOIN Card d ON c.cardId = d.id
JOIN Sprite s ON s.id = c.spriteId JOIN Sprite s ON s.id = c.spriteId
LEFT JOIN Adventure a ON a.characterId = uc.id LEFT JOIN Adventure a ON a.characterId = uc.id
WHERE uc.id = :id WHERE uc.id = :id
""") """
)
suspend fun getCharacterWithSprites(id: Long): CharacterDtos.CharacterWithSprites suspend fun getCharacterWithSprites(id: Long): CharacterDtos.CharacterWithSprites
@Query("SELECT * FROM UserCharacter WHERE id = :id") @Query("SELECT * FROM UserCharacter WHERE id = :id")
@@ -122,19 +125,20 @@ interface UserCharacterDao {
s.spriteIdle2 AS spriteIdle2, s.spriteIdle2 AS spriteIdle2,
s.width AS spriteWidth, s.width AS spriteWidth,
s.height AS spriteHeight, s.height AS spriteHeight,
c.name as nameSprite, c.nameSprite as nameSprite,
c.nameWidth as nameSpriteWidth, c.nameWidth as nameSpriteWidth,
c.nameHeight as nameSpriteHeight, c.nameHeight as nameSpriteHeight,
d.isBEm as isBemCard, d.isBEm as isBemCard,
a.characterId as isInAdventure a.characterId as isInAdventure
FROM UserCharacter uc FROM UserCharacter uc
JOIN Character c ON uc.charId = c.id JOIN CardCharacter c ON uc.charId = c.id
JOIN Card d ON c.dimId = d.id JOIN Card d ON c.cardId = d.id
JOIN Sprite s ON s.id = c.spriteId JOIN Sprite s ON s.id = c.spriteId
LEFT JOIN Adventure a ON a.characterId = uc.id LEFT JOIN Adventure a ON a.characterId = uc.id
WHERE uc.isActive = 1 WHERE uc.isActive = 1
LIMIT 1 LIMIT 1
""") """
)
suspend fun getActiveCharacter(): CharacterDtos.CharacterWithSprites? suspend fun getActiveCharacter(): CharacterDtos.CharacterWithSprites?
@Query("DELETE FROM UserCharacter WHERE id = :id") @Query("DELETE FROM UserCharacter WHERE id = :id")
@@ -149,22 +153,24 @@ interface UserCharacterDao {
@Query( @Query(
""" """
SELECT c.* SELECT c.*
FROM Character c FROM CardCharacter c
join UserCharacter uc on c.id = uc.charId join UserCharacter uc on c.id = uc.charId
where uc.id = :charId where uc.id = :charId
LIMIT 1 LIMIT 1
""" """
) )
suspend fun getCharacterInfo(charId: Long): Character suspend fun getCharacterInfo(charId: Long): CardCharacter
@Query(""" @Query(
"""
INSERT INTO TransformationHistory(monId, stageId, transformationDate) INSERT INTO TransformationHistory(monId, stageId, transformationDate)
VALUES VALUES
(:monId, (:monId,
(SELECT id FROM Character WHERE monIndex = :stage AND dimId = :dimId), (SELECT id FROM CardCharacter WHERE charaIndex = :stage AND cardId = :dimId),
:transformationDate) :transformationDate)
""") """
)
fun insertTransformation(monId: Long, stage: Int, dimId: Long, transformationDate: Long) fun insertTransformation(monId: Long, stage: Int, dimId: Long, transformationDate: Long)
@Upsert @Upsert
@@ -172,4 +178,54 @@ interface UserCharacterDao {
@Query("""SELECT * FROM VitalsHistory WHERE charId = :charId ORDER BY id ASC""") @Query("""SELECT * FROM VitalsHistory WHERE charId = :charId ORDER BY id ASC""")
suspend fun getVitalsHistory(charId: Long): List<VitalsHistory> suspend fun getVitalsHistory(charId: Long): List<VitalsHistory>
@Query(
"""
SELECT
uc.*,
c.stage,
c.attribute,
s.spriteIdle1 AS spriteIdle,
s.spriteIdle2 AS spriteIdle2,
s.width AS spriteWidth,
s.height AS spriteHeight,
c.nameSprite as nameSprite,
c.nameWidth as nameSpriteWidth,
c.nameHeight as nameSpriteHeight,
d.isBEm as isBemCard,
a.characterId = uc.id as isInAdventure
FROM UserCharacter uc
JOIN 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
WHERE uc.characterType = "BEDevice"
"""
)
suspend fun getBECharacters(): List<CharacterDtos.CharacterWithSprites>
@Query(
"""
SELECT
uc.*,
c.stage,
c.attribute,
s.spriteIdle1 AS spriteIdle,
s.spriteIdle2 AS spriteIdle2,
s.width AS spriteWidth,
s.height AS spriteHeight,
c.nameSprite as nameSprite,
c.nameWidth as nameSpriteWidth,
c.nameHeight as nameSpriteHeight,
d.isBEm as isBemCard,
a.characterId = uc.id as isInAdventure
FROM UserCharacter uc
JOIN 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
WHERE uc.characterType = "VBDevice"
"""
)
suspend fun getVBDimCharacters(): List<CharacterDtos.CharacterWithSprites>
} }
@@ -3,16 +3,25 @@ package com.github.nacabaro.vbhelper.database
import androidx.room.Database import androidx.room.Database
import androidx.room.RoomDatabase import androidx.room.RoomDatabase
import com.github.nacabaro.vbhelper.daos.AdventureDao import com.github.nacabaro.vbhelper.daos.AdventureDao
import com.github.nacabaro.vbhelper.daos.CardAdventureDao
import com.github.nacabaro.vbhelper.daos.CardBackgroundDao
import com.github.nacabaro.vbhelper.daos.CharacterDao import com.github.nacabaro.vbhelper.daos.CharacterDao
import com.github.nacabaro.vbhelper.daos.DexDao import com.github.nacabaro.vbhelper.daos.DexDao
import com.github.nacabaro.vbhelper.daos.CardDao 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.CardProgressDao
import com.github.nacabaro.vbhelper.daos.ItemDao 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.SpriteDao
import com.github.nacabaro.vbhelper.daos.UserCharacterDao import com.github.nacabaro.vbhelper.daos.UserCharacterDao
import com.github.nacabaro.vbhelper.domain.characters.Character import com.github.nacabaro.vbhelper.domain.card.CardBackground
import com.github.nacabaro.vbhelper.domain.card.CardCharacter
import com.github.nacabaro.vbhelper.domain.card.Card 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.CardProgress
import com.github.nacabaro.vbhelper.domain.card.CardSprites
import com.github.nacabaro.vbhelper.domain.card.CardTransformations
import com.github.nacabaro.vbhelper.domain.characters.Sprite import com.github.nacabaro.vbhelper.domain.characters.Sprite
import com.github.nacabaro.vbhelper.domain.characters.Adventure import com.github.nacabaro.vbhelper.domain.characters.Adventure
import com.github.nacabaro.vbhelper.domain.characters.Dex import com.github.nacabaro.vbhelper.domain.characters.Dex
@@ -29,7 +38,10 @@ import com.github.nacabaro.vbhelper.domain.items.Items
entities = [ entities = [
Card::class, Card::class,
CardProgress::class, CardProgress::class,
Character::class, CardCharacter::class,
CardAdventure::class,
CardFusions::class,
CardSprites::class,
Sprite::class, Sprite::class,
UserCharacter::class, UserCharacter::class,
BECharacterData::class, BECharacterData::class,
@@ -39,7 +51,9 @@ import com.github.nacabaro.vbhelper.domain.items.Items
VitalsHistory::class, VitalsHistory::class,
Dex::class, Dex::class,
Items::class, Items::class,
Adventure::class Adventure::class,
CardBackground::class,
CardTransformations::class
] ]
) )
abstract class AppDatabase : RoomDatabase() { abstract class AppDatabase : RoomDatabase() {
@@ -51,4 +65,8 @@ abstract class AppDatabase : RoomDatabase() {
abstract fun itemDao(): ItemDao abstract fun itemDao(): ItemDao
abstract fun adventureDao(): AdventureDao abstract fun adventureDao(): AdventureDao
abstract fun spriteDao(): SpriteDao abstract fun spriteDao(): SpriteDao
abstract fun specialMissionDao(): SpecialMissionDao
abstract fun cardAdventureDao(): CardAdventureDao
abstract fun cardFusionsDao(): CardFusionsDao
abstract fun cardBackgroundDao(): CardBackgroundDao
} }
@@ -1,31 +0,0 @@
package com.github.nacabaro.vbhelper.domain
import androidx.room.Entity
import androidx.room.ForeignKey
import androidx.room.PrimaryKey
import com.github.nacabaro.vbhelper.domain.characters.Character
@Entity(
foreignKeys = [
ForeignKey(
entity = Character::class,
parentColumns = ["id"],
childColumns = ["monId"],
onDelete = ForeignKey.CASCADE
),
ForeignKey(
entity = Character::class,
parentColumns = ["id"],
childColumns = ["nextMon"],
onDelete = ForeignKey.CASCADE
)
]
)
data class Evolutions(
@PrimaryKey val monId: Int,
@PrimaryKey val nextMon: Int,
val trophies: Int,
val vitals: Int,
val totalBattles: Int,
val winRate: Int // Does not need to be a floating point
)
@@ -3,7 +3,6 @@ package com.github.nacabaro.vbhelper.domain
import androidx.room.Entity import androidx.room.Entity
import androidx.room.ForeignKey import androidx.room.ForeignKey
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
import com.github.nacabaro.vbhelper.domain.characters.Character
@Entity( @Entity(
foreignKeys = [ foreignKeys = [
@@ -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?
)
@@ -0,0 +1,23 @@
package com.github.nacabaro.vbhelper.domain.card
import androidx.room.Entity
import androidx.room.ForeignKey
import androidx.room.PrimaryKey
@Entity(
foreignKeys = [
ForeignKey(
entity = Card::class,
parentColumns = ["id"],
childColumns = ["cardId"],
onDelete = ForeignKey.CASCADE
)
]
)
data class CardBackground (
@PrimaryKey(autoGenerate = true) val id: Long = 0,
val cardId: Long,
val background: ByteArray,
val backgroundWidth: Int,
val backgroundHeight: Int
)
@@ -1,17 +1,17 @@
package com.github.nacabaro.vbhelper.domain.characters package com.github.nacabaro.vbhelper.domain.card
import androidx.room.Entity import androidx.room.Entity
import androidx.room.PrimaryKey
import androidx.room.ForeignKey import androidx.room.ForeignKey
import androidx.room.PrimaryKey
import com.github.cfogrady.vbnfc.data.NfcCharacter import com.github.cfogrady.vbnfc.data.NfcCharacter
import com.github.nacabaro.vbhelper.domain.card.Card import com.github.nacabaro.vbhelper.domain.characters.Sprite
@Entity( @Entity(
foreignKeys = [ foreignKeys = [
ForeignKey( ForeignKey(
entity = Card::class, entity = Card::class,
parentColumns = ["id"], parentColumns = ["id"],
childColumns = ["dimId"], childColumns = ["cardId"],
onDelete = ForeignKey.CASCADE onDelete = ForeignKey.CASCADE
), ),
ForeignKey( ForeignKey(
@@ -28,17 +28,17 @@ import com.github.nacabaro.vbhelper.domain.card.Card
* and monIndex. * and monIndex.
* TODO: Customs will mean this should be unique per cardName and monIndex * TODO: Customs will mean this should be unique per cardName and monIndex
*/ */
data class Character ( data class CardCharacter (
@PrimaryKey(autoGenerate = true) val id: Long = 0, @PrimaryKey(autoGenerate = true) val id: Long = 0,
val dimId: Long, val cardId: Long,
val spriteId: Long, val spriteId: Long,
val monIndex: Int, val charaIndex: Int,
val name: ByteArray,
val stage: Int, // These should be replaced with enums val stage: Int, // These should be replaced with enums
val attribute: NfcCharacter.Attribute, // This one too val attribute: NfcCharacter.Attribute, // This one too
val baseHp: Int, val baseHp: Int,
val baseBp: Int, val baseBp: Int,
val baseAp: Int, val baseAp: Int,
val nameSprite: ByteArray,
val nameWidth: Int, val nameWidth: Int,
val nameHeight: Int val nameHeight: Int
) )
@@ -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 = ["toVirusFusion"],
onDelete = ForeignKey.CASCADE
),
ForeignKey(
entity = CardCharacter::class,
parentColumns = ["id"],
childColumns = ["toDataFusion"],
onDelete = ForeignKey.CASCADE
),
ForeignKey(
entity = CardCharacter::class,
parentColumns = ["id"],
childColumns = ["toVaccineFusion"],
onDelete = ForeignKey.CASCADE
),
ForeignKey(
entity = CardCharacter::class,
parentColumns = ["id"],
childColumns = ["toFreeFusion"],
onDelete = ForeignKey.CASCADE
)
]
)
data class CardFusions(
@PrimaryKey(autoGenerate = true) val id: Long,
val fromCharaId: Long,
val toVirusFusion: Long?,
val toDataFusion: Long?,
val toVaccineFusion: Long?,
val toFreeFusion: Long?
)
@@ -0,0 +1,12 @@
package com.github.nacabaro.vbhelper.domain.card
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity
data class CardSprites(
@PrimaryKey(autoGenerate = true) val id: Long = 0,
val sprite: ByteArray,
val width: Int,
val height: Int
)
@@ -0,0 +1,33 @@
package com.github.nacabaro.vbhelper.domain.card
import androidx.room.Entity
import androidx.room.ForeignKey
import androidx.room.PrimaryKey
@Entity(
foreignKeys = [
ForeignKey(
entity = CardCharacter::class,
parentColumns = ["id"],
childColumns = ["charaId"],
onDelete = ForeignKey.CASCADE
),
ForeignKey(
entity = CardCharacter::class,
parentColumns = ["id"],
childColumns = ["toCharaId"],
onDelete = ForeignKey.CASCADE
)
]
)
data class CardTransformations (
@PrimaryKey(autoGenerate = true) val id: Long = 0,
var charaId: Long,
val requiredVitals: Int,
val requiredTrophies: Int,
val requiredBattles: Int,
val requiredWinRate: Int,
val changeTimerHours: Int,
val requiredAdventureLevelCompleted: Int,
val toCharaId: Long?
)
@@ -3,11 +3,12 @@ package com.github.nacabaro.vbhelper.domain.characters
import androidx.room.Entity import androidx.room.Entity
import androidx.room.ForeignKey import androidx.room.ForeignKey
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
import com.github.nacabaro.vbhelper.domain.card.CardCharacter
@Entity( @Entity(
foreignKeys = [ foreignKeys = [
ForeignKey( ForeignKey(
entity = Character::class, entity = CardCharacter::class,
parentColumns = ["id"], parentColumns = ["id"],
childColumns = ["id"], childColumns = ["id"],
onDelete = ForeignKey.CASCADE onDelete = ForeignKey.CASCADE
@@ -3,7 +3,7 @@ package com.github.nacabaro.vbhelper.domain.device_data
import androidx.room.Entity import androidx.room.Entity
import androidx.room.ForeignKey import androidx.room.ForeignKey
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
import com.github.nacabaro.vbhelper.domain.characters.Character import com.github.nacabaro.vbhelper.domain.card.CardCharacter
@Entity( @Entity(
foreignKeys = [ foreignKeys = [
@@ -14,7 +14,7 @@ import com.github.nacabaro.vbhelper.domain.characters.Character
onDelete = ForeignKey.CASCADE onDelete = ForeignKey.CASCADE
), ),
ForeignKey( ForeignKey(
entity = Character::class, entity = CardCharacter::class,
parentColumns = ["id"], parentColumns = ["id"],
childColumns = ["stageId"], childColumns = ["stageId"],
onDelete = ForeignKey.CASCADE onDelete = ForeignKey.CASCADE
@@ -5,12 +5,12 @@ import androidx.room.ForeignKey
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
import com.github.cfogrady.vbnfc.data.NfcCharacter import com.github.cfogrady.vbnfc.data.NfcCharacter
import com.github.nacabaro.vbhelper.utils.DeviceType import com.github.nacabaro.vbhelper.utils.DeviceType
import com.github.nacabaro.vbhelper.domain.characters.Character import com.github.nacabaro.vbhelper.domain.card.CardCharacter
@Entity( @Entity(
foreignKeys = [ foreignKeys = [
ForeignKey( ForeignKey(
entity = Character::class, entity = CardCharacter::class,
parentColumns = ["id"], parentColumns = ["id"],
childColumns = ["charId"], childColumns = ["charId"],
onDelete = ForeignKey.CASCADE onDelete = ForeignKey.CASCADE
@@ -3,6 +3,13 @@ package com.github.nacabaro.vbhelper.domain.items
import androidx.room.Entity import androidx.room.Entity
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
enum class ItemType {
VBITEM,
BEITEM,
UNIVERSAL,
SPECIALMISSION
}
@Entity @Entity
data class Items( data class Items(
@PrimaryKey val id: Long, @PrimaryKey val id: Long,
@@ -11,5 +18,6 @@ data class Items(
val itemIcon: Int, val itemIcon: Int,
val itemLength: Int, val itemLength: Int,
val price: Int, val price: Int,
val quantity: Int val quantity: Int,
val itemType: ItemType
) )
@@ -10,4 +10,18 @@ object CardDtos {
val totalCharacters: Int, val totalCharacters: Int,
val obtainedCharacters: 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,
)
} }
@@ -35,7 +35,7 @@ object CharacterDtos {
) )
data class CardCharacterInfo( data class CardCharacterInfo(
val cardId: Int, val cardId: Long,
val charId: Int, val charId: Int,
val stage: Int, val stage: Int,
val attribute: NfcCharacter.Attribute val attribute: NfcCharacter.Attribute
@@ -50,12 +50,27 @@ object CharacterDtos {
val transformationDate: Long val transformationDate: Long
) )
data class CardCharaProgress(
val id: Long,
val spriteIdle: ByteArray,
val spriteWidth: Int,
val spriteHeight: Int,
val nameSprite: ByteArray,
val nameSpriteWidth: Int,
val nameSpriteHeight: Int,
val discoveredOn: Long?,
val baseHp: Int,
val baseBp: Int,
val baseAp: Int,
val stage: Int,
val attribute: NfcCharacter.Attribute,
)
data class CardProgress( data class CardProgress(
val id: Long, val id: Long,
val spriteIdle: ByteArray, val spriteIdle: ByteArray,
val spriteWidth: Int, val spriteWidth: Int,
val spriteHeight: Int, val spriteHeight: Int,
val discoveredOn: Long?
) )
data class AdventureCharacterWithSprites( data class AdventureCharacterWithSprites(
@@ -83,4 +98,19 @@ object CharacterDtos {
val finishesAdventure: Long, val finishesAdventure: Long,
val originalTimeInMinutes: Long val originalTimeInMinutes: Long
) )
data class EvolutionRequirementsWithSpritesAndObtained(
val charaId: Long,
val fromCharaId: Long,
val spriteIdle: ByteArray,
val spriteWidth: Int,
val spriteHeight: Int,
val discoveredOn: Long?,
val requiredTrophies: Int,
val requiredVitals: Int,
val requiredBattles: Int,
val requiredWinRate: Int,
val changeTimerHours: Int,
val requiredAdventureLevelCompleted: Int
)
} }
@@ -1,5 +1,7 @@
package com.github.nacabaro.vbhelper.dtos package com.github.nacabaro.vbhelper.dtos
import com.github.nacabaro.vbhelper.domain.items.ItemType
object ItemDtos { object ItemDtos {
data class ItemsWithQuantities( data class ItemsWithQuantities(
@@ -10,6 +12,7 @@ object ItemDtos {
val itemLength: Int, val itemLength: Int,
val price: Int, val price: Int,
val quantity: Int, val quantity: Int,
val itemType: ItemType
) )
data class PurchasedItem( data class PurchasedItem(
@@ -18,6 +21,7 @@ object ItemDtos {
val itemDescription: String, val itemDescription: String,
val itemIcon: Int, val itemIcon: Int,
val itemLength: Int, val itemLength: Int,
val itemAmount: Int val itemAmount: Int,
val itemType: ItemType
) )
} }
@@ -19,8 +19,8 @@ import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController import androidx.navigation.compose.rememberNavController
import com.github.nacabaro.vbhelper.di.VBHelper import com.github.nacabaro.vbhelper.di.VBHelper
import com.github.nacabaro.vbhelper.screens.BattlesScreen import com.github.nacabaro.vbhelper.screens.BattlesScreen
import com.github.nacabaro.vbhelper.screens.DexScreen import com.github.nacabaro.vbhelper.screens.cardScreen.CardsScreen
import com.github.nacabaro.vbhelper.screens.DiMScreen import com.github.nacabaro.vbhelper.screens.cardScreen.CardViewScreen
import com.github.nacabaro.vbhelper.screens.homeScreens.HomeScreen import com.github.nacabaro.vbhelper.screens.homeScreens.HomeScreen
import com.github.nacabaro.vbhelper.screens.itemsScreen.ItemsScreen import com.github.nacabaro.vbhelper.screens.itemsScreen.ItemsScreen
import com.github.nacabaro.vbhelper.screens.scanScreen.ScanScreen import com.github.nacabaro.vbhelper.screens.scanScreen.ScanScreen
@@ -34,6 +34,8 @@ import com.github.nacabaro.vbhelper.screens.itemsScreen.ItemsScreenControllerImp
import com.github.nacabaro.vbhelper.screens.settingsScreen.SettingsScreenControllerImpl import com.github.nacabaro.vbhelper.screens.settingsScreen.SettingsScreenControllerImpl
import com.github.nacabaro.vbhelper.screens.adventureScreen.AdventureScreen import com.github.nacabaro.vbhelper.screens.adventureScreen.AdventureScreen
import com.github.nacabaro.vbhelper.screens.adventureScreen.AdventureScreenControllerImpl 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.settingsScreen.CreditsScreen
import com.github.nacabaro.vbhelper.screens.spriteViewer.SpriteViewerControllerImpl import com.github.nacabaro.vbhelper.screens.spriteViewer.SpriteViewerControllerImpl
import com.github.nacabaro.vbhelper.screens.storageScreen.StorageScreenControllerImpl import com.github.nacabaro.vbhelper.screens.storageScreen.StorageScreenControllerImpl
@@ -46,7 +48,8 @@ data class AppNavigationHandlers(
val adventureScreenController: AdventureScreenControllerImpl, val adventureScreenController: AdventureScreenControllerImpl,
val storageScreenController: StorageScreenControllerImpl, val storageScreenController: StorageScreenControllerImpl,
val homeScreenController: HomeScreenControllerImpl, val homeScreenController: HomeScreenControllerImpl,
val spriteViewerController: SpriteViewerControllerImpl val spriteViewerController: SpriteViewerControllerImpl,
val cardScreenController: CardScreenControllerImpl
) )
@Composable @Composable
@@ -121,8 +124,9 @@ fun AppNavigation(
) )
} }
composable(NavigationItems.Dex.route) { composable(NavigationItems.Dex.route) {
DexScreen( CardsScreen(
navController = navController navController = navController,
cardScreenController = applicationNavigationHandlers.cardScreenController
) )
} }
composable(NavigationItems.Settings.route) { composable(NavigationItems.Settings.route) {
@@ -140,9 +144,9 @@ fun AppNavigation(
composable(NavigationItems.CardView.route) { composable(NavigationItems.CardView.route) {
val cardId = it.arguments?.getString("cardId") val cardId = it.arguments?.getString("cardId")
if (cardId != null) { if (cardId != null) {
DiMScreen( CardViewScreen(
navController = navController, navController = navController,
dimId = cardId.toLong() cardId = cardId.toLong()
) )
} }
} }
@@ -174,6 +178,17 @@ fun AppNavigation(
navController = navController navController = navController
) )
} }
composable(NavigationItems.CardAdventure.route) {
val cardId = it.arguments?.getString("cardId")
if (cardId != null) {
CardAdventureScreen(
navController = navController,
cardId = cardId.toLong(),
cardScreenController = applicationNavigationHandlers
.cardScreenController
)
}
}
} }
} }
} }
@@ -11,6 +11,7 @@ sealed class NavigationItems (
object Battles : NavigationItems("Battle", R.drawable.baseline_swords_24, "Battle") object Battles : NavigationItems("Battle", R.drawable.baseline_swords_24, "Battle")
object Home : NavigationItems("Home", R.drawable.baseline_cottage_24, "Home") object Home : NavigationItems("Home", R.drawable.baseline_cottage_24, "Home")
object Dex : NavigationItems("Dex", R.drawable.baseline_menu_book_24, "Dex") 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 Storage : NavigationItems("Storage", R.drawable.baseline_catching_pokemon_24, "Storage")
object Settings : NavigationItems("Settings", R.drawable.baseline_settings_24, "Settings") object Settings : NavigationItems("Settings", R.drawable.baseline_settings_24, "Settings")
object Viewer : NavigationItems("Viewer", R.drawable.baseline_image_24, "Viewer") object Viewer : NavigationItems("Viewer", R.drawable.baseline_image_24, "Viewer")
@@ -1,83 +0,0 @@
package com.github.nacabaro.vbhelper.screens
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
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.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
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.utils.BitmapData
import com.github.nacabaro.vbhelper.components.DexDiMEntry
import com.github.nacabaro.vbhelper.components.TopBanner
import com.github.nacabaro.vbhelper.di.VBHelper
import com.github.nacabaro.vbhelper.dtos.CardDtos
import com.github.nacabaro.vbhelper.navigation.NavigationItems
import com.github.nacabaro.vbhelper.source.DexRepository
import kotlinx.coroutines.launch
@Composable
fun DexScreen(
navController: NavController
) {
val coroutineScope = rememberCoroutineScope()
val application = LocalContext.current.applicationContext as VBHelper
val dexRepository = DexRepository(application.container.db)
val cardList = remember { mutableStateOf<List<CardDtos.CardProgress>>(emptyList()) }
LaunchedEffect(dexRepository) {
coroutineScope.launch {
val newDimList = dexRepository.getAllDims()
cardList.value = newDimList // Replace the entire list atomically
}
}
Scaffold (
topBar = {
TopBanner(
text = "Discovered characters",
onGearClick = {
navController.navigate(NavigationItems.Viewer.route)
}
)
}
) { contentPadding ->
LazyColumn (
modifier = Modifier
.padding(top = contentPadding.calculateTopPadding())
) {
items(cardList.value) {
DexDiMEntry(
name = it.cardName,
logo = BitmapData(
bitmap = it.cardLogo,
width = it.logoWidth,
height = it.logoHeight
),
onClick = {
navController
.navigate(
NavigationItems
.CardView.route
.replace("{cardId}", "${it.cardId}")
)
},
obtainedCharacters = it.obtainedCharacters,
totalCharacters = it.totalCharacters,
modifier = Modifier
.fillMaxWidth()
.padding(8.dp)
)
}
}
}
}
@@ -94,7 +94,8 @@ class AdventureScreenControllerImpl(
itemName = randomItem.name, itemName = randomItem.name,
itemIcon = randomItem.itemIcon, itemIcon = randomItem.itemIcon,
itemLength = randomItem.itemLength, itemLength = randomItem.itemLength,
itemDescription = randomItem.description itemDescription = randomItem.description,
itemType = randomItem.itemType
) )
} }
} }
@@ -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: -")
}
}
}
}
}
@@ -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<CardDtos.CardAdventureWithSprites>()) }
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
)
}
}
}
}
@@ -0,0 +1,109 @@
package com.github.nacabaro.vbhelper.screens.cardScreen
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.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material.icons.filled.Edit
import androidx.compose.material3.Card
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.FilterQuality
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import com.github.nacabaro.vbhelper.utils.BitmapData
import com.github.nacabaro.vbhelper.utils.getBitmap
@Composable
fun CardEntry(
name: String,
logo: BitmapData,
obtainedCharacters: Int,
totalCharacters: Int,
onClick: () -> Unit,
displayModify: Boolean,
onClickModify: () -> Unit,
onClickDelete: () -> Unit,
modifier: Modifier = Modifier
) {
val bitmap = remember (logo.bitmap) { logo.getBitmap() }
val imageBitmap = remember(bitmap) { bitmap.asImageBitmap() }
val density: Float = LocalContext.current.resources.displayMetrics.density
val dpSize = (logo.width * 4 / density).dp
Card (
shape = MaterialTheme.shapes.medium,
modifier = modifier,
onClick = if (!displayModify) {
onClick
} else {
{ }
}
) {
Row (
horizontalArrangement = Arrangement.Start,
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.padding(8.dp)
) {
Image (
bitmap = imageBitmap,
contentDescription = name,
filterQuality = FilterQuality.None,
modifier = Modifier
.padding(8.dp)
.size(dpSize)
)
Column(
modifier = Modifier
.padding(8.dp)
.weight(1f)
) {
Text(
text = name,
modifier = Modifier
)
Text(
text = "$obtainedCharacters of $totalCharacters characters obtained",
fontFamily = MaterialTheme.typography.labelSmall.fontFamily,
fontSize = MaterialTheme.typography.labelSmall.fontSize,
modifier = Modifier
)
}
if (displayModify) {
Row (
modifier = Modifier,
horizontalArrangement = Arrangement.End,
) {
IconButton(
onClick = onClickModify
) {
Icon(
imageVector = Icons.Default.Edit,
contentDescription = "Edit"
)
}
IconButton(
onClick = onClickDelete
) {
Icon(
imageVector = Icons.Default.Delete,
contentDescription = "Delete"
)
}
}
}
}
}
}
@@ -0,0 +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<CardDtos.CardAdventureWithSprites>
suspend fun getCardProgress(cardId: Long): Int
}
@@ -0,0 +1,46 @@
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(
private val componentActivity: ComponentActivity,
) : CardScreenController {
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
.cardDao()
.renameCard(cardId.toInt(), newName)
onRenamed(newName)
}
}
override fun deleteCard(cardId: Long, onDeleted: () -> Unit) {
componentActivity.lifecycleScope.launch {
database
.cardDao()
.deleteCard(cardId)
onDeleted()
}
}
override suspend fun getCardAdventureMissions(cardId: Long): List<CardDtos.CardAdventureWithSprites> {
return database
.cardAdventureDao()
.getAdventureForCard(cardId)
}
override suspend fun getCardProgress(cardId: Long): Int {
return database
.cardProgressDao()
.getCardProgress(cardId)
}
}
@@ -1,4 +1,4 @@
package com.github.nacabaro.vbhelper.screens package com.github.nacabaro.vbhelper.screens.cardScreen
import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
@@ -14,27 +14,34 @@ import androidx.navigation.NavController
import com.github.nacabaro.vbhelper.utils.BitmapData import com.github.nacabaro.vbhelper.utils.BitmapData
import com.github.nacabaro.vbhelper.components.CharacterEntry import com.github.nacabaro.vbhelper.components.CharacterEntry
import com.github.nacabaro.vbhelper.components.TopBanner import com.github.nacabaro.vbhelper.components.TopBanner
import com.github.nacabaro.vbhelper.domain.characters.Character
import com.github.nacabaro.vbhelper.di.VBHelper import com.github.nacabaro.vbhelper.di.VBHelper
import com.github.nacabaro.vbhelper.dtos.CharacterDtos 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 com.github.nacabaro.vbhelper.source.DexRepository
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@Composable @Composable
fun DiMScreen( fun CardViewScreen(
navController: NavController, navController: NavController,
dimId: Long cardId: Long
) { ) {
val coroutineScope = rememberCoroutineScope() val coroutineScope = rememberCoroutineScope()
val application = LocalContext.current.applicationContext as VBHelper val application = LocalContext.current.applicationContext as VBHelper
val dexRepository = DexRepository(application.container.db) val dexRepository = DexRepository(application.container.db)
val characterList = remember { mutableStateOf<List<CharacterDtos.CardProgress>>(emptyList()) } val characterList = remember { mutableStateOf<List<CharacterDtos.CardCharaProgress>>(emptyList()) }
val cardPossibleTransformations = remember { mutableStateOf<List<CharacterDtos.EvolutionRequirementsWithSpritesAndObtained>>(emptyList()) }
val selectedCharacter = remember { mutableStateOf<CharacterDtos.CardCharaProgress?>(null) }
LaunchedEffect(dexRepository) { LaunchedEffect(dexRepository) {
coroutineScope.launch { coroutineScope.launch {
val newCharacterList = dexRepository.getCharactersByDimId(dimId) val newCharacterList = dexRepository.getCharactersByCardId(cardId)
characterList.value = newCharacterList characterList.value = newCharacterList
val newCardPossibleTransformations = dexRepository.getCardPossibleTransformations(cardId)
cardPossibleTransformations.value = newCardPossibleTransformations
} }
} }
@@ -44,6 +51,17 @@ fun DiMScreen(
text = "Discovered characters", text = "Discovered characters",
onBackClick = { onBackClick = {
navController.popBackStack() navController.popBackStack()
},
onAdventureClick = {
navController
.navigate(route = NavigationItems
.CardAdventure
.route
.replace(
"{cardId}",
cardId.toString()
)
)
} }
) )
} }
@@ -54,7 +72,9 @@ fun DiMScreen(
) { ) {
items(characterList.value) { character -> items(characterList.value) { character ->
CharacterEntry( CharacterEntry(
onClick = { }, onClick = {
selectedCharacter.value = character
},
obscure = character.discoveredOn == null, obscure = character.discoveredOn == null,
icon = BitmapData( icon = BitmapData(
bitmap = character.spriteIdle, bitmap = character.spriteIdle,
@@ -64,5 +84,16 @@ fun DiMScreen(
) )
} }
} }
if (selectedCharacter.value != null) {
DexCharaDetailsDialog(
currentChara = selectedCharacter.value!!,
possibleTransformations = cardPossibleTransformations.value,
obscure = selectedCharacter.value!!.discoveredOn == null,
onClickClose = {
selectedCharacter.value = null
}
)
}
} }
} }
@@ -0,0 +1,145 @@
package com.github.nacabaro.vbhelper.screens.cardScreen
import android.util.Log
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
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.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
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
import com.github.nacabaro.vbhelper.utils.BitmapData
import com.github.nacabaro.vbhelper.components.TopBanner
import com.github.nacabaro.vbhelper.di.VBHelper
import com.github.nacabaro.vbhelper.dtos.CardDtos
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<List<CardDtos.CardProgress>>(emptyList()) }
val selectedCard = remember { mutableStateOf<CardDtos.CardProgress?>(null) }
var clickedDelete by remember { mutableStateOf(false) }
var clickedRename by remember { mutableStateOf(false) }
var modifyCards by remember { mutableStateOf(false) }
LaunchedEffect(dexRepository) {
coroutineScope.launch {
val newDimList = dexRepository.getAllDims()
cardList.value = newDimList
}
}
Scaffold (
topBar = {
TopBanner(
text = "My cards",
onModifyClick = {
modifyCards = !modifyCards
}
)
}
) { contentPadding ->
LazyColumn (
modifier = Modifier
.padding(top = contentPadding.calculateTopPadding())
) {
items(cardList.value) {
CardEntry(
name = it.cardName,
logo = BitmapData(
bitmap = it.cardLogo,
width = it.logoWidth,
height = it.logoHeight
),
onClick = {
navController
.navigate(
NavigationItems
.CardView.route
.replace("{cardId}", "${it.cardId}")
)
},
obtainedCharacters = it.obtainedCharacters,
totalCharacters = it.totalCharacters,
modifier = Modifier
.fillMaxWidth()
.padding(8.dp),
displayModify = modifyCards,
onClickModify = {
selectedCard.value = it
clickedRename = true
},
onClickDelete = {
selectedCard.value = it
clickedDelete = true
}
)
}
}
}
if (clickedRename) {
CardRenameDialog(
onDismiss = {
clickedRename = false
selectedCard.value = null
},
onRename = { newName ->
Log.d("CardsScreen", "New name: $newName")
Log.d("CardsScreen", "Card: ${selectedCard.value.toString()}")
cardScreenController
.renameCard(
cardId = selectedCard.value!!.cardId,
newName = newName,
onRenamed = {
clickedRename = false
selectedCard.value = null
}
)
},
currentName = selectedCard.value!!.cardName
)
}
if (clickedDelete) {
CardDeleteDialog(
cardName = selectedCard.value!!.cardName,
onDismiss = {
clickedDelete = false
selectedCard.value = null
},
onConfirm = {
cardScreenController
.deleteCard(
cardId = selectedCard.value!!.cardId,
onDeleted = {
clickedDelete = false
selectedCard.value = null
}
)
}
)
}
}
@@ -0,0 +1,51 @@
package com.github.nacabaro.vbhelper.screens.cardScreen.dialogs
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.Card
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
@Composable
fun CardDeleteDialog(
cardName: String,
onDismiss: () -> Unit,
onConfirm: () -> Unit
) {
Dialog(
onDismissRequest = onDismiss
) {
Card ( ) {
Column (
modifier = Modifier
.padding(16.dp)
) {
Text(text = "Are you sure you want to delete $cardName. This action will also delete all the characters raised from this card.")
Spacer(modifier = Modifier.padding(8.dp))
Row {
Button(
onClick = {
onDismiss()
}
) {
Text(text = "Confirm")
}
Button(
onClick = {
onConfirm()
}
) {
Text(text = "Delete")
}
}
}
}
}
}
@@ -0,0 +1,52 @@
package com.github.nacabaro.vbhelper.screens.cardScreen.dialogs
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.Card
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
@Composable
fun CardRenameDialog(
onDismiss: () -> Unit,
onRename: (String) -> Unit,
currentName: String
) {
var cardName by remember { mutableStateOf(currentName) }
Dialog(
onDismissRequest = onDismiss
) {
Card ( ) {
Column (
modifier = Modifier
.padding(16.dp)
) {
TextField(
value = cardName,
onValueChange = { cardName = it }
)
Spacer(modifier = Modifier.padding(8.dp))
Button(
onClick = {
onRename(cardName)
onDismiss()
}
) {
Text(text = "Rename")
}
}
}
}
}
@@ -0,0 +1,215 @@
package com.github.nacabaro.vbhelper.screens.cardScreen.dialogs
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.material3.Button
import androidx.compose.material3.Card
import androidx.compose.material3.CardColors
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.contentColorFor
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.FilterQuality
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import com.github.nacabaro.vbhelper.dtos.CharacterDtos
import com.github.nacabaro.vbhelper.utils.BitmapData
import com.github.nacabaro.vbhelper.utils.getImageBitmap
@Composable
fun DexCharaDetailsDialog(
currentChara: CharacterDtos.CardCharaProgress,
possibleTransformations: List<CharacterDtos.EvolutionRequirementsWithSpritesAndObtained>,
obscure: Boolean,
onClickClose: () -> Unit
) {
val nameMultiplier = 3
val charaMultiplier = 4
val currentCharaPossibleTransformations = possibleTransformations.filter { it.fromCharaId == currentChara.id }
val romanNumeralsStage = when (currentChara.stage) {
1 -> "II"
2 -> "III"
3 -> "IV"
4 -> "V"
5 -> "VI"
6 -> "VII"
else -> "I"
}
val charaBitmapData = BitmapData(
bitmap = currentChara.spriteIdle,
width = currentChara.spriteWidth,
height = currentChara.spriteHeight
)
val charaImageBitmapData = charaBitmapData.getImageBitmap(
context = LocalContext.current,
multiplier = charaMultiplier,
obscure = obscure
)
val nameBitmapData = BitmapData(
bitmap = currentChara.nameSprite,
width = currentChara.nameSpriteWidth,
height = currentChara.nameSpriteHeight
)
val nameImageBitmapData = nameBitmapData.getImageBitmap(
context = LocalContext.current,
multiplier = nameMultiplier,
obscure = obscure
)
Dialog(
onDismissRequest = onClickClose
) {
Card (
modifier = Modifier
.fillMaxWidth()
) {
Column (
modifier = Modifier
.padding(16.dp)
) {
Row (
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
) {
Card (
colors = CardColors(
containerColor = MaterialTheme.colorScheme.surfaceVariant,
contentColor = MaterialTheme.colorScheme.contentColorFor(
backgroundColor = MaterialTheme.colorScheme.surfaceVariant,
),
disabledContainerColor = MaterialTheme.colorScheme.surfaceVariant,
disabledContentColor = MaterialTheme.colorScheme.contentColorFor(
backgroundColor = MaterialTheme.colorScheme.surfaceVariant,
)
)
) {
Image(
bitmap = charaImageBitmapData.imageBitmap,
contentDescription = "Icon",
modifier = Modifier
.size(charaImageBitmapData.dpWidth)
.padding(8.dp),
colorFilter = when (obscure) {
true -> ColorFilter.tint(color = MaterialTheme.colorScheme.secondary)
false -> null
},
filterQuality = FilterQuality.None
)
}
Spacer(
modifier = Modifier
.padding(16.dp)
)
if (!obscure) {
Column {
Image(
bitmap = nameImageBitmapData.imageBitmap,
contentDescription = "Icon",
modifier = Modifier
.width(nameImageBitmapData.dpWidth)
.height(nameImageBitmapData.dpHeight),
filterQuality = FilterQuality.None
)
Spacer(modifier = Modifier.padding(4.dp))
if (currentChara.baseHp != 65535) {
Text(
text = "HP: ${currentChara.baseHp}, BP: ${currentChara.baseBp}, AP: ${currentChara.baseAp}"
)
Text(text = "Stg: ${romanNumeralsStage}, Atr: ${currentChara.attribute.toString().substring(0, 2)}")
}
}
} else {
Column {
Text(text = "????????????????")
Spacer(modifier = Modifier.padding(4.dp))
Text(text = "Stg: -, Atr: -")
Text(text = "HP: -, BP: -, AP: -")
}
}
}
Spacer(modifier = Modifier.padding(16.dp))
Column {
currentCharaPossibleTransformations.map {
val selectedCharaBitmap = BitmapData(
bitmap = it.spriteIdle,
width = it.spriteWidth,
height = it.spriteHeight
)
val selectedCharaImageBitmap = selectedCharaBitmap.getImageBitmap(
context = LocalContext.current,
multiplier = 4,
obscure = it.discoveredOn == null
)
Card (
modifier = Modifier
.padding(vertical = 8.dp)
) {
Row (
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.fillMaxWidth()
) {
Card (
colors = CardColors(
containerColor = MaterialTheme.colorScheme.surfaceVariant,
contentColor = MaterialTheme.colorScheme.contentColorFor(
backgroundColor = MaterialTheme.colorScheme.surfaceVariant,
),
disabledContainerColor = MaterialTheme.colorScheme.surfaceVariant,
disabledContentColor = MaterialTheme.colorScheme.contentColorFor(
backgroundColor = MaterialTheme.colorScheme.surfaceVariant,
)
)
) {
Image(
bitmap = selectedCharaImageBitmap.imageBitmap,
contentDescription = "Icon",
modifier = Modifier
.size(selectedCharaImageBitmap.dpWidth)
.padding(8.dp),
colorFilter = when (it.discoveredOn == null) {
true -> ColorFilter.tint(color = MaterialTheme.colorScheme.secondary)
false -> null
},
filterQuality = FilterQuality.None
)
}
Spacer(
modifier = Modifier
.padding(16.dp)
)
Column {
Text("Tr: ${it.requiredTrophies}; Bt: ${it.requiredBattles}; Vr: ${it.requiredVitals}; Wr: ${it.requiredWinRate}%; Ct: ${it.changeTimerHours}h")
Text("AdvLvl ${it.requiredAdventureLevelCompleted + 1}")
}
}
}
}
}
Button(
onClick = onClickClose
) {
Text("Close")
}
}
}
}
}
@@ -28,7 +28,7 @@ fun BetaWarning(
) )
Spacer(modifier = Modifier.padding(8.dp)) Spacer(modifier = Modifier.padding(8.dp))
Text( Text(
text = "Also, this application does not work yet with the original VB." text = "Application should work now with the original VB and the VH."
) )
Spacer(modifier = Modifier.padding(8.dp)) Spacer(modifier = Modifier.padding(8.dp))
Text( Text(
@@ -1,5 +1,6 @@
package com.github.nacabaro.vbhelper.screens.homeScreens package com.github.nacabaro.vbhelper.screens.homeScreens
import android.util.Log
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
@@ -27,9 +28,15 @@ import com.github.nacabaro.vbhelper.components.TopBanner
import com.github.nacabaro.vbhelper.di.VBHelper import com.github.nacabaro.vbhelper.di.VBHelper
import com.github.nacabaro.vbhelper.utils.DeviceType import com.github.nacabaro.vbhelper.utils.DeviceType
import com.github.nacabaro.vbhelper.domain.device_data.BECharacterData 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.VBCharacterData import com.github.nacabaro.vbhelper.domain.device_data.VBCharacterData
import com.github.nacabaro.vbhelper.dtos.CharacterDtos import com.github.nacabaro.vbhelper.dtos.CharacterDtos
import com.github.nacabaro.vbhelper.dtos.ItemDtos
import com.github.nacabaro.vbhelper.navigation.NavigationItems import com.github.nacabaro.vbhelper.navigation.NavigationItems
import com.github.nacabaro.vbhelper.screens.homeScreens.screens.BEBEmHomeScreen
import com.github.nacabaro.vbhelper.screens.homeScreens.screens.BEDiMHomeScreen
import com.github.nacabaro.vbhelper.screens.homeScreens.screens.VBDiMHomeScreen
import com.github.nacabaro.vbhelper.screens.itemsScreen.ObtainedItemDialog
import com.github.nacabaro.vbhelper.source.StorageRepository import com.github.nacabaro.vbhelper.source.StorageRepository
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
@@ -45,15 +52,21 @@ fun HomeScreen(
val transformationHistory = remember { mutableStateOf<List<CharacterDtos.TransformationHistory>?>(null) } val transformationHistory = remember { mutableStateOf<List<CharacterDtos.TransformationHistory>?>(null) }
val beData = remember { mutableStateOf<BECharacterData?>(null) } val beData = remember { mutableStateOf<BECharacterData?>(null) }
val vbData = remember { mutableStateOf<VBCharacterData?>(null) } val vbData = remember { mutableStateOf<VBCharacterData?>(null) }
val vbSpecialMissions = remember { mutableStateOf<List<SpecialMissions>>(emptyList()) }
var adventureMissionsFinished by rememberSaveable { mutableStateOf(false) } var adventureMissionsFinished by rememberSaveable { mutableStateOf(false) }
var betaWarning by rememberSaveable { mutableStateOf(true) } var betaWarning by rememberSaveable { mutableStateOf(true) }
var collectedItem by remember { mutableStateOf<ItemDtos.PurchasedItem?>(null) }
LaunchedEffect(storageRepository, activeMon) { LaunchedEffect(storageRepository, activeMon, collectedItem) {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
activeMon.value = storageRepository.getActiveCharacter() activeMon.value = storageRepository.getActiveCharacter()
if (activeMon.value != null) { if (activeMon.value != null && activeMon.value!!.characterType == DeviceType.BEDevice) {
beData.value = storageRepository.getCharacterBeData(activeMon.value!!.id) beData.value = storageRepository.getCharacterBeData(activeMon.value!!.id)
transformationHistory.value = storageRepository.getTransformationHistory(activeMon.value!!.id) transformationHistory.value = storageRepository.getTransformationHistory(activeMon.value!!.id)
} else if (activeMon.value != null && activeMon.value!!.characterType == DeviceType.VBDevice) {
vbData.value = storageRepository.getCharacterVbData(activeMon.value!!.id)
vbSpecialMissions.value = storageRepository.getSpecialMissions(activeMon.value!!.id)
transformationHistory.value = storageRepository.getTransformationHistory(activeMon.value!!.id)
} }
} }
} }
@@ -79,6 +92,7 @@ fun HomeScreen(
} }
) { contentPadding -> ) { contentPadding ->
if (activeMon.value == null || (beData.value == null && vbData.value == null) || transformationHistory.value == null) { if (activeMon.value == null || (beData.value == null && vbData.value == null) || transformationHistory.value == null) {
Log.d("TetTet", "Something is null")
Column ( Column (
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center, verticalArrangement = Arrangement.Center,
@@ -89,6 +103,7 @@ fun HomeScreen(
Text(text = "Nothing to see here") Text(text = "Nothing to see here")
} }
} else { } else {
Log.d("TetTet", "Something is not null")
if (activeMon.value!!.isBemCard) { if (activeMon.value!!.isBemCard) {
BEBEmHomeScreen( BEBEmHomeScreen(
activeMon = activeMon.value!!, activeMon = activeMon.value!!,
@@ -108,12 +123,26 @@ fun HomeScreen(
activeMon = activeMon.value!!, activeMon = activeMon.value!!,
vbData = vbData.value!!, vbData = vbData.value!!,
transformationHistory = transformationHistory.value!!, transformationHistory = transformationHistory.value!!,
contentPadding = contentPadding contentPadding = contentPadding,
specialMissions = vbSpecialMissions.value,
homeScreenController = homeScreenController,
onClickCollect = { item ->
collectedItem = item
}
) )
} }
} }
} }
if (collectedItem != null) {
ObtainedItemDialog(
obtainedItem = collectedItem!!,
onClickDismiss = {
collectedItem = null
}
)
}
if (adventureMissionsFinished) { if (adventureMissionsFinished) {
Dialog( Dialog(
onDismissRequest = { adventureMissionsFinished = false }, onDismissRequest = { adventureMissionsFinished = false },
@@ -1,5 +1,9 @@
package com.github.nacabaro.vbhelper.screens.homeScreens package com.github.nacabaro.vbhelper.screens.homeScreens
import com.github.cfogrady.vbnfc.vb.SpecialMission
import com.github.nacabaro.vbhelper.dtos.ItemDtos
interface HomeScreenController { interface HomeScreenController {
fun didAdventureMissionsFinish(onCompletion: (Boolean) -> Unit) fun didAdventureMissionsFinish(onCompletion: (Boolean) -> Unit)
fun clearSpecialMission(missionId: Long, missionCompletion: SpecialMission.Status, onCleared: (ItemDtos.PurchasedItem?) -> Unit)
} }
@@ -2,9 +2,13 @@ package com.github.nacabaro.vbhelper.screens.homeScreens
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.github.cfogrady.vbnfc.vb.SpecialMission
import com.github.nacabaro.vbhelper.di.VBHelper import com.github.nacabaro.vbhelper.di.VBHelper
import com.github.nacabaro.vbhelper.dtos.ItemDtos
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.time.Instant import java.time.Instant
import kotlin.math.roundToInt
import kotlin.random.Random
class HomeScreenControllerImpl( class HomeScreenControllerImpl(
private val componentActivity: ComponentActivity, private val componentActivity: ComponentActivity,
@@ -26,4 +30,43 @@ class HomeScreenControllerImpl(
onCompletion(finishedAdventureCharacters.isNotEmpty()) onCompletion(finishedAdventureCharacters.isNotEmpty())
} }
} }
override fun clearSpecialMission(missionId: Long, missionCompletion: SpecialMission.Status, onCleared: (ItemDtos.PurchasedItem?) -> Unit) {
componentActivity.lifecycleScope.launch {
database
.specialMissionDao()
.clearSpecialMission(missionId)
if (missionCompletion == SpecialMission.Status.COMPLETED) {
val randomItem = database
.itemDao()
.getAllItems()
.random()
val randomItemAmount = (Random.nextFloat() * 5).roundToInt()
database
.itemDao()
.purchaseItem(
itemId = randomItem.id,
itemAmount = randomItemAmount
)
val purchasedItem = ItemDtos.PurchasedItem(
itemId = randomItem.id,
itemName = randomItem.name,
itemDescription = randomItem.description,
itemIcon = randomItem.itemIcon,
itemLength = randomItem.itemLength,
itemAmount = randomItemAmount,
itemType = randomItem.itemType
)
onCleared(purchasedItem)
} else {
onCleared(null)
}
}
}
} }
@@ -1,16 +0,0 @@
package com.github.nacabaro.vbhelper.screens.homeScreens
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.runtime.Composable
import com.github.nacabaro.vbhelper.domain.device_data.VBCharacterData
import com.github.nacabaro.vbhelper.dtos.CharacterDtos
@Composable
fun VBDiMHomeScreen(
activeMon: CharacterDtos.CharacterWithSprites,
vbData: VBCharacterData,
transformationHistory: List<CharacterDtos.TransformationHistory>,
contentPadding: PaddingValues
) {
TODO("Not implemented yet")
}
@@ -1,4 +1,4 @@
package com.github.nacabaro.vbhelper.screens.homeScreens package com.github.nacabaro.vbhelper.screens.homeScreens.screens
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
@@ -1,4 +1,4 @@
package com.github.nacabaro.vbhelper.screens.homeScreens package com.github.nacabaro.vbhelper.screens.homeScreens.screens
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
@@ -0,0 +1,189 @@
package com.github.nacabaro.vbhelper.screens.homeScreens.screens
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.github.nacabaro.vbhelper.R
import com.github.nacabaro.vbhelper.components.CharacterEntry
import com.github.nacabaro.vbhelper.components.ItemDisplay
import com.github.nacabaro.vbhelper.components.SpecialMissionsEntry
import com.github.nacabaro.vbhelper.components.TransformationHistoryCard
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 com.github.nacabaro.vbhelper.screens.homeScreens.HomeScreenControllerImpl
import com.github.nacabaro.vbhelper.utils.BitmapData
import java.util.Locale
@Composable
fun VBDiMHomeScreen(
activeMon: CharacterDtos.CharacterWithSprites,
vbData: VBCharacterData,
specialMissions: List<SpecialMissions>,
homeScreenController: HomeScreenControllerImpl,
transformationHistory: List<CharacterDtos.TransformationHistory>,
contentPadding: PaddingValues,
onClickCollect: (ItemDtos.PurchasedItem?) -> Unit
) {
Column(
modifier = Modifier
.padding(top = contentPadding.calculateTopPadding())
.verticalScroll(state = rememberScrollState())
) {
Row(
modifier = Modifier
.fillMaxWidth()
) {
CharacterEntry(
icon = BitmapData(
bitmap = activeMon.spriteIdle,
width = activeMon.spriteWidth,
height = activeMon.spriteHeight
),
multiplier = 8,
shape = androidx.compose.material.MaterialTheme.shapes.small,
modifier = Modifier
.weight(1f)
.aspectRatio(1f)
)
Column(
modifier = Modifier
.weight(0.5f)
.aspectRatio(0.5f)
) {
ItemDisplay(
icon = R.drawable.baseline_vitals_24,
textValue = activeMon.vitalPoints.toString(),
definition = "Vitals",
modifier = Modifier
.weight(0.5f)
.aspectRatio(1f)
.padding(8.dp)
)
ItemDisplay(
icon = R.drawable.baseline_trophy_24,
textValue = activeMon.trophies.toString(),
definition = "Trophies",
modifier = Modifier
.weight(0.5f)
.aspectRatio(1f)
.padding(8.dp)
)
}
}
Row(
modifier = Modifier
.fillMaxWidth()
) {
ItemDisplay(
icon = R.drawable.baseline_mood_24,
textValue = activeMon.mood.toString(),
definition = "Mood",
modifier = Modifier
.weight(1f)
.aspectRatio(1f)
.padding(8.dp)
)
val transformationCountdownInHours = activeMon.transformationCountdown / 60
ItemDisplay(
icon = R.drawable.baseline_next_24,
textValue = when (transformationCountdownInHours) {
0 -> "${activeMon.transformationCountdown} m"
else -> "$transformationCountdownInHours h"
},
definition = "Next timer",
modifier = Modifier
.weight(1f)
.aspectRatio(1f)
.padding(8.dp)
)
ItemDisplay(
icon = R.drawable.baseline_swords_24,
textValue = when {
activeMon.totalBattlesLost == 0 -> "0.00 %"
else -> {
val battleWinPercentage =
activeMon.totalBattlesWon.toFloat() / (activeMon.totalBattlesWon + activeMon.totalBattlesLost).toFloat()
String.format(
Locale.getDefault(),
"%.2f",
battleWinPercentage * 100
) + " %" // Specify locale
}
},
definition = "Total battle win %",
modifier = Modifier
.weight(1f)
.aspectRatio(1f)
.padding(8.dp)
)
ItemDisplay(
icon = R.drawable.baseline_swords_24,
textValue = when {
activeMon.totalBattlesLost == 0 -> "0.00 %"
else -> {
val battleWinPercentage =
activeMon.currentPhaseBattlesWon.toFloat() / (activeMon.currentPhaseBattlesWon + activeMon.currentPhaseBattlesLost).toFloat()
String.format(
Locale.getDefault(),
"%.2f",
battleWinPercentage * 100
) + " %" // Specify locale
}
},
definition = "Current phase win %",
modifier = Modifier
.weight(1f)
.aspectRatio(1f)
.padding(8.dp)
)
}
Row(
modifier = Modifier
.fillMaxWidth()
) {
TransformationHistoryCard(
transformationHistory = transformationHistory,
modifier = Modifier
.weight(1f)
.padding(8.dp)
)
}
Row (
modifier = Modifier
.padding(16.dp)
) {
Text(
text = "Special missions",
fontSize = 24.sp
)
}
for (mission in specialMissions) {
Row(
modifier = Modifier
.fillMaxWidth()
) {
SpecialMissionsEntry(
specialMission = mission,
modifier = Modifier
.weight(1f)
.padding(8.dp),
) {
homeScreenController
.clearSpecialMission(mission.id, mission.status, onClickCollect)
}
}
}
}
}
@@ -19,7 +19,9 @@ import androidx.navigation.NavController
import com.github.nacabaro.vbhelper.components.CharacterEntry import com.github.nacabaro.vbhelper.components.CharacterEntry
import com.github.nacabaro.vbhelper.components.TopBanner import com.github.nacabaro.vbhelper.components.TopBanner
import com.github.nacabaro.vbhelper.di.VBHelper import com.github.nacabaro.vbhelper.di.VBHelper
import com.github.nacabaro.vbhelper.domain.items.ItemType
import com.github.nacabaro.vbhelper.dtos.CharacterDtos 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.source.StorageRepository
import com.github.nacabaro.vbhelper.utils.BitmapData import com.github.nacabaro.vbhelper.utils.BitmapData
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@@ -39,10 +41,25 @@ fun ChooseCharacterScreen(
} }
var selectedCharacter by remember { mutableStateOf<Long?>(null) } var selectedCharacter by remember { mutableStateOf<Long?>(null) }
var selectedItem by remember { mutableStateOf<ItemDtos.ItemsWithQuantities?>(null) }
LaunchedEffect(storageRepository) { LaunchedEffect(storageRepository) {
coroutineScope.launch { coroutineScope.launch {
characterList.value = storageRepository.getAllCharacters() selectedItem = storageRepository.getItem(itemId)
when (selectedItem?.itemType) {
ItemType.BEITEM -> {
characterList.value = storageRepository.getBECharacters()
}
ItemType.VBITEM -> {
characterList.value = storageRepository.getVBCharacters()
}
ItemType.SPECIALMISSION-> {
characterList.value = storageRepository.getVBCharacters()
}
else -> {
characterList.value = storageRepository.getAllCharacters()
}
}
} }
} }
@@ -2,9 +2,12 @@ package com.github.nacabaro.vbhelper.screens.itemsScreen
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.github.cfogrady.vbnfc.vb.SpecialMission
import com.github.nacabaro.vbhelper.domain.device_data.SpecialMissions
import com.github.nacabaro.vbhelper.database.AppDatabase import com.github.nacabaro.vbhelper.database.AppDatabase
import com.github.nacabaro.vbhelper.di.VBHelper import com.github.nacabaro.vbhelper.di.VBHelper
import com.github.nacabaro.vbhelper.domain.device_data.BECharacterData import com.github.nacabaro.vbhelper.domain.device_data.BECharacterData
import com.github.nacabaro.vbhelper.domain.device_data.VBCharacterData
import com.github.nacabaro.vbhelper.dtos.ItemDtos import com.github.nacabaro.vbhelper.dtos.ItemDtos
import com.github.nacabaro.vbhelper.utils.DeviceType import com.github.nacabaro.vbhelper.utils.DeviceType
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@@ -24,7 +27,15 @@ class ItemsScreenControllerImpl (
AllTraining(5), AllTraining(5),
EvoTimer(6), EvoTimer(6),
LimitTimer(7), LimitTimer(7),
Vitals(8) Vitals(8),
Step8k(9),
Step4k(10),
Vitals1000(11),
Vitals250(12),
Battle20(13),
Battle5(14),
Win10(15),
Win4(16)
} }
init { init {
@@ -37,17 +48,20 @@ class ItemsScreenControllerImpl (
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
val item = getItem(itemId) val item = getItem(itemId)
val characterData = database.userCharacterDao().getCharacter(characterId) val characterData = database.userCharacterDao().getCharacter(characterId)
val beCharacterData: BECharacterData var beCharacterData: BECharacterData? = null
//var vbCharacterData: VBCharacterData var vbCharacterData: VBCharacterData? = null
if (characterData.characterType == DeviceType.BEDevice) { if (characterData.characterType == DeviceType.BEDevice) {
beCharacterData = database.userCharacterDao().getBeData(characterId) beCharacterData = database.userCharacterDao().getBeData(characterId)
} else { } else if (characterData.characterType == DeviceType.VBDevice) {
TODO("Not implemented") vbCharacterData = database.userCharacterDao().getVbData(characterId)
//vbCharacterData = database.userCharacterDao().getVbData(characterId)
} }
if (item.itemIcon in 1 .. 5 && characterData.characterType == DeviceType.BEDevice) { if (
item.itemIcon in 1 .. 5 &&
characterData.characterType == DeviceType.BEDevice &&
beCharacterData != null
) {
beCharacterData.itemType = item.itemIcon beCharacterData.itemType = item.itemIcon
beCharacterData.itemMultiplier = 3 beCharacterData.itemMultiplier = 3
beCharacterData.itemRemainingTime = item.itemLength beCharacterData.itemRemainingTime = item.itemLength
@@ -72,7 +86,11 @@ class ItemsScreenControllerImpl (
.userCharacterDao() .userCharacterDao()
.updateCharacter(characterData) .updateCharacter(characterData)
} else if (item.itemIcon == ItemTypes.LimitTimer.id) { } else if (
item.itemIcon == ItemTypes.LimitTimer.id &&
characterData.characterType == DeviceType.BEDevice &&
beCharacterData != null
) {
beCharacterData.remainingTrainingTimeInMinutes += item.itemLength beCharacterData.remainingTrainingTimeInMinutes += item.itemLength
if (beCharacterData.remainingTrainingTimeInMinutes > 6000) { if (beCharacterData.remainingTrainingTimeInMinutes > 6000) {
beCharacterData.remainingTrainingTimeInMinutes = 6000 beCharacterData.remainingTrainingTimeInMinutes = 6000
@@ -93,6 +111,12 @@ class ItemsScreenControllerImpl (
database database
.userCharacterDao() .userCharacterDao()
.updateCharacter(characterData) .updateCharacter(characterData)
} else if (item.itemIcon in ItemTypes.Step8k.id .. ItemTypes.Win4.id &&
characterData.characterType == DeviceType.VBDevice &&
vbCharacterData != null
) {
applySpecialMission(item.itemIcon, item.itemLength, characterId)
} }
consumeItem(item.id) consumeItem(item.id)
@@ -104,13 +128,72 @@ class ItemsScreenControllerImpl (
} }
} }
private fun getItem(itemId: Long): ItemDtos.ItemsWithQuantities { private suspend fun applySpecialMission(itemIcon: Int, itemLength: Int, characterId: Long) {
// Hello, it's me, naca! No! I don't like this, I'll see how I can improve it later on...
val specialMissionType = when (itemIcon) {
ItemTypes.Step8k.id -> SpecialMission.Type.STEPS
ItemTypes.Step4k.id -> SpecialMission.Type.STEPS
ItemTypes.Vitals1000.id -> SpecialMission.Type.VITALS
ItemTypes.Vitals250.id -> SpecialMission.Type.VITALS
ItemTypes.Battle20.id -> SpecialMission.Type.BATTLES
ItemTypes.Battle5.id -> SpecialMission.Type.BATTLES
ItemTypes.Win10.id -> SpecialMission.Type.WINS
ItemTypes.Win4.id -> SpecialMission.Type.WINS
else -> SpecialMission.Type.NONE
}
val specialMissionGoal = when (itemIcon) {
ItemTypes.Step8k.id -> 8000
ItemTypes.Step4k.id -> 4000
ItemTypes.Vitals1000.id -> 1000
ItemTypes.Vitals250.id -> 250
ItemTypes.Battle20.id -> 20
ItemTypes.Battle5.id -> 5
ItemTypes.Win10.id -> 10
ItemTypes.Win4.id -> 4
else -> 0
}
val availableSpecialMissions = database
.userCharacterDao()
.getSpecialMissions(characterId)
var firstUnavailableMissionSlot: Long = 0
var watchId = 0
for ((index, mission) in availableSpecialMissions.withIndex()) {
if (
mission.status == SpecialMission.Status.UNAVAILABLE
) {
firstUnavailableMissionSlot = mission.id
watchId = index + 1
}
}
val newSpecialMission = SpecialMissions(
id = firstUnavailableMissionSlot,
characterId = characterId,
missionType = specialMissionType,
goal = specialMissionGoal,
timeLimitInMinutes = itemLength,
watchId = watchId,
status = SpecialMission.Status.AVAILABLE,
progress = 0,
timeElapsedInMinutes = 0
)
database
.userCharacterDao()
.insertSpecialMissions(newSpecialMission)
}
private suspend fun getItem(itemId: Long): ItemDtos.ItemsWithQuantities {
return database return database
.itemDao() .itemDao()
.getItem(itemId) .getItem(itemId)
} }
private fun consumeItem(itemId: Long) { private suspend fun consumeItem(itemId: Long) {
database database
.itemDao() .itemDao()
.useItem(itemId) .useItem(itemId)
@@ -30,7 +30,9 @@ import com.github.cfogrady.vbnfc.data.NfcCharacter
import com.github.nacabaro.vbhelper.ActivityLifecycleListener import com.github.nacabaro.vbhelper.ActivityLifecycleListener
import com.github.nacabaro.vbhelper.components.TopBanner import com.github.nacabaro.vbhelper.components.TopBanner
import com.github.nacabaro.vbhelper.di.VBHelper 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.navigation.NavigationItems
import com.github.nacabaro.vbhelper.screens.cardScreen.ChooseCard
import com.github.nacabaro.vbhelper.source.StorageRepository import com.github.nacabaro.vbhelper.source.StorageRepository
import com.github.nacabaro.vbhelper.source.isMissingSecrets import com.github.nacabaro.vbhelper.source.isMissingSecrets
import com.github.nacabaro.vbhelper.source.proto.Secrets import com.github.nacabaro.vbhelper.source.proto.Secrets
@@ -53,6 +55,8 @@ fun ScanScreen(
val storageRepository = StorageRepository(application.container.db) val storageRepository = StorageRepository(application.container.db)
var nfcCharacter by remember { mutableStateOf<NfcCharacter?>(null) } var nfcCharacter by remember { mutableStateOf<NfcCharacter?>(null) }
var cardsRead by remember { mutableStateOf<List<Card>?>(null) }
val context = LocalContext.current val context = LocalContext.current
LaunchedEffect(storageRepository) { LaunchedEffect(storageRepository) {
@@ -71,6 +75,7 @@ fun ScanScreen(
var readingScreen by remember { mutableStateOf(false) } var readingScreen by remember { mutableStateOf(false) }
var writingScreen by remember { mutableStateOf(false) } var writingScreen by remember { mutableStateOf(false) }
var cardSelectScreen by remember { mutableStateOf(false) }
var isDoneReadingCharacter by remember { mutableStateOf(false) } var isDoneReadingCharacter by remember { mutableStateOf(false) }
var isDoneSendingCard by remember { mutableStateOf(false) } var isDoneSendingCard by remember { mutableStateOf(false) }
var isDoneWritingCharacter by remember { mutableStateOf(false) } var isDoneWritingCharacter by remember { mutableStateOf(false) }
@@ -85,15 +90,33 @@ fun ScanScreen(
} }
override fun onResume() { override fun onResume() {
scanScreenController.onClickRead(secrets!!) { scanScreenController.onClickRead(
isDoneReadingCharacter = true secrets = secrets!!,
} onComplete = {
isDoneReadingCharacter = true
},
onMultipleCards = { cards ->
cardsRead = cards
readingScreen = false
cardSelectScreen = true
isDoneReadingCharacter = true
}
)
} }
} }
) )
scanScreenController.onClickRead(secrets!!) { scanScreenController.onClickRead(
isDoneReadingCharacter = true secrets = secrets!!,
} onComplete = {
isDoneReadingCharacter = true
},
onMultipleCards = { cards ->
cardsRead = cards
readingScreen = false
cardSelectScreen = true
isDoneReadingCharacter = true
}
)
} }
onDispose { onDispose {
if(readingScreen) { if(readingScreen) {
@@ -149,7 +172,7 @@ fun ScanScreen(
} }
} }
if (isDoneReadingCharacter) { if (isDoneReadingCharacter && !cardSelectScreen) {
readingScreen = false readingScreen = false
navController.navigate(NavigationItems.Home.route) navController.navigate(NavigationItems.Home.route)
} else if (isDoneSendingCard && isDoneWritingCharacter) { } else if (isDoneSendingCard && isDoneWritingCharacter) {
@@ -181,6 +204,14 @@ fun ScanScreen(
scanScreenController.cancelRead() scanScreenController.cancelRead()
} }
} }
} else if (cardSelectScreen) {
ChooseCard(
cards = cardsRead!!,
onCardSelected = { card ->
cardSelectScreen = false
scanScreenController.flushCharacter(card.id)
}
)
} else { } else {
ChooseConnectOption( ChooseConnectOption(
onClickRead = when { onClickRead = when {
@@ -290,11 +321,12 @@ fun ScanScreenPreview() {
) { ) {
} }
override fun onClickRead(secrets: Secrets, onComplete: ()->Unit) {} override fun flushCharacter(cardId: Long) {}
override fun onClickRead(secrets: Secrets, onComplete: ()->Unit, onMultipleCards: (List<Card>) -> Unit) {}
override fun onClickCheckCard(secrets: Secrets, nfcCharacter: NfcCharacter, onComplete: () -> Unit) {} override fun onClickCheckCard(secrets: Secrets, nfcCharacter: NfcCharacter, onComplete: () -> Unit) {}
override fun onClickWrite(secrets: Secrets, nfcCharacter: NfcCharacter, onComplete: () -> Unit) {} override fun onClickWrite(secrets: Secrets, nfcCharacter: NfcCharacter, onComplete: () -> Unit) {}
override fun cancelRead() {} override fun cancelRead() {}
override fun characterFromNfc(nfcCharacter: NfcCharacter): String { return "" } override fun characterFromNfc(nfcCharacter: NfcCharacter, onMultipleCards: (List<Card>, NfcCharacter) -> Unit): String { return "" }
override suspend fun characterToNfc(characterId: Long): NfcCharacter? { return null } override suspend fun characterToNfc(characterId: Long): NfcCharacter? { return null }
}, },
characterId = null, characterId = null,
@@ -2,12 +2,13 @@ package com.github.nacabaro.vbhelper.screens.scanScreen
import com.github.cfogrady.vbnfc.data.NfcCharacter import com.github.cfogrady.vbnfc.data.NfcCharacter
import com.github.nacabaro.vbhelper.ActivityLifecycleListener import com.github.nacabaro.vbhelper.ActivityLifecycleListener
import com.github.nacabaro.vbhelper.domain.card.Card
import com.github.nacabaro.vbhelper.source.proto.Secrets import com.github.nacabaro.vbhelper.source.proto.Secrets
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
interface ScanScreenController { interface ScanScreenController {
val secretsFlow: Flow<Secrets> val secretsFlow: Flow<Secrets>
fun onClickRead(secrets: Secrets, onComplete: ()->Unit) fun onClickRead(secrets: Secrets, onComplete: ()->Unit, onMultipleCards: (List<Card>) -> Unit)
fun onClickCheckCard(secrets: Secrets, nfcCharacter: NfcCharacter, onComplete: () -> Unit) fun onClickCheckCard(secrets: Secrets, nfcCharacter: NfcCharacter, onComplete: () -> Unit)
fun onClickWrite(secrets: Secrets, nfcCharacter: NfcCharacter, onComplete: () -> Unit) fun onClickWrite(secrets: Secrets, nfcCharacter: NfcCharacter, onComplete: () -> Unit)
@@ -16,6 +17,11 @@ interface ScanScreenController {
fun registerActivityLifecycleListener(key: String, activityLifecycleListener: ActivityLifecycleListener) fun registerActivityLifecycleListener(key: String, activityLifecycleListener: ActivityLifecycleListener)
fun unregisterActivityLifecycleListener(key: String) fun unregisterActivityLifecycleListener(key: String)
fun characterFromNfc(nfcCharacter: NfcCharacter): String fun flushCharacter(cardId: Long)
fun characterFromNfc(
nfcCharacter: NfcCharacter,
onMultipleCards: (List<Card>, NfcCharacter) -> Unit
): String
suspend fun characterToNfc(characterId: Long): NfcCharacter? suspend fun characterToNfc(characterId: Long): NfcCharacter?
} }
@@ -11,8 +11,11 @@ import android.widget.Toast
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.github.cfogrady.vbnfc.TagCommunicator import com.github.cfogrady.vbnfc.TagCommunicator
import com.github.cfogrady.vbnfc.be.BENfcCharacter
import com.github.cfogrady.vbnfc.data.NfcCharacter import com.github.cfogrady.vbnfc.data.NfcCharacter
import com.github.cfogrady.vbnfc.vb.VBNfcCharacter
import com.github.nacabaro.vbhelper.ActivityLifecycleListener import com.github.nacabaro.vbhelper.ActivityLifecycleListener
import com.github.nacabaro.vbhelper.domain.card.Card
import com.github.nacabaro.vbhelper.screens.scanScreen.converters.FromNfcConverter import com.github.nacabaro.vbhelper.screens.scanScreen.converters.FromNfcConverter
import com.github.nacabaro.vbhelper.screens.scanScreen.converters.ToNfcConverter import com.github.nacabaro.vbhelper.screens.scanScreen.converters.ToNfcConverter
import com.github.nacabaro.vbhelper.source.getCryptographicTransformerMap import com.github.nacabaro.vbhelper.source.getCryptographicTransformerMap
@@ -29,7 +32,7 @@ class ScanScreenControllerImpl(
private val registerActivityLifecycleListener: (String, ActivityLifecycleListener)->Unit, private val registerActivityLifecycleListener: (String, ActivityLifecycleListener)->Unit,
private val unregisterActivityLifecycleListener: (String)->Unit, private val unregisterActivityLifecycleListener: (String)->Unit,
): ScanScreenController { ): ScanScreenController {
private var lastScannedCharacter: NfcCharacter? = null
private val nfcAdapter: NfcAdapter private val nfcAdapter: NfcAdapter
init { init {
@@ -41,10 +44,14 @@ class ScanScreenControllerImpl(
checkSecrets() checkSecrets()
} }
override fun onClickRead(secrets: Secrets, onComplete: ()->Unit) { override fun onClickRead(secrets: Secrets, onComplete: ()->Unit, onMultipleCards: (List<Card>) -> Unit) {
handleTag(secrets) { tagCommunicator -> handleTag(secrets) { tagCommunicator ->
val character = tagCommunicator.receiveCharacter() val character = tagCommunicator.receiveCharacter()
val resultMessage = characterFromNfc(character) val resultMessage = characterFromNfc(character) { cards, nfcCharacter ->
lastScannedCharacter = nfcCharacter
onMultipleCards(cards)
}
onComplete.invoke() onComplete.invoke()
resultMessage resultMessage
} }
@@ -118,7 +125,15 @@ class ScanScreenControllerImpl(
) { ) {
handleTag(secrets) { tagCommunicator -> handleTag(secrets) { tagCommunicator ->
try { try {
tagCommunicator.sendCharacter(nfcCharacter) if (nfcCharacter is VBNfcCharacter) {
Log.d("SendCharacter", "VBNfcCharacter")
val castNfcCharacter: VBNfcCharacter = nfcCharacter
tagCommunicator.sendCharacter(castNfcCharacter)
} else if (nfcCharacter is BENfcCharacter) {
Log.d("SendCharacter", "BENfcCharacter")
val castNfcCharacter: BENfcCharacter = nfcCharacter
tagCommunicator.sendCharacter(castNfcCharacter)
}
onComplete.invoke() onComplete.invoke()
"Sent character successfully!" "Sent character successfully!"
} catch (e: Throwable) { } catch (e: Throwable) {
@@ -146,17 +161,36 @@ class ScanScreenControllerImpl(
componentActivity.startActivity(Intent(Settings.ACTION_WIRELESS_SETTINGS)) componentActivity.startActivity(Intent(Settings.ACTION_WIRELESS_SETTINGS))
} }
override fun characterFromNfc(nfcCharacter: NfcCharacter): String { override fun characterFromNfc(
nfcCharacter: NfcCharacter,
onMultipleCards: (List<Card>, NfcCharacter) -> Unit
): String {
val nfcConverter = FromNfcConverter( val nfcConverter = FromNfcConverter(
componentActivity = componentActivity componentActivity = componentActivity
) )
return nfcConverter.addCharacter(nfcCharacter) return nfcConverter.addCharacter(nfcCharacter, onMultipleCards)
} }
override suspend fun characterToNfc(characterId: Long): NfcCharacter { override suspend fun characterToNfc(characterId: Long): NfcCharacter {
val nfcGenerator = ToNfcConverter( val nfcGenerator = ToNfcConverter(
componentActivity = componentActivity componentActivity = componentActivity
) )
return nfcGenerator.characterToNfc(characterId)
val character = nfcGenerator.characterToNfc(characterId)
Log.d("CharacterType", character.toString())
return character
}
override fun flushCharacter(cardId: Long) {
val nfcConverter = FromNfcConverter(
componentActivity = componentActivity
)
componentActivity.lifecycleScope.launch(Dispatchers.IO) {
if (lastScannedCharacter != null) {
nfcConverter.addCharacterUsingCard(lastScannedCharacter!!, cardId)
lastScannedCharacter = null
}
}
} }
} }
@@ -0,0 +1,48 @@
package com.github.nacabaro.vbhelper.screens.cardScreen
import androidx.compose.foundation.layout.padding
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.ui.Modifier
import androidx.compose.ui.unit.dp
import com.github.nacabaro.vbhelper.components.TopBanner
import com.github.nacabaro.vbhelper.domain.card.Card
import com.github.nacabaro.vbhelper.screens.scanScreen.cardSelect.ScanCardEntry
import com.github.nacabaro.vbhelper.utils.BitmapData
@Composable
fun ChooseCard(
cards: List<Card>,
onCardSelected: (Card) -> Unit
) {
Scaffold (
topBar = {
TopBanner(
text = "Choose card",
)
}
) { contentPadding ->
LazyColumn (
modifier = Modifier
.padding(top = contentPadding.calculateTopPadding())
) {
items(cards) {
ScanCardEntry(
name = it.name,
logo = BitmapData(
it.logo,
it.logoWidth,
it.logoHeight
),
onClick = {
onCardSelected(it)
},
modifier = Modifier
.padding(8.dp)
)
}
}
}
}
@@ -1,4 +1,4 @@
package com.github.nacabaro.vbhelper.components package com.github.nacabaro.vbhelper.screens.scanScreen.cardSelect
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
@@ -21,11 +21,9 @@ import com.github.nacabaro.vbhelper.utils.BitmapData
import com.github.nacabaro.vbhelper.utils.getBitmap import com.github.nacabaro.vbhelper.utils.getBitmap
@Composable @Composable
fun DexDiMEntry( fun ScanCardEntry(
name: String, name: String,
logo: BitmapData, logo: BitmapData,
obtainedCharacters: Int,
totalCharacters: Int,
onClick: () -> Unit, onClick: () -> Unit,
modifier: Modifier = Modifier modifier: Modifier = Modifier
) { ) {
@@ -56,17 +54,12 @@ fun DexDiMEntry(
Column( Column(
modifier = Modifier modifier = Modifier
.padding(8.dp) .padding(8.dp)
.weight(1f)
) { ) {
Text( Text(
text = name, text = name,
modifier = Modifier modifier = Modifier
) )
Text(
text = "$obtainedCharacters of $totalCharacters characters obtained",
fontFamily = MaterialTheme.typography.labelSmall.fontFamily,
fontSize = MaterialTheme.typography.labelSmall.fontSize,
modifier = Modifier
)
} }
} }
} }
@@ -21,17 +21,70 @@ class FromNfcConverter (
) { ) {
private val application = componentActivity.applicationContext as VBHelper private val application = componentActivity.applicationContext as VBHelper
private val database = application.container.db private val database = application.container.db
fun addCharacterUsingCard(
fun addCharacter(nfcCharacter: NfcCharacter): String { nfcCharacter: NfcCharacter,
cardId: Long
): String {
val cardData = database val cardData = database
.cardDao() .cardDao()
.getDimById(nfcCharacter.dimId.toInt()) .getCardById(cardId)
if (cardData == null) if (cardData == null) {
return "Card not found" return "Card not found"
}
return insertCharacter(nfcCharacter, cardData)
}
fun addCharacter(
nfcCharacter: NfcCharacter,
onMultipleCards: (List<Card>, NfcCharacter) -> Unit
): String {
val appReservedCardId = nfcCharacter
.appReserved2[0].toLong()
var cardData: Card? = null
if (appReservedCardId != 0L) {
val fetchedCard = database
.cardDao()
.getCardById(appReservedCardId)
if (fetchedCard == null) {
return "Card not found"
} else if (fetchedCard.cardId == nfcCharacter.dimId.toInt()) {
cardData = fetchedCard
}
}
if (cardData == null) {
val allCards = database
.cardDao()
.getCardByCardId(nfcCharacter.dimId.toInt())
if (allCards.isEmpty())
return "Card not found"
if (allCards.size > 1) {
onMultipleCards(allCards, nfcCharacter)
return "Multiple cards found"
}
cardData = allCards[0]
}
return insertCharacter(nfcCharacter, cardData)
}
private fun insertCharacter(
nfcCharacter: NfcCharacter,
cardData: Card
): String {
val cardCharData = database val cardCharData = database
.characterDao() .characterDao()
.getCharacterByMonIndex(nfcCharacter.charIndex.toInt(), cardData.id) .getCharacterByMonIndex(nfcCharacter.charIndex.toInt(), cardData.id)
@@ -92,7 +145,7 @@ class FromNfcConverter (
return "Done reading character!" return "Done reading character!"
} }
private fun updateCardProgress( private fun updateCardProgress(
@@ -1,6 +1,7 @@
package com.github.nacabaro.vbhelper.screens.scanScreen.converters package com.github.nacabaro.vbhelper.screens.scanScreen.converters
import android.icu.util.Calendar import android.icu.util.Calendar
import android.icu.util.TimeZone
import android.util.Log import android.util.Log
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import com.github.cfogrady.vbnfc.be.BENfcCharacter import com.github.cfogrady.vbnfc.be.BENfcCharacter
@@ -59,7 +60,7 @@ class ToNfcConverter(
.userCharacterDao() .userCharacterDao()
.getVbData(characterId) .getVbData(characterId)
val paddedTransformationArray = generateTransformationHistory(characterId) val paddedTransformationArray = generateTransformationHistory(characterId, 9)
val watchSpecialMissions = generateSpecialMissionsArray(characterId) val watchSpecialMissions = generateSpecialMissionsArray(characterId)
@@ -84,7 +85,7 @@ class ToNfcConverter(
transformationHistory = paddedTransformationArray, transformationHistory = paddedTransformationArray,
vitalHistory = generateVitalsHistoryArray(characterId), vitalHistory = generateVitalsHistoryArray(characterId),
appReserved1 = ByteArray(12) {0}, appReserved1 = ByteArray(12) {0},
appReserved2 = Array(3) {0u}, appReserved2 = generateUShortAppReserved(userCharacter),
generation = vbData.generation.toUShort(), generation = vbData.generation.toUShort(),
totalTrophies = vbData.totalTrophies.toUShort(), totalTrophies = vbData.totalTrophies.toUShort(),
specialMissions = watchSpecialMissions.toTypedArray() specialMissions = watchSpecialMissions.toTypedArray()
@@ -94,6 +95,23 @@ class ToNfcConverter(
} }
private suspend fun generateUShortAppReserved(
userCharacter: UserCharacter
): Array<UShort> {
val cardData = database
.cardDao()
.getCardByCharacterId(userCharacter.id)
val appReserved = Array<UShort>(3) {
0u
}
appReserved[0] = cardData.id.toUShort()
return appReserved
}
private suspend fun generateSpecialMissionsArray( private suspend fun generateSpecialMissionsArray(
characterId: Long characterId: Long
@@ -218,23 +236,31 @@ class ToNfcConverter(
private suspend fun generateTransformationHistory( private suspend fun generateTransformationHistory(
characterId: Long characterId: Long,
length: Int = 8
): Array<NfcCharacter.Transformation> { ): Array<NfcCharacter.Transformation> {
val transformationHistory = database val transformationHistory = database
.userCharacterDao() .userCharacterDao()
.getTransformationHistory(characterId)!! .getTransformationHistory(characterId)!!
.map { .map {
val date = Date(it.transformationDate) val date = Date(it.transformationDate)
val calendar = android.icu.util.GregorianCalendar() val calendar = android.icu.util.GregorianCalendar(TimeZone.getTimeZone("UTC"))
calendar.time = date calendar.time = date
Log.d(
"TransformationHistory",
"Year: ${calendar.get(Calendar.YEAR)}, " +
"Month: ${calendar.get(Calendar.MONTH) + 1}, " +
"Day: ${calendar.get(Calendar.DAY_OF_MONTH)}"
)
NfcCharacter.Transformation( NfcCharacter.Transformation(
toCharIndex = it.monIndex.toUByte(), toCharIndex = it.monIndex.toUByte(),
year = calendar year = calendar
.get(Calendar.YEAR) .get(Calendar.YEAR)
.toUShort(), .toUShort(),
month = calendar month = (calendar
.get(Calendar.MONTH) .get(Calendar.MONTH) + 1)
.toUByte(), .toUByte(),
day = calendar day = calendar
.get(Calendar.DAY_OF_MONTH) .get(Calendar.DAY_OF_MONTH)
@@ -242,7 +268,7 @@ class ToNfcConverter(
) )
}.toTypedArray() }.toTypedArray()
val paddedTransformationArray = padTransformationArray(transformationHistory) val paddedTransformationArray = padTransformationArray(transformationHistory, length)
return paddedTransformationArray return paddedTransformationArray
} }
@@ -250,13 +276,14 @@ class ToNfcConverter(
private fun padTransformationArray( private fun padTransformationArray(
transformationArray: Array<NfcCharacter.Transformation> transformationArray: Array<NfcCharacter.Transformation>,
length: Int
): Array<NfcCharacter.Transformation> { ): Array<NfcCharacter.Transformation> {
if (transformationArray.size >= 8) { if (transformationArray.size >= 8) {
return transformationArray return transformationArray
} }
val paddedArray = Array(8) { val paddedArray = Array(length) {
NfcCharacter.Transformation( NfcCharacter.Transformation(
toCharIndex = 255u, toCharIndex = 255u,
year = 65535u, year = 65535u,
@@ -8,6 +8,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.navigation.NavController import androidx.navigation.NavController
import com.github.nacabaro.vbhelper.components.TopBanner import com.github.nacabaro.vbhelper.components.TopBanner
import com.github.nacabaro.vbhelper.navigation.NavigationItems
@Composable @Composable
fun CreditsScreen( fun CreditsScreen(
@@ -19,6 +20,9 @@ fun CreditsScreen(
text = "Credits", text = "Credits",
onBackClick = { onBackClick = {
navController.popBackStack() navController.popBackStack()
},
onGearClick = {
navController.navigate(NavigationItems.Viewer.route)
} }
) )
}, },
@@ -35,6 +39,7 @@ fun CreditsScreen(
SettingsEntry(title = "cfogrady", description = "Developed vb-lib-nfc and part of this application.") { } SettingsEntry(title = "cfogrady", description = "Developed vb-lib-nfc and part of this application.") { }
SettingsEntry(title = "nacabaro", description = "Developed this application.") { } SettingsEntry(title = "nacabaro", description = "Developed this application.") { }
SettingsEntry(title = "lightheel", description = "Developing the battling part for this application, including server. Still in the works.") { } SettingsEntry(title = "lightheel", description = "Developing the battling part for this application, including server. Still in the works.") { }
SettingsEntry(title = "shvstrz", description = "Designing the app icon in SVG.") { }
} }
} }
} }
@@ -17,8 +17,10 @@ import com.github.nacabaro.vbhelper.database.AppDatabase
import com.github.nacabaro.vbhelper.di.VBHelper import com.github.nacabaro.vbhelper.di.VBHelper
import com.github.nacabaro.vbhelper.domain.characters.Sprite import com.github.nacabaro.vbhelper.domain.characters.Sprite
import com.github.nacabaro.vbhelper.domain.card.Card import com.github.nacabaro.vbhelper.domain.card.Card
import com.github.nacabaro.vbhelper.domain.card.CardBackground
import com.github.nacabaro.vbhelper.domain.card.CardProgress import com.github.nacabaro.vbhelper.domain.card.CardProgress
import com.github.nacabaro.vbhelper.domain.characters.Character import com.github.nacabaro.vbhelper.domain.card.CardCharacter
import com.github.nacabaro.vbhelper.domain.card.CardSprites
import com.github.nacabaro.vbhelper.source.ApkSecretsImporter import com.github.nacabaro.vbhelper.source.ApkSecretsImporter
import com.github.nacabaro.vbhelper.source.SecretsImporter import com.github.nacabaro.vbhelper.source.SecretsImporter
import com.github.nacabaro.vbhelper.source.SecretsRepository import com.github.nacabaro.vbhelper.source.SecretsRepository
@@ -109,10 +111,283 @@ class SettingsScreenControllerImpl(
filePickerCard.launch(arrayOf("*/*")) 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<CardCharacter>()
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,
toCharaIdVirus = it.attribute1Fusion,
toCharaIdData = it.attribute2Fusion,
toCharaIdVaccine = it.attribute3Fusion,
toCharaIdFree = it.attribute4Fusion
)
}
}
}
private fun updateCardProgress(
cardId: Long,
) {
val cardProgress = CardProgress(
cardId = cardId,
currentStage = 0,
unlocked = false
)
database
.cardProgressDao()
.updateDimProgress(cardProgress)
}
private suspend fun importBackgrounds(
cardId: Long,
card: com.github.cfogrady.vb.dim.card.Card<*, *, *, *, *, *>
) {
val backgrounds = mutableListOf<CardBackground>()
if (card is DimCard) {
backgrounds.add(
CardBackground(
cardId = cardId,
background = card.spriteData.sprites[1].pixelData,
backgroundWidth = card.spriteData.sprites[1].width,
backgroundHeight = card.spriteData.sprites[1].height
)
)
} else if (card is BemCard) {
val possibleBackgroundsIds = listOf(1, 10, 30, 31, 32, 33)
for (id in possibleBackgroundsIds) {
backgrounds.add(
CardBackground(
cardId = cardId,
background = card.spriteData.sprites[id].pixelData,
backgroundWidth = card.spriteData.sprites[id].width,
backgroundHeight = card.spriteData.sprites[id].height
)
)
}
}
database
.cardBackgroundDao()
.insertCardBackground(*backgrounds.toTypedArray())
}
private fun importCard(uri: Uri) { private fun importCard(uri: Uri) {
context.lifecycleScope.launch(Dispatchers.IO) { context.lifecycleScope.launch(Dispatchers.IO) {
val contentResolver = context.contentResolver val contentResolver = context.contentResolver
val inputStream = contentResolver.openInputStream(uri) val inputStream = contentResolver.openInputStream(uri)
inputStream.use { fileReader -> inputStream.use { fileReader ->
val dimReader = DimReader() val dimReader = DimReader()
val card = dimReader.readCard(fileReader, false) val card = dimReader.readCard(fileReader, false)
@@ -120,112 +395,40 @@ class SettingsScreenControllerImpl(
val cardModel = Card( val cardModel = Card(
cardId = card.header.dimId, cardId = card.header.dimId,
logo = card.spriteData.sprites[0].pixelData, logo = card.spriteData.sprites[0].pixelData,
name = card.spriteData.text, // TODO Make user write card name name = card.spriteData.text,
stageCount = card.adventureLevels.levels.size, stageCount = card.adventureLevels.levels.size,
logoHeight = card.spriteData.sprites[0].height, logoHeight = card.spriteData.sprites[0].height,
logoWidth = card.spriteData.sprites[0].width, logoWidth = card.spriteData.sprites[0].width,
isBEm = card is BemCard isBEm = card is BemCard
) )
val dimId = database val cardId = database
.cardDao() .cardDao()
.insertNewDim(cardModel) .insertNewCard(cardModel)
val cardProgress = CardProgress( updateCardProgress(cardId = cardId)
cardId = dimId,
currentStage = 0,
unlocked = false
)
database importCharacterData(cardId, card)
.cardProgressDao()
.updateDimProgress(cardProgress)
val characters = card importEvoData(cardId, card)
.characterStats
.characterEntries
var spriteCounter = when (card is BemCard) { importAdventureMissions(cardId, card)
true -> 54
false -> 10
}
val domainCharacters = mutableListOf<Character>() importCardFusions(cardId, card)
for (index in 0 until characters.size) { importBackgrounds(cardId, card)
var domainSprite: Sprite?
if (index < 2 && card is DimCard) { val cardSprites: List<CardSprites> = card.spriteData.sprites.map {
domainSprite = Sprite( CardSprites(
width = card.spriteData.sprites[spriteCounter + 1].spriteDimensions.width, sprite = it.pixelData,
height = card.spriteData.sprites[spriteCounter + 1].spriteDimensions.height, width = it.width,
spriteIdle1 = card.spriteData.sprites[spriteCounter + 1].pixelData, height = it.height
spriteIdle2 = card.spriteData.sprites[spriteCounter + 2].pixelData,
spriteWalk1 = card.spriteData.sprites[spriteCounter + 1].pixelData,
spriteWalk2 = card.spriteData.sprites[spriteCounter + 3].pixelData,
spriteRun1 = card.spriteData.sprites[spriteCounter + 1].pixelData,
spriteRun2 = card.spriteData.sprites[spriteCounter + 3].pixelData,
spriteTrain1 = card.spriteData.sprites[spriteCounter + 1].pixelData,
spriteTrain2 = card.spriteData.sprites[spriteCounter + 3].pixelData,
spriteHappy = card.spriteData.sprites[spriteCounter + 4].pixelData,
spriteSleep = card.spriteData.sprites[spriteCounter + 5].pixelData,
spriteAttack = card.spriteData.sprites[spriteCounter + 2].pixelData,
spriteDodge = card.spriteData.sprites[spriteCounter + 3].pixelData
)
} else {
domainSprite = Sprite(
width = card.spriteData.sprites[spriteCounter + 1].spriteDimensions.width,
height = card.spriteData.sprites[spriteCounter + 1].spriteDimensions.height,
spriteIdle1 = card.spriteData.sprites[spriteCounter + 1].pixelData,
spriteIdle2 = card.spriteData.sprites[spriteCounter + 2].pixelData,
spriteWalk1 = card.spriteData.sprites[spriteCounter + 3].pixelData,
spriteWalk2 = card.spriteData.sprites[spriteCounter + 4].pixelData,
spriteRun1 = card.spriteData.sprites[spriteCounter + 5].pixelData,
spriteRun2 = card.spriteData.sprites[spriteCounter + 6].pixelData,
spriteTrain1 = card.spriteData.sprites[spriteCounter + 7].pixelData,
spriteTrain2 = card.spriteData.sprites[spriteCounter + 8].pixelData,
spriteHappy = card.spriteData.sprites[spriteCounter + 9].pixelData,
spriteSleep = card.spriteData.sprites[spriteCounter + 10].pixelData,
spriteAttack = card.spriteData.sprites[spriteCounter + 11].pixelData,
spriteDodge = card.spriteData.sprites[spriteCounter + 12].pixelData
)
}
val spriteId = database
.spriteDao()
.insertSprite(domainSprite)
domainCharacters.add(
Character(
dimId = dimId,
spriteId = spriteId,
monIndex = index,
name = card.spriteData.sprites[spriteCounter].pixelData,
stage = characters[index].stage,
attribute = NfcCharacter.Attribute.entries[characters[index].attribute],
baseHp = characters[index].hp,
baseBp = characters[index].dp,
baseAp = characters[index].ap,
nameWidth = card.spriteData.sprites[spriteCounter].spriteDimensions.width,
nameHeight = card.spriteData.sprites[spriteCounter].spriteDimensions.height,
)
) )
spriteCounter += if (card is BemCard) {
14
} else {
when (index) {
0 -> 6
1 -> 7
else -> 14
}
}
} }
database database
.characterDao() .cardDao()
.insertCharacter(*domainCharacters.toTypedArray()) .insertSprites(*cardSprites.toTypedArray())
} }
inputStream?.close() inputStream?.close()
@@ -1,9 +1,9 @@
package com.github.nacabaro.vbhelper.screens.spriteViewer package com.github.nacabaro.vbhelper.screens.spriteViewer
import android.graphics.Bitmap import android.graphics.Bitmap
import com.github.nacabaro.vbhelper.domain.characters.Sprite import com.github.nacabaro.vbhelper.domain.card.CardBackground
interface SpriteViewerController { interface SpriteViewerController {
suspend fun getAllSprites(): List<Sprite> suspend fun getAllSprites(): List<CardBackground>
fun convertToBitmap(sprites: List<Sprite>): List<Bitmap> fun convertToBitmap(sprites: List<CardBackground>): List<Bitmap>
} }
@@ -1,65 +1,29 @@
package com.github.nacabaro.vbhelper.screens.spriteViewer package com.github.nacabaro.vbhelper.screens.spriteViewer
import android.graphics.Bitmap import android.graphics.Bitmap
import android.util.Log
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.compose.runtime.remember
import androidx.compose.ui.graphics.asImageBitmap
import com.github.nacabaro.vbhelper.di.VBHelper import com.github.nacabaro.vbhelper.di.VBHelper
import com.github.nacabaro.vbhelper.domain.characters.Sprite
import java.nio.ByteBuffer import java.nio.ByteBuffer
import androidx.core.graphics.createBitmap
import com.github.nacabaro.vbhelper.domain.card.CardBackground
class SpriteViewerControllerImpl( class SpriteViewerControllerImpl(
private val context: ComponentActivity private val context: ComponentActivity
) : SpriteViewerController { ) : SpriteViewerController {
override suspend fun getAllSprites(): List<Sprite> { override suspend fun getAllSprites(): List<CardBackground> {
val applicationContext = context.applicationContext as VBHelper val applicationContext = context.applicationContext as VBHelper
val db = applicationContext.container.db val db = applicationContext.container.db
val sprites = db.spriteDao().getAllSprites() val sprites = db.cardBackgroundDao().getBackgrounds()
return sprites return sprites
} }
// I don't like this, chief // I don't like this, chief
override fun convertToBitmap(sprites: List<Sprite>): List<Bitmap> { override fun convertToBitmap(sprites: List<CardBackground>): List<Bitmap> {
val bitmapList = mutableListOf<Bitmap>() val bitmapList = mutableListOf<Bitmap>()
for (sprite in sprites) { for (sprite in sprites) {
Log.d("SpriteViewer", "sprite: $sprite") bitmapList.add(createBitmap(sprite.backgroundWidth, sprite.backgroundHeight, Bitmap.Config.RGB_565).apply {
bitmapList.add(Bitmap.createBitmap(sprite.width, sprite.height, Bitmap.Config.RGB_565).apply { copyPixelsFromBuffer(ByteBuffer.wrap(sprite.background))
copyPixelsFromBuffer(ByteBuffer.wrap(sprite.spriteIdle1))
})
bitmapList.add(Bitmap.createBitmap(sprite.width, sprite.height, Bitmap.Config.RGB_565).apply {
copyPixelsFromBuffer(ByteBuffer.wrap(sprite.spriteIdle2))
})
bitmapList.add(Bitmap.createBitmap(sprite.width, sprite.height, Bitmap.Config.RGB_565).apply {
copyPixelsFromBuffer(ByteBuffer.wrap(sprite.spriteWalk1))
})
bitmapList.add(Bitmap.createBitmap(sprite.width, sprite.height, Bitmap.Config.RGB_565).apply {
copyPixelsFromBuffer(ByteBuffer.wrap(sprite.spriteWalk2))
})
bitmapList.add(Bitmap.createBitmap(sprite.width, sprite.height, Bitmap.Config.RGB_565).apply {
copyPixelsFromBuffer(ByteBuffer.wrap(sprite.spriteRun1))
})
bitmapList.add(Bitmap.createBitmap(sprite.width, sprite.height, Bitmap.Config.RGB_565).apply {
copyPixelsFromBuffer(ByteBuffer.wrap(sprite.spriteRun2))
})
bitmapList.add(Bitmap.createBitmap(sprite.width, sprite.height, Bitmap.Config.RGB_565).apply {
copyPixelsFromBuffer(ByteBuffer.wrap(sprite.spriteTrain1))
})
bitmapList.add(Bitmap.createBitmap(sprite.width, sprite.height, Bitmap.Config.RGB_565).apply {
copyPixelsFromBuffer(ByteBuffer.wrap(sprite.spriteTrain2))
})
bitmapList.add(Bitmap.createBitmap(sprite.width, sprite.height, Bitmap.Config.RGB_565).apply {
copyPixelsFromBuffer(ByteBuffer.wrap(sprite.spriteHappy))
})
bitmapList.add(Bitmap.createBitmap(sprite.width, sprite.height, Bitmap.Config.RGB_565).apply {
copyPixelsFromBuffer(ByteBuffer.wrap(sprite.spriteSleep))
})
bitmapList.add(Bitmap.createBitmap(sprite.width, sprite.height, Bitmap.Config.RGB_565).apply {
copyPixelsFromBuffer(ByteBuffer.wrap(sprite.spriteAttack))
})
bitmapList.add(Bitmap.createBitmap(sprite.width, sprite.height, Bitmap.Config.RGB_565).apply {
copyPixelsFromBuffer(ByteBuffer.wrap(sprite.spriteDodge))
}) })
} }
@@ -11,7 +11,11 @@ class DexRepository (
return db.dexDao().getCardsWithProgress() return db.dexDao().getCardsWithProgress()
} }
suspend fun getCharactersByDimId(cardId: Long): List<CharacterDtos.CardProgress> { suspend fun getCharactersByCardId(cardId: Long): List<CharacterDtos.CardCharaProgress> {
return db.dexDao().getSingleCardProgress(cardId) return db.dexDao().getSingleCardProgress(cardId)
} }
suspend fun getCardPossibleTransformations(cardId: Long): List<CharacterDtos.EvolutionRequirementsWithSpritesAndObtained> {
return db.characterDao().getEvolutionRequirementsForCard(cardId)
}
} }
@@ -2,7 +2,10 @@ package com.github.nacabaro.vbhelper.source
import com.github.nacabaro.vbhelper.database.AppDatabase import com.github.nacabaro.vbhelper.database.AppDatabase
import com.github.nacabaro.vbhelper.domain.device_data.BECharacterData 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.VBCharacterData
import com.github.nacabaro.vbhelper.dtos.CharacterDtos import com.github.nacabaro.vbhelper.dtos.CharacterDtos
import com.github.nacabaro.vbhelper.dtos.ItemDtos
class StorageRepository ( class StorageRepository (
private val db: AppDatabase private val db: AppDatabase
@@ -23,6 +26,18 @@ class StorageRepository (
return db.userCharacterDao().getTransformationHistory(characterId) return db.userCharacterDao().getTransformationHistory(characterId)
} }
suspend fun getCharacterVbData(id: Long): VBCharacterData {
return db.userCharacterDao().getVbData(id)
}
suspend fun getSpecialMissions(id: Long): List<SpecialMissions> {
return db.userCharacterDao().getSpecialMissions(id)
}
suspend fun getItem(id: Long): ItemDtos.ItemsWithQuantities {
return db.itemDao().getItem(id)
}
suspend fun getActiveCharacter(): CharacterDtos.CharacterWithSprites? { suspend fun getActiveCharacter(): CharacterDtos.CharacterWithSprites? {
return db.userCharacterDao().getActiveCharacter() return db.userCharacterDao().getActiveCharacter()
} }
@@ -34,4 +49,12 @@ class StorageRepository (
suspend fun getAdventureCharacters(): List<CharacterDtos.AdventureCharacterWithSprites> { suspend fun getAdventureCharacters(): List<CharacterDtos.AdventureCharacterWithSprites> {
return db.adventureDao().getAdventureCharacters() return db.adventureDao().getAdventureCharacters()
} }
suspend fun getBECharacters(): List<CharacterDtos.CharacterWithSprites> {
return db.userCharacterDao().getBECharacters()
}
suspend fun getVBCharacters(): List<CharacterDtos.CharacterWithSprites> {
return db.userCharacterDao().getVBDimCharacters()
}
} }
@@ -1,6 +1,11 @@
package com.github.nacabaro.vbhelper.utils package com.github.nacabaro.vbhelper.utils
import android.content.Context
import android.graphics.Bitmap import android.graphics.Bitmap
import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.github.cfogrady.vb.dim.sprite.SpriteData import com.github.cfogrady.vb.dim.sprite.SpriteData
// simple, but smooth // simple, but smooth
@@ -10,6 +15,32 @@ data class BitmapData (
val height: Int val height: Int
) )
data class ImageBitmapData(
val imageBitmap: ImageBitmap,
val dpWidth: Dp,
val dpHeight: Dp
)
fun BitmapData.getImageBitmap(
context: Context,
multiplier: Int,
obscure: Boolean
): ImageBitmapData {
val density: Float = context.resources.displayMetrics.density
val imageBitmap: ImageBitmap = if (obscure) {
this.getObscuredBitmap().asImageBitmap()
} else {
this.getBitmap().asImageBitmap()
}
return ImageBitmapData(
imageBitmap = imageBitmap,
dpWidth = (this.width * multiplier / density).dp,
dpHeight = (this.height * multiplier / density).dp
)
}
fun BitmapData.getBitmap(): Bitmap { fun BitmapData.getBitmap(): Bitmap {
return Bitmap.createBitmap(createARGBIntArray(), this.width, this.height, Bitmap.Config.HARDWARE) return Bitmap.createBitmap(createARGBIntArray(), this.width, this.height, Bitmap.Config.HARDWARE)
} }
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:pathData="M200,760h57l391,-391 -57,-57 -391,391v57ZM120,840v-170l528,-527q12,-11 26.5,-17t30.5,-6q16,0 31,6t26,18l55,56q12,11 17.5,26t5.5,30q0,16 -5.5,30.5T817,313L290,840L120,840ZM760,256 L704,200 760,256ZM619,341 L591,312 648,369 619,341Z"
android:fillColor="#000000"/>
</vector>
@@ -1,30 +1,39 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" <vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp" android:width="108dp"
android:height="108dp" android:height="108dp"
android:viewportWidth="108" android:viewportWidth="512"
android:viewportHeight="108"> android:viewportHeight="512">
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z"> <group android:scaleX="0.79"
<aapt:attr name="android:fillColor"> android:scaleY="0.79"
<gradient android:translateX="53.76"
android:endX="85.84757" android:translateY="53.76">
android:endY="92.4963" <group>
android:startX="42.9492" <clip-path
android:startY="49.59793" android:pathData="M0,0h512v512h-512z"/>
android:type="linear"> <path
<item android:pathData="M0,0h512v512h-512z"
android:color="#44000000" android:fillColor="#000000"/>
android:offset="0.0" /> <path
<item android:pathData="M164,107L186.19,107L186.19,129C186.19,132.86 189.32,136 193.19,136L219,136L235.95,163.3C236.53,164.24 237.9,165 239,165L267,165C268.1,165 269.47,164.24 270.05,163.3L287,136L312.8,136C316.66,136 319.8,132.86 319.8,129L319.8,107L313.1,107L313.1,128C313.1,129.1 312.2,130 311.1,130L283,130L267.04,156.29C266.46,157.23 265.1,158 264,158L242,158C240.9,158 239.54,157.23 238.96,156.29L223,130L194.94,130C193.83,130 192.94,129.1 192.94,128L192.94,107L217.75,107L229,92L277,92L288,107L342,107L342,405L319.81,405L319.81,383C319.81,379.14 316.68,376 312.81,376L287,376L270.05,348.7C269.47,347.76 268.1,347 267,347L239,347C237.9,347 236.53,347.76 235.95,348.7L219,376L193.2,376C189.34,376 186.2,379.14 186.2,383L186.2,405L192.94,405L192.94,384C192.94,382.9 193.83,382 194.94,382L223,382L238.96,355.71C239.54,354.77 240.9,354 242,354L264,354C265.1,354 266.46,354.77 267.04,355.71L283,382L311.06,382C312.17,382 313.06,382.9 313.06,384L313.06,405L288.25,405L277,420L229,420L218,405L164,405L164,107Z"
android:color="#00000000" android:fillColor="#EBEBEB"/>
android:offset="1.0" /> <path
</gradient> android:pathData="M212.61,181.5L299.39,181.5C306.15,181.5 311.63,186.98 311.63,193.74L311.63,318.26C311.63,325.02 306.15,330.5 299.39,330.5L212.61,330.5C205.85,330.5 200.37,325.02 200.37,318.26L200.37,193.74C200.37,186.98 205.85,181.5 212.61,181.5Z"
</aapt:attr> android:fillColor="#000000"
</path> android:strokeColor="#00000000"/>
<path <path
android:fillColor="#FFFFFF" android:pathData="M0,249L192.88,249L214,208L232.38,249L253.69,171L274.85,255L296,194L310.27,249L512,249L512,269L296,269L294,261L274.85,331L262.59,283.5L253.69,249L232.38,316L213.38,255L205.75,269L0,269L0,249Z"
android:fillType="nonZero" android:fillColor="#EBEBEB"/>
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z" <group>
android:strokeWidth="1" <clip-path
android:strokeColor="#00000000" /> android:pathData="M-1024,-1024h2048v2048h-2048zM0,249L192.88,249L214,208L232.38,249L253.69,171L274.85,255L296,194L310.27,249L512,249L512,269L296,269L294,261L274.85,331L262.59,283.5L253.69,249L232.38,316L213.38,255L205.75,269L0,269L0,249Z"/>
</vector> <path
android:pathData="M0,249L192.88,249L214,208L232.38,249L253.69,171L274.85,255L296,194L310.27,249L512,249L512,269L296,269L294,261L274.85,331L262.59,283.5L253.69,249L232.38,316L213.38,255L205.75,269L0,269L0,249Z"
android:strokeLineJoin="round"
android:strokeWidth="10"
android:fillColor="#EBEBEB"
android:strokeColor="#000000"
android:strokeLineCap="square"/>
</group>
</group>
</group>
</vector>
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" /> <background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground" /> <foreground android:drawable="@drawable/ic_launcher_foreground"/>
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon> </adaptive-icon>
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" /> <background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground" /> <foreground android:drawable="@drawable/ic_launcher_foreground"/>
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon> </adaptive-icon>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 938 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 982 B

After

Width:  |  Height:  |  Size: 658 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_background">#000000</color>
</resources>
+1 -1
View File
@@ -1,5 +1,5 @@
[versions] [versions]
agp = "8.7.3" agp = "8.12.3"
datastore = "1.1.2" datastore = "1.1.2"
kotlin = "2.0.0" kotlin = "2.0.0"
coreKtx = "1.15.0" coreKtx = "1.15.0"
+1 -1
View File
@@ -1,6 +1,6 @@
#Tue Dec 24 10:55:57 UTC 2024 #Tue Dec 24 10:55:57 UTC 2024
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists