Compare commits

...
Author SHA1 Message Date
nacabaroandGitHub 0b3ce486cb Merge pull request #39 from nacabaro/card/import_more
Adventure Missions progress
2025-09-10 02:10:22 +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
Nacho 36c3e25fd3 Last minute bug related to sending the active character from the main screen. 2025-07-29 19:22:23 +02:00
nacabaroandGitHub ac05dfc541 Merge pull request #33 from nacabaro/vb/nfc_compat
Add VB Compatibility
2025-07-29 18:42:10 +02:00
Nacho 220a61a553 Bug fixes
- Fixed an issue with the calendar, now I store the date as year/month/day in the database directly.
- VB expects to have a year between 2021 and 2032, for some reason it gives me back a 2000 year when reading an empty history. I just patched it in the application to return as year 0 in those cases.
- VitalsHistory works as intended now. Also verified functionality of the new classes.
- Moved a few SQL queries too, since they didn't belong where they were.

Also added a small feature, now the application knows if you entered the scan screen through a character in the storage, or the home screen. If there is an active character and you opened the home screen, the application will let you send the active character, while if it was opened through the storage screen, the application will send the character from the storage screen.

What's now missing is to create a VBSCanScreen, and depending on which device we are using, make use of one screen or the other.
2025-07-29 10:36:07 +02:00
Nacho d847f600f1 Quite a few things
- Renamed a few objects to avoid legality issues
- Added a few accompanying tables to support the VB/VH
  - While we speak about tables, added an auxiliary table to track card adventure progress
- Extracted NFC character generation and extraction to different classes, should result in a cleaner ScanScreenController
2025-07-29 02:50:15 +02:00
Nacho f7a4039d66 Merge branch 'main' into vb/nfc_compat
# Conflicts:
#	app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/ScanScreenControllerImpl.kt
2025-07-29 01:35:21 +02:00
nacabaroandGitHub 1eed6a4c17 Merge pull request #32 from nacabaro/navigation/animations
Graphical things
2025-07-29 01:29:55 +02:00
nacabaroandGitHub 1a09e40d1d Merge pull request #31 from nacabaro/database/get_all_sprites
Fetch all the character sprites into the internal database
2025-07-29 01:26:49 +02:00
Nacho 13051145b7 Few things:
- Speed up the fade animation a tad bit
- Added a back arrow in the scan screen
- Improved a bit the placeholder of the battles screen, since it'd throw out the transition animation.
2025-07-29 00:01:02 +02:00
Nacho 1a150ce7ce Few things:
- Speed up the fade animation a tad bit
- Added a back arrow in the scan screen
- Improved a bit the placeholder of the battles screen, since it'd throw out the transition animation.
2025-07-28 23:58:41 +02:00
Nacho 9f5f806786 Added basic stuff for compatibility with VB 2025-06-01 18:51:30 +02:00
Nacho f7b3b7256a VB NFC compatibility
- Refactored some names (not really relevant)
- Added the ability to store special missions inside the application's database
- Refactored the conversion code into two classes inside the scan screen package
- Added the missing tables to store the necessary vb data

Also not relevant to this update
- Updated adventure progress app wide, so that instead of it being stored in a character basis, it is shared across all characters in the same dim
2025-02-10 17:44:13 +01:00
95 changed files with 3325 additions and 696 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,7 +96,8 @@ 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(
@@ -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
@@ -115,3 +121,89 @@ 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,10 +55,21 @@ 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"
)
}
} else if (onModifyClick != null) {
IconButton(
onClick = onModifyClick,
modifier = Modifier
.align(Alignment.CenterEnd)
) {
Icon(
painter = painterResource(R.drawable.baseline_edit_24),
contentDescription = "Adventure" contentDescription = "Adventure"
) )
} }
@@ -67,10 +79,10 @@ fun TopBanner(
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,36 @@
package com.github.nacabaro.vbhelper.daos
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import com.github.nacabaro.vbhelper.domain.card.Card
@Dao
interface CardDao {
@Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun insertNewCard(card: Card): Long
@Query("SELECT * FROM Card WHERE cardId = :id")
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)
}
@@ -0,0 +1,32 @@
package com.github.nacabaro.vbhelper.daos
import androidx.room.Dao
import androidx.room.Query
@Dao
interface CardFusionsDao {
@Query("""
INSERT INTO
CardFusions (
fromCharaId,
attribute1Fusion,
attribute2Fusion,
attribute3Fusion,
attribute4Fusion
)
SELECT
(SELECT id FROM CardCharacter WHERE cardId = :cardId AND charaIndex = :fromCharaId),
(SELECT id FROM CardCharacter WHERE cardId = :cardId AND charaIndex = :toCharaIdAttr1),
(SELECT id FROM CardCharacter WHERE cardId = :cardId AND charaIndex = :toCharaIdAttr2),
(SELECT id FROM CardCharacter WHERE cardId = :cardId AND charaIndex = :toCharaIdAttr3),
(SELECT id FROM CardCharacter WHERE cardId = :cardId AND charaIndex = :toCharaIdAttr4)
""")
suspend fun insertNewFusion(
cardId: Long,
fromCharaId: Int,
toCharaIdAttr1: Int,
toCharaIdAttr2: Int,
toCharaIdAttr3: Int,
toCharaIdAttr4: Int
)
}
@@ -0,0 +1,17 @@
package com.github.nacabaro.vbhelper.daos
import androidx.room.Dao
import androidx.room.Query
import androidx.room.Upsert
import com.github.nacabaro.vbhelper.domain.card.CardProgress
@Dao
interface CardProgressDao {
@Upsert
fun updateDimProgress(vararg cardProgresses: CardProgress)
@Query(
"SELECT currentStage FROM CardProgress WHERE cardId = :cardId"
)
fun getCardProgress(cardId: Long): Int
}
@@ -3,49 +3,85 @@ 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") @Query("SELECT * FROM CardCharacter WHERE charaIndex = :monIndex AND cardId = :dimId LIMIT 1")
suspend fun getAllCharacters(): List<Character> fun getCharacterByMonIndex(monIndex: Int, dimId: Long): CardCharacter
@Query("SELECT * FROM Character WHERE dimId = :dimId")
suspend fun getCharacterByDimId(dimId: Int): List<Character>
@Query("SELECT * FROM Character WHERE monIndex = :monIndex AND dimId = :dimId LIMIT 1")
fun getCharacterByMonIndex(monIndex: Int, dimId: Long): Character
@Insert @Insert
suspend fun insertSprite(vararg sprite: Sprite) suspend fun insertSprite(vararg sprite: Sprite)
@Query("SELECT * FROM Sprite") @Query(
suspend fun getAllSprites(): List<Sprite> """
SELECT
d.cardId as cardId,
c.charaIndex as charId,
c.stage as stage,
c.attribute as attribute
FROM CardCharacter c
JOIN UserCharacter uc ON c.id = uc.charId
JOIN Card d ON c.cardId = d.id
WHERE c.id = :charId
"""
)
suspend fun getCharacterInfo(charId: Long): CharacterDtos.CardCharacterInfo
@Query(
"""
INSERT INTO PossibleTransformations (charaId, requiredVitals, requiredTrophies, requiredBattles, requiredWinRate, changeTimerHours, requiredAdventureLevelCompleted, toCharaId)
SELECT
(SELECT id FROM 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( @Query(
""" """
SELECT SELECT
d.dimId as cardId, pt.charaId as fromCharaId,
c.monIndex as charId pt.toCharaId as charaId,
FROM Character c s.spriteIdle1 as spriteIdle,
JOIN UserCharacter uc ON c.id = uc.charId s.width as spriteWidth,
JOIN Card d ON c.dimId = d.id s.height as spriteHeight,
WHERE uc.id = :charId d.discoveredOn as discoveredOn,
pt.requiredTrophies as requiredTrophies,
pt.requiredVitals as requiredVitals,
pt.requiredBattles as requiredBattles,
pt.requiredWinRate as requiredWinRate,
pt.changeTimerHours as changeTimerHours,
pt.requiredAdventureLevelCompleted as requiredAdventureLevelCompleted
FROM
PossibleTransformations pt
JOIN 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 getCharacterInfo(charId: Long): CharacterDtos.DiMInfo suspend fun getEvolutionRequirementsForCard(cardId: Long): List<CharacterDtos.EvolutionRequirementsWithSpritesAndObtained>
@Query("""
INSERT INTO TransformationHistory(monId, stageId, transformationDate)
VALUES
(:monId,
(SELECT id FROM Character WHERE monIndex = :stage AND dimId = :dimId),
:transformationDate)
""")
fun insertTransformation(monId: Long, stage: Int, dimId: Long, transformationDate: Long)
} }
@@ -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>
} }
@@ -1,19 +0,0 @@
package com.github.nacabaro.vbhelper.daos
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import com.github.nacabaro.vbhelper.domain.characters.Card
@Dao
interface DiMDao {
@Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun insertNewDim(card: Card): Long
@Query("SELECT * FROM Card")
suspend fun getAllDims(): List<Card>
@Query("SELECT * FROM Card WHERE dimId = :id")
fun getDimById(id: Int): Card?
}
@@ -1,11 +0,0 @@
package com.github.nacabaro.vbhelper.daos
import androidx.room.Dao
import androidx.room.Upsert
import com.github.nacabaro.vbhelper.domain.DimProgress
@Dao
interface DiMProgressDao {
@Upsert
suspend fun updateDimProgress(vararg dimProgress: DimProgress)
}
@@ -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,10 +5,13 @@ 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.TransformationHistory import com.github.nacabaro.vbhelper.domain.device_data.TransformationHistory
import com.github.nacabaro.vbhelper.domain.device_data.VBCharacterData
import com.github.nacabaro.vbhelper.domain.device_data.VitalsHistory
import com.github.nacabaro.vbhelper.dtos.CharacterDtos import com.github.nacabaro.vbhelper.dtos.CharacterDtos
@Dao @Dao
@@ -19,6 +22,9 @@ interface UserCharacterDao {
@Insert @Insert
fun insertBECharacterData(characterData: BECharacterData) fun insertBECharacterData(characterData: BECharacterData)
@Insert
fun insertVBCharacterData(characterData: VBCharacterData)
@Upsert @Upsert
fun updateCharacter(character: UserCharacter) fun updateCharacter(character: UserCharacter)
@@ -28,20 +34,25 @@ interface UserCharacterDao {
@Insert(onConflict = OnConflictStrategy.REPLACE) @Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertTransformationHistory(vararg transformationHistory: TransformationHistory) fun insertTransformationHistory(vararg transformationHistory: TransformationHistory)
@Query(""" @Upsert
fun insertSpecialMissions(vararg specialMissions: SpecialMissions)
@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
""") """
fun getTransformationHistory(monId: Long): List<CharacterDtos.TransformationHistory>? )
suspend fun getTransformationHistory(monId: Long): List<CharacterDtos.TransformationHistory>?
@Query( @Query(
""" """
@@ -53,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
""" """
@@ -77,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")
@@ -97,6 +109,12 @@ interface UserCharacterDao {
@Query("SELECT * FROM BECharacterData WHERE id = :id") @Query("SELECT * FROM BECharacterData WHERE id = :id")
suspend fun getBeData(id: Long): BECharacterData suspend fun getBeData(id: Long): BECharacterData
@Query("SELECT * FROM VBCharacterData WHERE id = :id")
suspend fun getVbData(id: Long): VBCharacterData
@Query("SELECT * FROM SpecialMissions WHERE characterId = :id")
suspend fun getSpecialMissions(id: Long): List<SpecialMissions>
@Query( @Query(
""" """
SELECT SELECT
@@ -107,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")
@@ -134,11 +153,79 @@ 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(
"""
INSERT INTO TransformationHistory(monId, stageId, transformationDate)
VALUES
(:monId,
(SELECT id FROM CardCharacter WHERE charaIndex = :stage AND cardId = :dimId),
:transformationDate)
"""
)
fun insertTransformation(monId: Long, stage: Int, dimId: Long, transformationDate: Long)
@Upsert
fun insertVitals(vararg vitalsHistory: VitalsHistory)
@Query("""SELECT * FROM VitalsHistory WHERE charId = :charId ORDER BY id ASC""")
suspend fun getVitalsHistory(charId: Long): List<VitalsHistory>
@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,42 +3,66 @@ 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.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.DiMDao import com.github.nacabaro.vbhelper.daos.CardDao
import com.github.nacabaro.vbhelper.daos.CardFusionsDao
import com.github.nacabaro.vbhelper.daos.CardProgressDao
import com.github.nacabaro.vbhelper.daos.ItemDao import com.github.nacabaro.vbhelper.daos.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.Background
import com.github.nacabaro.vbhelper.domain.characters.Card import com.github.nacabaro.vbhelper.domain.card.CardCharacter
import com.github.nacabaro.vbhelper.domain.card.Card
import com.github.nacabaro.vbhelper.domain.card.CardAdventure
import com.github.nacabaro.vbhelper.domain.card.CardFusions
import com.github.nacabaro.vbhelper.domain.card.CardProgress
import com.github.nacabaro.vbhelper.domain.card.PossibleTransformations
import com.github.nacabaro.vbhelper.domain.characters.Sprite 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
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.TransformationHistory import com.github.nacabaro.vbhelper.domain.device_data.TransformationHistory
import com.github.nacabaro.vbhelper.domain.device_data.UserCharacter import com.github.nacabaro.vbhelper.domain.device_data.UserCharacter
import com.github.nacabaro.vbhelper.domain.device_data.VBCharacterData
import com.github.nacabaro.vbhelper.domain.device_data.VitalsHistory
import com.github.nacabaro.vbhelper.domain.items.Items import com.github.nacabaro.vbhelper.domain.items.Items
@Database( @Database(
version = 1, version = 1,
entities = [ entities = [
Card::class, Card::class,
Character::class, CardProgress::class,
CardCharacter::class,
CardAdventure::class,
CardFusions::class,
Sprite::class, Sprite::class,
UserCharacter::class, UserCharacter::class,
BECharacterData::class, BECharacterData::class,
VBCharacterData::class,
SpecialMissions::class,
TransformationHistory::class, TransformationHistory::class,
VitalsHistory::class,
Dex::class, Dex::class,
Items::class, Items::class,
Adventure::class Adventure::class,
Background::class,
PossibleTransformations::class
] ]
) )
abstract class AppDatabase : RoomDatabase() { abstract class AppDatabase : RoomDatabase() {
abstract fun dimDao(): DiMDao abstract fun cardDao(): CardDao
abstract fun cardProgressDao(): CardProgressDao
abstract fun characterDao(): CharacterDao abstract fun characterDao(): CharacterDao
abstract fun userCharacterDao(): UserCharacterDao abstract fun userCharacterDao(): UserCharacterDao
abstract fun dexDao(): DexDao abstract fun dexDao(): DexDao
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
} }
@@ -1,29 +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.Card
@Entity(
foreignKeys = [
ForeignKey(
entity = User::class,
parentColumns = ["id"],
childColumns = ["userId"],
onDelete = ForeignKey.CASCADE
),
ForeignKey(
entity = Card::class,
parentColumns = ["id"],
childColumns = ["dimId"],
onDelete = ForeignKey.CASCADE
)
]
)
data class DimProgress(
@PrimaryKey val dimId: Int,
@PrimaryKey val userId: Int,
val currentStage: Int,
val unlocked: Boolean
)
@@ -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 = [
@@ -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,23 @@
package com.github.nacabaro.vbhelper.domain.card
import androidx.room.Entity
import androidx.room.ForeignKey
import androidx.room.PrimaryKey
@Entity(
foreignKeys = [
ForeignKey(
entity = Card::class,
parentColumns = ["id"],
childColumns = ["cardId"],
onDelete = ForeignKey.CASCADE
)
]
)
data class Background (
@PrimaryKey(autoGenerate = true) val id: Long,
val cardId: Long,
val background: ByteArray,
val backgroundWidth: Int,
val backgroundHeight: Int
)
@@ -1,4 +1,4 @@
package com.github.nacabaro.vbhelper.domain.characters package com.github.nacabaro.vbhelper.domain.card
import androidx.room.Entity import androidx.room.Entity
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
@@ -7,7 +7,7 @@ import androidx.room.PrimaryKey
data class Card( data class Card(
@PrimaryKey(autoGenerate = true) @PrimaryKey(autoGenerate = true)
val id: Long = 0, val id: Long = 0,
val dimId: Int, val cardId: Int,
val logo: ByteArray, val logo: ByteArray,
val logoWidth: Int, val logoWidth: Int,
val logoHeight: Int, val logoHeight: Int,
@@ -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?
)
@@ -1,16 +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.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(
@@ -23,21 +24,21 @@ import com.github.cfogrady.vbnfc.data.NfcCharacter
) )
/* /*
* Character represents a character on a DIM card. There should only be one of these per dimId * Character represents a character on a card. There should only be one of these per dimId
* and monIndex. * 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 = ["attribute1Fusion"],
onDelete = ForeignKey.CASCADE
),
ForeignKey(
entity = CardCharacter::class,
parentColumns = ["id"],
childColumns = ["attribute2Fusion"],
onDelete = ForeignKey.CASCADE
),
ForeignKey(
entity = CardCharacter::class,
parentColumns = ["id"],
childColumns = ["attribute3Fusion"],
onDelete = ForeignKey.CASCADE
),
ForeignKey(
entity = CardCharacter::class,
parentColumns = ["id"],
childColumns = ["attribute4Fusion"],
onDelete = ForeignKey.CASCADE
)
]
)
data class CardFusions(
@PrimaryKey(autoGenerate = true) val id: Long,
val fromCharaId: Long,
val attribute1Fusion: Long?,
val attribute2Fusion: Long?,
val attribute3Fusion: Long?,
val attribute4Fusion: Long?
)
@@ -0,0 +1,21 @@
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 CardProgress(
@PrimaryKey val cardId: Long,
val currentStage: Int,
val unlocked: Boolean
)
@@ -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 PossibleTransformations (
@PrimaryKey(autoGenerate = true) val id: Long = 0,
var charaId: Long,
val requiredVitals: Int,
val requiredTrophies: Int,
val requiredBattles: Int,
val requiredWinRate: Int,
val changeTimerHours: Int,
val requiredAdventureLevelCompleted: Int,
val toCharaId: Long?
)
@@ -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
@@ -0,0 +1,28 @@
package com.github.nacabaro.vbhelper.domain.device_data
import androidx.room.Entity
import androidx.room.ForeignKey
import androidx.room.PrimaryKey
import com.github.cfogrady.vbnfc.vb.SpecialMission
@Entity(
foreignKeys = [
ForeignKey(
entity = UserCharacter::class,
parentColumns = ["id"],
childColumns = ["characterId"],
onDelete = ForeignKey.CASCADE
)
]
)
data class SpecialMissions (
@PrimaryKey(autoGenerate = true) var id: Long = 0,
var characterId: Long,
var goal: Int,
val watchId: Int,
val progress: Int,
val status: SpecialMission.Status,
val timeElapsedInMinutes: Int,
val timeLimitInMinutes: Int,
val missionType: SpecialMission.Type
)
@@ -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
@@ -24,7 +24,6 @@ data class UserCharacter (
@PrimaryKey(autoGenerate = true) val id: Long = 0, @PrimaryKey(autoGenerate = true) val id: Long = 0,
var charId: Long, var charId: Long,
var ageInDays: Int, var ageInDays: Int,
var nextAdventureMissionStage: Int, // next adventure mission stage on the character's dim
var mood: Int, var mood: Int,
var vitalPoints: Int, var vitalPoints: Int,
var transformationCountdown: Int, var transformationCountdown: Int,
@@ -1,4 +1,21 @@
package com.github.nacabaro.vbhelper.domain.device_data package com.github.nacabaro.vbhelper.domain.device_data
class VBCharacterData { import androidx.room.Entity
} import androidx.room.ForeignKey
import androidx.room.PrimaryKey
@Entity(
foreignKeys = [
ForeignKey(
entity = UserCharacter::class,
parentColumns = ["id"],
childColumns = ["id"],
onDelete = ForeignKey.CASCADE
)
]
)
data class VBCharacterData (
@PrimaryKey val id: Long,
val generation: Int,
val totalTrophies: Int
)
@@ -0,0 +1,24 @@
package com.github.nacabaro.vbhelper.domain.device_data
import androidx.room.Entity
import androidx.room.ForeignKey
import androidx.room.PrimaryKey
@Entity(
foreignKeys = [
ForeignKey(
entity = UserCharacter::class,
parentColumns = ["id"],
childColumns = ["charId"],
onDelete = ForeignKey.CASCADE
)
]
)
data class VitalsHistory (
@PrimaryKey(autoGenerate = true) val id: Long = 0,
val charId: Long,
val year: Int,
val month: Int,
val day: Int,
val vitalPoints: Int
)
@@ -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,
)
} }
@@ -11,7 +11,6 @@ object CharacterDtos {
var stage: Int, var stage: Int,
var attribute: NfcCharacter.Attribute, var attribute: NfcCharacter.Attribute,
var ageInDays: Int, var ageInDays: Int,
var nextAdventureMissionStage: Int, // next adventure mission stage on the character's dim
var mood: Int, var mood: Int,
var vitalPoints: Int, var vitalPoints: Int,
var transformationCountdown: Int, var transformationCountdown: Int,
@@ -35,9 +34,11 @@ object CharacterDtos {
val isInAdventure: Boolean val isInAdventure: Boolean
) )
data class DiMInfo( data class CardCharacterInfo(
val cardId: Int, val cardId: Long,
val charId: Int val charId: Int,
val stage: Int,
val attribute: NfcCharacter.Attribute
) )
data class TransformationHistory( data class TransformationHistory(
@@ -49,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(
@@ -63,7 +79,6 @@ object CharacterDtos {
var stage: Int, var stage: Int,
var attribute: NfcCharacter.Attribute, var attribute: NfcCharacter.Attribute,
var ageInDays: Int, var ageInDays: Int,
var nextAdventureMissionStage: Int, // next adventure mission stage on the character's dim
var mood: Int, var mood: Int,
var vitalPoints: Int, var vitalPoints: Int,
var transformationCountdown: Int, var transformationCountdown: Int,
@@ -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
) )
} }
@@ -1,15 +1,26 @@
package com.github.nacabaro.vbhelper.navigation package com.github.nacabaro.vbhelper.navigation
import android.util.Log
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable 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.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.navigation.compose.NavHost import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable 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.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
@@ -23,9 +34,12 @@ 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
import com.github.nacabaro.vbhelper.source.StorageRepository
data class AppNavigationHandlers( data class AppNavigationHandlers(
val settingsScreenController: SettingsScreenControllerImpl, val settingsScreenController: SettingsScreenControllerImpl,
@@ -34,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
@@ -51,8 +66,19 @@ fun AppNavigation(
NavHost( NavHost(
navController = navController, navController = navController,
startDestination = NavigationItems.Home.route, startDestination = NavigationItems.Home.route,
enterTransition = {
fadeIn(
animationSpec = tween(200)
)
},
exitTransition = {
fadeOut(
animationSpec = tween(200)
)
},
modifier = Modifier modifier = Modifier
.padding(contentPadding) .padding(contentPadding)
) { ) {
composable(NavigationItems.Battles.route) { composable(NavigationItems.Battles.route) {
BattlesScreen() BattlesScreen()
@@ -72,17 +98,35 @@ fun AppNavigation(
} }
composable(NavigationItems.Scan.route) { composable(NavigationItems.Scan.route) {
val characterIdString = it.arguments?.getString("characterId") val characterIdString = it.arguments?.getString("characterId")
val characterId = characterIdString?.toLongOrNull() var characterId by remember { mutableStateOf(characterIdString?.toLongOrNull()) }
Log.d("ScanScreen", "characterId: $characterId")
val launchedFromHomeScreen = (characterIdString?.toLongOrNull() == null)
if (characterId == null) {
val context = LocalContext.current.applicationContext as VBHelper
val storageRepository = StorageRepository(context.container.db)
LaunchedEffect(characterId) {
if (characterId == null) {
val characterData = storageRepository.getActiveCharacter()
if (characterData != null) {
characterId = characterData.id
}
}
}
}
ScanScreen( ScanScreen(
navController = navController, navController = navController,
scanScreenController = applicationNavigationHandlers.scanScreenController, scanScreenController = applicationNavigationHandlers.scanScreenController,
characterId = characterId characterId = characterId,
launchedFromHomeScreen = launchedFromHomeScreen
) )
} }
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) {
@@ -100,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()
) )
} }
} }
@@ -134,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,9 +1,33 @@
package com.github.nacabaro.vbhelper.screens package com.github.nacabaro.vbhelper.screens
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import com.github.nacabaro.vbhelper.components.TopBanner
@Composable @Composable
fun BattlesScreen() { fun BattlesScreen() {
Text("Battles Screen") Scaffold (
topBar = {
TopBanner(
text = "Online battles"
)
}
) { contentPadding ->
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
modifier = Modifier
.padding(top = contentPadding.calculateTopPadding())
.fillMaxSize()
) {
Text("Coming soon")
}
}
} }
@@ -1,84 +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.domain.characters.Card
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,12 +41,27 @@ 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 {
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() characterList.value = storageRepository.getAllCharacters()
} }
} }
}
}
LaunchedEffect (selectedCharacter) { LaunchedEffect (selectedCharacter) {
if (selectedCharacter != null) { if (selectedCharacter != null) {
@@ -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,11 +30,12 @@ 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
import com.github.nacabaro.vbhelper.utils.characterToNfc
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
@@ -46,45 +47,89 @@ fun ScanScreen(
navController: NavController, navController: NavController,
characterId: Long?, characterId: Long?,
scanScreenController: ScanScreenController, scanScreenController: ScanScreenController,
launchedFromHomeScreen: Boolean
) { ) {
val secrets by scanScreenController.secretsFlow.collectAsState(null) val secrets by scanScreenController.secretsFlow.collectAsState(null)
var readingScreen by remember { mutableStateOf(false) }
var writingScreen by remember { mutableStateOf(false) }
var isDoneReadingCharacter by remember { mutableStateOf(false) }
var isDoneSendingCard by remember { mutableStateOf(false) }
var isDoneWritingCharacter by remember { mutableStateOf(false) }
val application = LocalContext.current.applicationContext as VBHelper val application = LocalContext.current.applicationContext as VBHelper
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) {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
/*
First check if there is a character sent through the navigation system
If there is not, that means we got here through the home screen nfc button
If we got here through the home screen, it does not hurt to check if there is
an active character.
*/
if (characterId != null && nfcCharacter == null) { if (characterId != null && nfcCharacter == null) {
nfcCharacter = characterToNfc(context, characterId) nfcCharacter = scanScreenController.characterToNfc(characterId)
} }
} }
} }
DisposableEffect(readingScreen || writingScreen, isDoneSendingCard) { var readingScreen by remember { mutableStateOf(false) }
var writingScreen by remember { mutableStateOf(false) }
var cardSelectScreen by remember { mutableStateOf(false) }
var isDoneReadingCharacter by remember { mutableStateOf(false) }
var isDoneSendingCard by remember { mutableStateOf(false) }
var isDoneWritingCharacter by remember { mutableStateOf(false) }
DisposableEffect(readingScreen) {
if(readingScreen) { if(readingScreen) {
scanScreenController.registerActivityLifecycleListener(SCAN_SCREEN_ACTIVITY_LIFECYCLE_LISTENER, object: ActivityLifecycleListener { scanScreenController.registerActivityLifecycleListener(
SCAN_SCREEN_ACTIVITY_LIFECYCLE_LISTENER,
object: ActivityLifecycleListener {
override fun onPause() { override fun onPause() {
scanScreenController.cancelRead() scanScreenController.cancelRead()
} }
override fun onResume() { override fun onResume() {
scanScreenController.onClickRead(secrets!!) { scanScreenController.onClickRead(
secrets = secrets!!,
onComplete = {
isDoneReadingCharacter = true isDoneReadingCharacter = true
},
onMultipleCards = { cards ->
cardsRead = cards
readingScreen = false
cardSelectScreen = true
isDoneReadingCharacter = true
}
)
}
}
)
scanScreenController.onClickRead(
secrets = secrets!!,
onComplete = {
isDoneReadingCharacter = true
},
onMultipleCards = { cards ->
cardsRead = cards
readingScreen = false
cardSelectScreen = true
isDoneReadingCharacter = true
}
)
}
onDispose {
if(readingScreen) {
scanScreenController.unregisterActivityLifecycleListener(
SCAN_SCREEN_ACTIVITY_LIFECYCLE_LISTENER
)
scanScreenController.cancelRead()
}
} }
} }
}) DisposableEffect(writingScreen, isDoneSendingCard) {
scanScreenController.onClickRead(secrets!!) { if (writingScreen) {
isDoneReadingCharacter = true
}
} else if (writingScreen) {
scanScreenController.registerActivityLifecycleListener( scanScreenController.registerActivityLifecycleListener(
SCAN_SCREEN_ACTIVITY_LIFECYCLE_LISTENER, SCAN_SCREEN_ACTIVITY_LIFECYCLE_LISTENER,
object : ActivityLifecycleListener { object : ActivityLifecycleListener {
@@ -105,6 +150,9 @@ fun ScanScreen(
} }
} }
) )
}
if (secrets != null && nfcCharacter != null) {
if (!isDoneSendingCard) { if (!isDoneSendingCard) {
scanScreenController.onClickCheckCard(secrets!!, nfcCharacter!!) { scanScreenController.onClickCheckCard(secrets!!, nfcCharacter!!) {
isDoneSendingCard = true isDoneSendingCard = true
@@ -115,15 +163,16 @@ fun ScanScreen(
} }
} }
} }
onDispose { onDispose {
if(readingScreen || writingScreen) { if(writingScreen) {
scanScreenController.unregisterActivityLifecycleListener(SCAN_SCREEN_ACTIVITY_LIFECYCLE_LISTENER) scanScreenController.unregisterActivityLifecycleListener(SCAN_SCREEN_ACTIVITY_LIFECYCLE_LISTENER)
scanScreenController.cancelRead() scanScreenController.cancelRead()
} }
} }
} }
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) {
@@ -155,10 +204,18 @@ 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 {
characterId != null -> null !launchedFromHomeScreen -> null
else -> { else -> {
{ {
if(secrets == null) { if(secrets == null) {
@@ -184,7 +241,8 @@ fun ScanScreen(
} }
} }
} }
} },
navController = navController
) )
} }
} }
@@ -193,9 +251,17 @@ fun ScanScreen(
fun ChooseConnectOption( fun ChooseConnectOption(
onClickRead: (() -> Unit)? = null, onClickRead: (() -> Unit)? = null,
onClickWrite: (() -> Unit)? = null, onClickWrite: (() -> Unit)? = null,
navController: NavController
) { ) {
Scaffold( Scaffold(
topBar = { TopBanner(text = "Scan a Vital Bracelet") } topBar = {
TopBanner(
text = "Scan a Vital Bracelet",
onBackClick = {
navController.popBackStack()
}
)
}
) { contentPadding -> ) { contentPadding ->
Column( Column(
verticalArrangement = Arrangement.Center, verticalArrangement = Arrangement.Center,
@@ -255,11 +321,15 @@ 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, onMultipleCards: (List<Card>, NfcCharacter) -> Unit): String { return "" }
override suspend fun characterToNfc(characterId: Long): NfcCharacter? { return null }
}, },
characterId = null characterId = null,
launchedFromHomeScreen = false
) )
} }
@@ -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)
@@ -15,4 +16,12 @@ interface ScanScreenController {
fun registerActivityLifecycleListener(key: String, activityLifecycleListener: ActivityLifecycleListener) fun registerActivityLifecycleListener(key: String, activityLifecycleListener: ActivityLifecycleListener)
fun unregisterActivityLifecycleListener(key: String) fun unregisterActivityLifecycleListener(key: String)
fun flushCharacter(cardId: Long)
fun characterFromNfc(
nfcCharacter: NfcCharacter,
onMultipleCards: (List<Card>, NfcCharacter) -> Unit
): String
suspend fun characterToNfc(characterId: Long): NfcCharacter?
} }
@@ -15,18 +15,16 @@ 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.cfogrady.vbnfc.vb.VBNfcCharacter
import com.github.nacabaro.vbhelper.ActivityLifecycleListener import com.github.nacabaro.vbhelper.ActivityLifecycleListener
import com.github.nacabaro.vbhelper.di.VBHelper import com.github.nacabaro.vbhelper.domain.card.Card
import com.github.nacabaro.vbhelper.domain.device_data.BECharacterData import com.github.nacabaro.vbhelper.screens.scanScreen.converters.FromNfcConverter
import com.github.nacabaro.vbhelper.domain.device_data.UserCharacter import com.github.nacabaro.vbhelper.screens.scanScreen.converters.ToNfcConverter
import com.github.nacabaro.vbhelper.source.getCryptographicTransformerMap import com.github.nacabaro.vbhelper.source.getCryptographicTransformerMap
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
import com.github.nacabaro.vbhelper.utils.DeviceType
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.util.GregorianCalendar
class ScanScreenControllerImpl( class ScanScreenControllerImpl(
override val secretsFlow: Flow<Secrets>, override val secretsFlow: Flow<Secrets>,
@@ -34,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 {
@@ -46,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 = addCharacterScannedIntoDatabase(character) val resultMessage = characterFromNfc(character) { cards, nfcCharacter ->
lastScannedCharacter = nfcCharacter
onMultipleCards(cards)
}
onComplete.invoke() onComplete.invoke()
resultMessage resultMessage
} }
@@ -123,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) {
@@ -151,101 +161,36 @@ class ScanScreenControllerImpl(
componentActivity.startActivity(Intent(Settings.ACTION_WIRELESS_SETTINGS)) componentActivity.startActivity(Intent(Settings.ACTION_WIRELESS_SETTINGS))
} }
private fun addCharacterScannedIntoDatabase(nfcCharacter: NfcCharacter): String { override fun characterFromNfc(
val application = componentActivity.applicationContext as VBHelper nfcCharacter: NfcCharacter,
val storageRepository = application.container.db onMultipleCards: (List<Card>, NfcCharacter) -> Unit
): String {
val nfcConverter = FromNfcConverter(
componentActivity = componentActivity
)
return nfcConverter.addCharacter(nfcCharacter, onMultipleCards)
}
val dimData = storageRepository override suspend fun characterToNfc(characterId: Long): NfcCharacter {
.dimDao() val nfcGenerator = ToNfcConverter(
.getDimById(nfcCharacter.dimId.toInt()) componentActivity = componentActivity
if (dimData == null) return "Card not found"
val cardCharData = storageRepository
.characterDao()
.getCharacterByMonIndex(nfcCharacter.charIndex.toInt(), dimData.id)
val characterData = UserCharacter(
charId = cardCharData.id,
ageInDays = nfcCharacter.ageInDays.toInt(),
nextAdventureMissionStage = nfcCharacter.nextAdventureMissionStage.toInt(),
mood = nfcCharacter.mood.toInt(),
vitalPoints = nfcCharacter.vitalPoints.toInt(),
transformationCountdown = nfcCharacter.transformationCountdownInMinutes.toInt(),
injuryStatus = nfcCharacter.injuryStatus,
trophies = nfcCharacter.trophies.toInt(),
currentPhaseBattlesWon = nfcCharacter.currentPhaseBattlesWon.toInt(),
currentPhaseBattlesLost = nfcCharacter.currentPhaseBattlesLost.toInt(),
totalBattlesWon = nfcCharacter.totalBattlesWon.toInt(),
totalBattlesLost = nfcCharacter.totalBattlesLost.toInt(),
activityLevel = nfcCharacter.activityLevel.toInt(),
heartRateCurrent = nfcCharacter.heartRateCurrent.toInt(),
characterType = when (nfcCharacter) {
is BENfcCharacter -> DeviceType.BEDevice
else -> DeviceType.VBDevice
},
isActive = true
) )
storageRepository val character = nfcGenerator.characterToNfc(characterId)
.userCharacterDao() Log.d("CharacterType", character.toString())
.clearActiveCharacter() return character
}
val characterId: Long = storageRepository override fun flushCharacter(cardId: Long) {
.userCharacterDao() val nfcConverter = FromNfcConverter(
.insertCharacterData(characterData) componentActivity = componentActivity
if (nfcCharacter is BENfcCharacter) {
val extraCharacterData = BECharacterData(
id = characterId,
trainingHp = nfcCharacter.trainingHp.toInt(),
trainingAp = nfcCharacter.trainingAp.toInt(),
trainingBp = nfcCharacter.trainingBp.toInt(),
remainingTrainingTimeInMinutes = nfcCharacter.remainingTrainingTimeInMinutes.toInt(),
itemEffectActivityLevelValue = nfcCharacter.itemEffectActivityLevelValue.toInt(),
itemEffectMentalStateValue = nfcCharacter.itemEffectMentalStateValue.toInt(),
itemEffectMentalStateMinutesRemaining = nfcCharacter.itemEffectMentalStateMinutesRemaining.toInt(),
itemEffectActivityLevelMinutesRemaining = nfcCharacter.itemEffectActivityLevelMinutesRemaining.toInt(),
itemEffectVitalPointsChangeValue = nfcCharacter.itemEffectVitalPointsChangeValue.toInt(),
itemEffectVitalPointsChangeMinutesRemaining = nfcCharacter.itemEffectVitalPointsChangeMinutesRemaining.toInt(),
abilityRarity = nfcCharacter.abilityRarity,
abilityType = nfcCharacter.abilityType.toInt(),
abilityBranch = nfcCharacter.abilityBranch.toInt(),
abilityReset = nfcCharacter.abilityReset.toInt(),
rank = nfcCharacter.abilityReset.toInt(),
itemType = nfcCharacter.itemType.toInt(),
itemMultiplier = nfcCharacter.itemMultiplier.toInt(),
itemRemainingTime = nfcCharacter.itemRemainingTime.toInt(),
otp0 = "", //nfcCharacter.value!!.otp0.toString(),
otp1 = "", //nfcCharacter.value!!.otp1.toString(),
minorVersion = nfcCharacter.characterCreationFirmwareVersion.minorVersion.toInt(),
majorVersion = nfcCharacter.characterCreationFirmwareVersion.majorVersion.toInt(),
) )
storageRepository componentActivity.lifecycleScope.launch(Dispatchers.IO) {
.userCharacterDao() if (lastScannedCharacter != null) {
.insertBECharacterData(extraCharacterData) nfcConverter.addCharacterUsingCard(lastScannedCharacter!!, cardId)
lastScannedCharacter = null
val transformationHistoryWatch = nfcCharacter.transformationHistory
transformationHistoryWatch.map { item ->
if (item.toCharIndex.toInt() != 255) {
val date = GregorianCalendar(item.year.toInt(), item.month.toInt(), item.day.toInt())
.time
.time
storageRepository
.characterDao()
.insertTransformation(characterId, item.toCharIndex.toInt(), dimData.id, date)
storageRepository
.dexDao()
.insertCharacter(item.toCharIndex.toInt(), dimData.id, date)
} }
} }
} else if (nfcCharacter is VBNfcCharacter) {
return "Not implemented yet"
}
return "Done reading character!"
} }
} }
@@ -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
)
} }
} }
} }
@@ -0,0 +1,306 @@
package com.github.nacabaro.vbhelper.screens.scanScreen.converters
import android.util.Log
import androidx.activity.ComponentActivity
import com.github.cfogrady.vbnfc.be.BENfcCharacter
import com.github.cfogrady.vbnfc.data.NfcCharacter
import com.github.cfogrady.vbnfc.vb.VBNfcCharacter
import com.github.nacabaro.vbhelper.di.VBHelper
import com.github.nacabaro.vbhelper.domain.card.Card
import com.github.nacabaro.vbhelper.domain.card.CardProgress
import com.github.nacabaro.vbhelper.domain.device_data.BECharacterData
import com.github.nacabaro.vbhelper.domain.device_data.SpecialMissions
import com.github.nacabaro.vbhelper.domain.device_data.UserCharacter
import com.github.nacabaro.vbhelper.domain.device_data.VBCharacterData
import com.github.nacabaro.vbhelper.domain.device_data.VitalsHistory
import com.github.nacabaro.vbhelper.utils.DeviceType
import java.util.GregorianCalendar
class FromNfcConverter (
componentActivity: ComponentActivity
) {
private val application = componentActivity.applicationContext as VBHelper
private val database = application.container.db
fun addCharacterUsingCard(
nfcCharacter: NfcCharacter,
cardId: Long
): String {
val cardData = database
.cardDao()
.getCardById(cardId)
if (cardData == null) {
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
.characterDao()
.getCharacterByMonIndex(nfcCharacter.charIndex.toInt(), cardData.id)
updateCardProgress(nfcCharacter, cardData)
val characterData = UserCharacter(
charId = cardCharData.id,
ageInDays = nfcCharacter.ageInDays.toInt(),
mood = nfcCharacter.mood.toInt(),
vitalPoints = nfcCharacter.vitalPoints.toInt(),
transformationCountdown = nfcCharacter.transformationCountdownInMinutes.toInt(),
injuryStatus = nfcCharacter.injuryStatus,
trophies = nfcCharacter.trophies.toInt(),
currentPhaseBattlesWon = nfcCharacter.currentPhaseBattlesWon.toInt(),
currentPhaseBattlesLost = nfcCharacter.currentPhaseBattlesLost.toInt(),
totalBattlesWon = nfcCharacter.totalBattlesWon.toInt(),
totalBattlesLost = nfcCharacter.totalBattlesLost.toInt(),
activityLevel = nfcCharacter.activityLevel.toInt(),
heartRateCurrent = nfcCharacter.heartRateCurrent.toInt(),
characterType = when (nfcCharacter) {
is BENfcCharacter -> DeviceType.BEDevice
else -> DeviceType.VBDevice
},
isActive = true
)
database
.userCharacterDao()
.clearActiveCharacter()
val characterId: Long = database
.userCharacterDao()
.insertCharacterData(characterData)
if (nfcCharacter is BENfcCharacter) {
addBeCharacterToDatabase(
characterId = characterId,
nfcCharacter = nfcCharacter
)
} else if (nfcCharacter is VBNfcCharacter) {
addVbCharacterToDatabase(
characterId = characterId,
nfcCharacter = nfcCharacter
)
}
addTransformationHistoryToDatabase(
characterId = characterId,
nfcCharacter = nfcCharacter,
dimData = cardData
)
addVitalsHistoryToDatabase(
characterId = characterId,
nfcCharacter = nfcCharacter
)
return "Done reading character!"
}
private fun updateCardProgress(
nfcCharacter: NfcCharacter,
cardData: Card
) {
val currentCardProgress = CardProgress(
cardId = cardData.id,
currentStage = nfcCharacter.nextAdventureMissionStage.toInt(),
unlocked = nfcCharacter.nextAdventureMissionStage.toInt() > cardData.stageCount
)
database
.cardProgressDao()
.updateDimProgress(currentCardProgress)
}
private fun addVbCharacterToDatabase(
characterId: Long,
nfcCharacter: VBNfcCharacter
) {
val extraCharacterData = VBCharacterData(
id = characterId,
generation = nfcCharacter.generation.toInt(),
totalTrophies = nfcCharacter.totalTrophies.toInt()
)
database
.userCharacterDao()
.insertVBCharacterData(extraCharacterData)
addSpecialMissionsToDatabase(nfcCharacter, characterId)
}
private fun addSpecialMissionsToDatabase(
nfcCharacter: VBNfcCharacter,
characterId: Long
) {
val specialMissionsWatch = nfcCharacter.specialMissions
val specialMissionsDb = specialMissionsWatch.map { item ->
SpecialMissions(
characterId = characterId,
goal = item.goal.toInt(),
watchId = item.id.toInt(),
progress = item.progress.toInt(),
status = item.status,
timeElapsedInMinutes = item.timeElapsedInMinutes.toInt(),
timeLimitInMinutes = item.timeLimitInMinutes.toInt(),
missionType = item.type,
)
}
database
.userCharacterDao()
.insertSpecialMissions(*specialMissionsDb.toTypedArray())
}
private fun addBeCharacterToDatabase(
characterId: Long,
nfcCharacter: BENfcCharacter
) {
val extraCharacterData = BECharacterData(
id = characterId,
trainingHp = nfcCharacter.trainingHp.toInt(),
trainingAp = nfcCharacter.trainingAp.toInt(),
trainingBp = nfcCharacter.trainingBp.toInt(),
remainingTrainingTimeInMinutes = nfcCharacter.remainingTrainingTimeInMinutes.toInt(),
itemEffectActivityLevelValue = nfcCharacter.itemEffectActivityLevelValue.toInt(),
itemEffectMentalStateValue = nfcCharacter.itemEffectMentalStateValue.toInt(),
itemEffectMentalStateMinutesRemaining = nfcCharacter.itemEffectMentalStateMinutesRemaining.toInt(),
itemEffectActivityLevelMinutesRemaining = nfcCharacter.itemEffectActivityLevelMinutesRemaining.toInt(),
itemEffectVitalPointsChangeValue = nfcCharacter.itemEffectVitalPointsChangeValue.toInt(),
itemEffectVitalPointsChangeMinutesRemaining = nfcCharacter.itemEffectVitalPointsChangeMinutesRemaining.toInt(),
abilityRarity = nfcCharacter.abilityRarity,
abilityType = nfcCharacter.abilityType.toInt(),
abilityBranch = nfcCharacter.abilityBranch.toInt(),
abilityReset = nfcCharacter.abilityReset.toInt(),
rank = nfcCharacter.abilityReset.toInt(),
itemType = nfcCharacter.itemType.toInt(),
itemMultiplier = nfcCharacter.itemMultiplier.toInt(),
itemRemainingTime = nfcCharacter.itemRemainingTime.toInt(),
otp0 = "", //nfcCharacter.value!!.otp0.toString(),
otp1 = "", //nfcCharacter.value!!.otp1.toString(),
minorVersion = nfcCharacter.characterCreationFirmwareVersion.minorVersion.toInt(),
majorVersion = nfcCharacter.characterCreationFirmwareVersion.majorVersion.toInt(),
)
database
.userCharacterDao()
.insertBECharacterData(extraCharacterData)
}
private fun addVitalsHistoryToDatabase(
characterId: Long,
nfcCharacter: NfcCharacter
) {
val vitalsHistoryWatch = nfcCharacter.vitalHistory
val vitalsHistory = vitalsHistoryWatch.map { historyElement ->
Log.d("VitalsHistory", "${historyElement.year.toInt()} ${historyElement.month.toInt()} ${historyElement.day.toInt()}")
VitalsHistory(
charId = characterId,
year = historyElement.year.toInt(),
month = historyElement.month.toInt(),
day = historyElement.day.toInt(),
vitalPoints = historyElement.vitalsGained.toInt()
)
}
database
.userCharacterDao()
.insertVitals(*vitalsHistory.toTypedArray())
}
private fun addTransformationHistoryToDatabase(
characterId: Long,
nfcCharacter: NfcCharacter,
dimData: Card
) {
val transformationHistoryWatch = nfcCharacter.transformationHistory
transformationHistoryWatch.map { item ->
if (item.toCharIndex.toInt() != 255) {
val date = GregorianCalendar(
item.year.toInt(),
item.month.toInt(),
item.day.toInt()
)
.time
.time
database
.userCharacterDao()
.insertTransformation(
characterId,
item.toCharIndex.toInt(),
dimData.id,
date
)
database
.dexDao()
.insertCharacter(
item.toCharIndex.toInt(),
dimData.id,
date
)
}
}
}
}
@@ -0,0 +1,298 @@
package com.github.nacabaro.vbhelper.screens.scanScreen.converters
import android.icu.util.Calendar
import android.icu.util.TimeZone
import android.util.Log
import androidx.activity.ComponentActivity
import com.github.cfogrady.vbnfc.be.BENfcCharacter
import com.github.cfogrady.vbnfc.be.FirmwareVersion
import com.github.cfogrady.vbnfc.data.NfcCharacter
import com.github.cfogrady.vbnfc.vb.SpecialMission
import com.github.cfogrady.vbnfc.vb.VBNfcCharacter
import com.github.nacabaro.vbhelper.database.AppDatabase
import com.github.nacabaro.vbhelper.di.VBHelper
import com.github.nacabaro.vbhelper.domain.device_data.UserCharacter
import com.github.nacabaro.vbhelper.dtos.CharacterDtos
import com.github.nacabaro.vbhelper.utils.DeviceType
import java.util.Date
class ToNfcConverter(
private val componentActivity: ComponentActivity
) {
private val application: VBHelper = componentActivity.applicationContext as VBHelper
private val database: AppDatabase = application.container.db
suspend fun characterToNfc(
characterId: Long
): NfcCharacter {
val app = componentActivity.applicationContext as VBHelper
val database = app.container.db
val userCharacter = database
.userCharacterDao()
.getCharacter(characterId)
val characterInfo = database
.characterDao()
.getCharacterInfo(userCharacter.charId)
val currentCardStage = database
.cardProgressDao()
.getCardProgress(characterInfo.cardId)
return if (userCharacter.characterType == DeviceType.BEDevice)
nfcToBENfc(characterId, characterInfo, currentCardStage, userCharacter)
else
nfcToVBNfc(characterId, characterInfo, currentCardStage, userCharacter)
}
private suspend fun nfcToVBNfc(
characterId: Long,
characterInfo: CharacterDtos.CardCharacterInfo,
currentCardStage: Int,
userCharacter: UserCharacter
): VBNfcCharacter {
val vbData = database
.userCharacterDao()
.getVbData(characterId)
val paddedTransformationArray = generateTransformationHistory(characterId, 9)
val watchSpecialMissions = generateSpecialMissionsArray(characterId)
val nfcData = VBNfcCharacter(
dimId = characterInfo.cardId.toUShort(),
charIndex = characterInfo.charId.toUShort(),
stage = characterInfo.stage.toByte(),
attribute = characterInfo.attribute,
ageInDays = userCharacter.ageInDays.toByte(),
nextAdventureMissionStage = currentCardStage.toByte(),
mood = userCharacter.mood.toByte(),
vitalPoints = userCharacter.vitalPoints.toUShort(),
transformationCountdownInMinutes = userCharacter.transformationCountdown.toUShort(),
injuryStatus = userCharacter.injuryStatus,
trophies = userCharacter.trophies.toUShort(),
currentPhaseBattlesWon = userCharacter.currentPhaseBattlesWon.toUShort(),
currentPhaseBattlesLost = userCharacter.currentPhaseBattlesLost.toUShort(),
totalBattlesWon = userCharacter.totalBattlesWon.toUShort(),
totalBattlesLost = userCharacter.totalBattlesLost.toUShort(),
activityLevel = userCharacter.activityLevel.toByte(),
heartRateCurrent = userCharacter.heartRateCurrent.toUByte(),
transformationHistory = paddedTransformationArray,
vitalHistory = generateVitalsHistoryArray(characterId),
appReserved1 = ByteArray(12) {0},
appReserved2 = generateUShortAppReserved(userCharacter),
generation = vbData.generation.toUShort(),
totalTrophies = vbData.totalTrophies.toUShort(),
specialMissions = watchSpecialMissions.toTypedArray()
)
return nfcData
}
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(
characterId: Long
): List<SpecialMission> {
val specialMissions = database
.userCharacterDao()
.getSpecialMissions(characterId)
val watchSpecialMissions = specialMissions.map {
SpecialMission(
goal = it.goal.toUShort(),
id = it.watchId.toUShort(),
progress = it.progress.toUShort(),
status = it.status,
timeElapsedInMinutes = it.timeElapsedInMinutes.toUShort(),
timeLimitInMinutes = it.timeLimitInMinutes.toUShort(),
type = it.missionType
)
}
return watchSpecialMissions
}
private suspend fun generateVitalsHistoryArray(
characterId: Long
): Array<NfcCharacter.DailyVitals> {
val vitalsHistory = database
.userCharacterDao()
.getVitalsHistory(characterId)
val nfcVitalsHistory = Array(7) {
NfcCharacter.DailyVitals(0u, 0u, 0u, 0u)
}
vitalsHistory.mapIndexed { index, historyElement ->
var actualYear = 0
if (historyElement.year != 2000) {
actualYear = historyElement.year
}
nfcVitalsHistory[index] = NfcCharacter.DailyVitals(
day = historyElement.day.toUByte(),
month = historyElement.month.toUByte(),
year = actualYear.toUShort(),
vitalsGained = vitalsHistory[index].vitalPoints.toUShort()
)
}
nfcVitalsHistory.map {
Log.d("NFC", it.toString())
}
return nfcVitalsHistory
}
private suspend fun nfcToBENfc(
characterId: Long,
characterInfo: CharacterDtos.CardCharacterInfo,
currentCardStage: Int,
userCharacter: UserCharacter
): BENfcCharacter {
val beData = database
.userCharacterDao()
.getBeData(characterId)
val paddedTransformationArray = generateTransformationHistory(characterId)
val nfcData = BENfcCharacter(
dimId = characterInfo.cardId.toUShort(),
charIndex = characterInfo.charId.toUShort(),
stage = characterInfo.stage.toByte(),
attribute = characterInfo.attribute,
ageInDays = userCharacter.ageInDays.toByte(),
nextAdventureMissionStage = currentCardStage.toByte(),
mood = userCharacter.mood.toByte(),
vitalPoints = userCharacter.vitalPoints.toUShort(),
itemEffectMentalStateValue = beData.itemEffectMentalStateValue.toByte(),
itemEffectMentalStateMinutesRemaining = beData.itemEffectMentalStateMinutesRemaining.toByte(),
itemEffectActivityLevelValue = beData.itemEffectActivityLevelValue.toByte(),
itemEffectActivityLevelMinutesRemaining = beData.itemEffectActivityLevelMinutesRemaining.toByte(),
itemEffectVitalPointsChangeValue = beData.itemEffectVitalPointsChangeValue.toByte(),
itemEffectVitalPointsChangeMinutesRemaining = beData.itemEffectVitalPointsChangeMinutesRemaining.toByte(),
transformationCountdownInMinutes = userCharacter.transformationCountdown.toUShort(),
injuryStatus = userCharacter.injuryStatus,
trainingPp = userCharacter.trophies.toUShort(),
currentPhaseBattlesWon = userCharacter.currentPhaseBattlesWon.toUShort(),
currentPhaseBattlesLost = userCharacter.currentPhaseBattlesLost.toUShort(),
totalBattlesWon = userCharacter.totalBattlesWon.toUShort(),
totalBattlesLost = userCharacter.totalBattlesLost.toUShort(),
activityLevel = userCharacter.activityLevel.toByte(),
heartRateCurrent = userCharacter.heartRateCurrent.toUByte(),
transformationHistory = paddedTransformationArray,
vitalHistory = generateVitalsHistoryArray(characterId),
appReserved1 = ByteArray(12) {0},
appReserved2 = Array(3) {0u},
trainingHp = beData.trainingHp.toUShort(),
trainingAp = beData.trainingAp.toUShort(),
trainingBp = beData.trainingBp.toUShort(),
remainingTrainingTimeInMinutes = beData.remainingTrainingTimeInMinutes.toUShort(),
abilityRarity = beData.abilityRarity,
abilityType = beData.abilityType.toUShort(),
abilityBranch = beData.abilityBranch.toUShort(),
abilityReset = beData.abilityReset.toByte(),
rank = beData.rank.toByte(),
itemType = beData.itemType.toByte(),
itemMultiplier = beData.itemMultiplier.toByte(),
itemRemainingTime = beData.itemRemainingTime.toByte(),
otp0 = byteArrayOf(8),
otp1 = byteArrayOf(8),
characterCreationFirmwareVersion = FirmwareVersion(
minorVersion = beData.minorVersion.toByte(),
majorVersion = beData.majorVersion.toByte()
)
)
return nfcData
}
private suspend fun generateTransformationHistory(
characterId: Long,
length: Int = 8
): Array<NfcCharacter.Transformation> {
val transformationHistory = database
.userCharacterDao()
.getTransformationHistory(characterId)!!
.map {
val date = Date(it.transformationDate)
val calendar = android.icu.util.GregorianCalendar(TimeZone.getTimeZone("UTC"))
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(
toCharIndex = it.monIndex.toUByte(),
year = calendar
.get(Calendar.YEAR)
.toUShort(),
month = (calendar
.get(Calendar.MONTH) + 1)
.toUByte(),
day = calendar
.get(Calendar.DAY_OF_MONTH)
.toUByte()
)
}.toTypedArray()
val paddedTransformationArray = padTransformationArray(transformationHistory, length)
return paddedTransformationArray
}
private fun padTransformationArray(
transformationArray: Array<NfcCharacter.Transformation>,
length: Int
): Array<NfcCharacter.Transformation> {
if (transformationArray.size >= 8) {
return transformationArray
}
val paddedArray = Array(length) {
NfcCharacter.Transformation(
toCharIndex = 255u,
year = 65535u,
month = 255u,
day = 255u
)
}
System.arraycopy(transformationArray, 0, paddedArray, 0, transformationArray.size)
return paddedArray
}
}
@@ -35,6 +35,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.") { }
} }
} }
} }
@@ -16,8 +16,9 @@ import com.github.cfogrady.vbnfc.data.NfcCharacter
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.characters.Sprite import com.github.nacabaro.vbhelper.domain.characters.Sprite
import com.github.nacabaro.vbhelper.domain.characters.Card import com.github.nacabaro.vbhelper.domain.card.Card
import com.github.nacabaro.vbhelper.domain.characters.Character import com.github.nacabaro.vbhelper.domain.card.CardProgress
import com.github.nacabaro.vbhelper.domain.card.CardCharacter
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
@@ -108,40 +109,97 @@ class SettingsScreenControllerImpl(
filePickerCard.launch(arrayOf("*/*")) filePickerCard.launch(arrayOf("*/*"))
} }
private fun importCard(uri: Uri) { private suspend fun importEvoData(
context.lifecycleScope.launch(Dispatchers.IO) { cardId: Long,
val contentResolver = context.contentResolver card: com.github.cfogrady.vb.dim.card.Card<*, *, *, *, *, *>
val inputStream = contentResolver.openInputStream(uri) ) {
inputStream.use { fileReader -> for (index in 0 until card.transformationRequirements.transformationEntries.size) {
val dimReader = DimReader() val evo = card.transformationRequirements.transformationEntries[index]
val card = dimReader.readCard(fileReader, false)
val cardModel = Card( var transformationTimerHours: Int
dimId = card.header.dimId, var unlockAdventureLevel: Int
logo = card.spriteData.sprites[0].pixelData,
name = card.spriteData.text, // TODO Make user write card name// TODO Make user write card name if (card is BemCard) {
stageCount = card.adventureLevels.levels.size, transformationTimerHours = card
logoHeight = card.spriteData.sprites[0].height, .transformationRequirements
logoWidth = card.spriteData.sprites[0].width, .transformationEntries[index]
isBEm = card is BemCard .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
) )
}
}
val dimId = database private suspend fun importCharacterData(
.dimDao() cardId: Long,
.insertNewDim(cardModel) card: com.github.cfogrady.vb.dim.card.Card<*, *, *, *, *, *>
) {
val characters = card.characterStats.characterEntries
var spriteCounter = when (card is BemCard) { var spriteCounter = when (card is BemCard) {
true -> 54 true -> 54
false -> 10 false -> 10
} }
val domainCharacters = mutableListOf<Character>() val domainCharacters = mutableListOf<CardCharacter>()
val characters = card
.characterStats
.characterEntries
for (index in 0 until characters.size) { for (index in 0 until characters.size) {
var domainSprite: Sprite? = null; var domainSprite: Sprite?
if (index < 2 && card is DimCard) { if (index < 2 && card is DimCard) {
domainSprite = Sprite( domainSprite = Sprite(
width = card.spriteData.sprites[spriteCounter + 1].spriteDimensions.width, width = card.spriteData.sprites[spriteCounter + 1].spriteDimensions.width,
@@ -178,24 +236,24 @@ class SettingsScreenControllerImpl(
) )
} }
val spriteId = database val spriteId = database
.spriteDao() .spriteDao()
.insertSprite(domainSprite) .insertSprite(domainSprite)
domainCharacters.add( domainCharacters.add(
Character( CardCharacter(
dimId = dimId, cardId = cardId,
spriteId = spriteId, spriteId = spriteId,
monIndex = index, charaIndex = index,
name = card.spriteData.sprites[spriteCounter].pixelData, nameSprite = card.spriteData.sprites[spriteCounter].pixelData,
stage = characters[index].stage, stage = characters[index].stage,
attribute = NfcCharacter.Attribute.entries[characters[index].attribute], attribute = NfcCharacter.Attribute.entries[characters[index].attribute],
baseHp = characters[index].hp, baseHp = characters[index].hp,
baseBp = characters[index].dp, baseBp = characters[index].dp,
baseAp = characters[index].ap, baseAp = characters[index].ap,
nameWidth = card.spriteData.sprites[spriteCounter].spriteDimensions.width, nameWidth = card.spriteData.sprites[spriteCounter].spriteDimensions.width,
nameHeight = card.spriteData.sprites[spriteCounter].spriteDimensions.height, nameHeight = card.spriteData.sprites[spriteCounter].spriteDimensions.height
) )
) )
@@ -215,6 +273,114 @@ class SettingsScreenControllerImpl(
.insertCharacter(*domainCharacters.toTypedArray()) .insertCharacter(*domainCharacters.toTypedArray())
} }
private suspend fun importAdventureMissions(
cardId: Long,
card: com.github.cfogrady.vb.dim.card.Card<*, *, *, *, *, *>
) {
Log.d("importAdventureMissions", "Importing adventure missions")
if (card is BemCard) {
card.adventureLevels.levels.forEach {
database
.cardAdventureDao()
.insertNewAdventure(
cardId = cardId,
characterId = it.bossCharacterIndex,
steps = it.steps,
bossAp = it.bossAp,
bossHp = it.bossHp,
bossDp = it.bossDp,
bossBp = it.bossBp
)
}
} else if (card is DimCard) {
card.adventureLevels.levels.map {
database
.cardAdventureDao()
.insertNewAdventure(
cardId = cardId,
characterId = it.bossCharacterIndex,
steps = it.steps,
bossAp = it.bossAp,
bossHp = it.bossHp,
bossDp = it.bossDp,
bossBp = null
)
}
}
}
private suspend fun importCardFusions(
cardId: Long,
card: com.github.cfogrady.vb.dim.card.Card<*, *, *, *, *, *>
) {
Log.d("importCardFusions", "Importing card fusions")
if (card is DimCard) {
card
.attributeFusions
.entries
.forEach {
database
.cardFusionsDao()
.insertNewFusion(
cardId = cardId,
fromCharaId = it.characterIndex,
toCharaIdAttr1 = it.attribute1Fusion,
toCharaIdAttr2 = it.attribute2Fusion,
toCharaIdAttr3 = it.attribute3Fusion,
toCharaIdAttr4 = it.attribute4Fusion
)
}
}
}
private fun updateCardProgress(
cardId: Long,
) {
val cardProgress = CardProgress(
cardId = cardId,
currentStage = 0,
unlocked = false
)
database
.cardProgressDao()
.updateDimProgress(cardProgress)
}
private fun importCard(uri: Uri) {
context.lifecycleScope.launch(Dispatchers.IO) {
val contentResolver = context.contentResolver
val inputStream = contentResolver.openInputStream(uri)
inputStream.use { fileReader ->
val dimReader = DimReader()
val card = dimReader.readCard(fileReader, false)
val cardModel = Card(
cardId = card.header.dimId,
logo = card.spriteData.sprites[0].pixelData,
name = card.spriteData.text,
stageCount = card.adventureLevels.levels.size,
logoHeight = card.spriteData.sprites[0].height,
logoWidth = card.spriteData.sprites[0].width,
isBEm = card is BemCard
)
val cardId = database
.cardDao()
.insertNewCard(cardModel)
updateCardProgress(cardId = cardId)
importCharacterData(cardId, card)
importEvoData(cardId, card)
importAdventureMissions(cardId, card)
importCardFusions(cardId, card)
}
inputStream?.close() inputStream?.close()
context.runOnUiThread { context.runOnUiThread {
Toast.makeText(context, "Import successful!", Toast.LENGTH_SHORT).show() Toast.makeText(context, "Import successful!", Toast.LENGTH_SHORT).show()
@@ -1,7 +1,6 @@
package com.github.nacabaro.vbhelper.source 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.characters.Card
import com.github.nacabaro.vbhelper.dtos.CardDtos import com.github.nacabaro.vbhelper.dtos.CardDtos
import com.github.nacabaro.vbhelper.dtos.CharacterDtos import com.github.nacabaro.vbhelper.dtos.CharacterDtos
@@ -12,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
@@ -19,12 +22,20 @@ class StorageRepository (
return db.userCharacterDao().getBeData(id) return db.userCharacterDao().getBeData(id)
} }
fun getTransformationHistory(characterId: Long): List<CharacterDtos.TransformationHistory>? { suspend fun getTransformationHistory(characterId: Long): List<CharacterDtos.TransformationHistory>? {
return db.userCharacterDao().getTransformationHistory(characterId) return db.userCharacterDao().getTransformationHistory(characterId)
} }
suspend fun getCharacterData(id: Long): CharacterDtos.DiMInfo { suspend fun getCharacterVbData(id: Long): VBCharacterData {
return db.characterDao().getCharacterInfo(id) 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? {
@@ -38,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)
} }
@@ -1,99 +0,0 @@
package com.github.nacabaro.vbhelper.utils
import android.content.Context
import android.icu.util.Calendar
import android.icu.util.GregorianCalendar
import com.github.cfogrady.vbnfc.be.BENfcCharacter
import com.github.cfogrady.vbnfc.be.FirmwareVersion
import com.github.cfogrady.vbnfc.data.NfcCharacter
import com.github.nacabaro.vbhelper.di.VBHelper
import com.github.nacabaro.vbhelper.source.StorageRepository
import java.util.Date
suspend fun characterToNfc(context: Context, characterId: Long): NfcCharacter? {
val app = context.applicationContext as VBHelper
val database = app.container.db
val storageRepository = StorageRepository(database)
val userCharacter = storageRepository.getSingleCharacter(characterId)
val characterInfo = storageRepository.getCharacterData(characterId)
if (userCharacter.characterType == DeviceType.BEDevice) {
val beData = storageRepository.getCharacterBeData(characterId)
val transformationHistory = storageRepository
.getTransformationHistory(characterId)!!
.map {
val date = Date(it.transformationDate)
val calendar = GregorianCalendar()
calendar.time = date
NfcCharacter.Transformation(
toCharIndex = it.monIndex.toUByte(),
year = calendar
.get(Calendar.YEAR)
.toUShort(),
month = calendar
.get(Calendar.MONTH)
.toUByte(),
day = calendar
.get(Calendar.DAY_OF_MONTH)
.toUByte()
)
}.toTypedArray()
val paddedTransformationArray = padTransformationArray(transformationHistory)
val nfcData = BENfcCharacter(
dimId = characterInfo.cardId.toUShort(),
charIndex = characterInfo.charId.toUShort(),
stage = userCharacter.stage.toByte(),
attribute = userCharacter.attribute,
ageInDays = userCharacter.ageInDays.toByte(),
nextAdventureMissionStage = userCharacter.nextAdventureMissionStage.toByte(),
mood = userCharacter.mood.toByte(),
vitalPoints = userCharacter.vitalPoints.toUShort(),
itemEffectMentalStateValue = beData.itemEffectMentalStateValue.toByte(),
itemEffectMentalStateMinutesRemaining = beData.itemEffectMentalStateMinutesRemaining.toByte(),
itemEffectActivityLevelValue = beData.itemEffectActivityLevelValue.toByte(),
itemEffectActivityLevelMinutesRemaining = beData.itemEffectActivityLevelMinutesRemaining.toByte(),
itemEffectVitalPointsChangeValue = beData.itemEffectVitalPointsChangeValue.toByte(),
itemEffectVitalPointsChangeMinutesRemaining = beData.itemEffectVitalPointsChangeMinutesRemaining.toByte(),
transformationCountdownInMinutes = userCharacter.transformationCountdown.toUShort(),
injuryStatus = userCharacter.injuryStatus,
trainingPp = userCharacter.trophies.toUShort(),
currentPhaseBattlesWon = userCharacter.currentPhaseBattlesWon.toUShort(),
currentPhaseBattlesLost = userCharacter.currentPhaseBattlesLost.toUShort(),
totalBattlesWon = userCharacter.totalBattlesWon.toUShort(),
totalBattlesLost = userCharacter.totalBattlesLost.toUShort(),
activityLevel = userCharacter.activityLevel.toByte(),
heartRateCurrent = userCharacter.heartRateCurrent.toUByte(),
transformationHistory = paddedTransformationArray,
vitalHistory = Array(7) {
NfcCharacter.DailyVitals(0u, 0u, 0u, 0u)
},
appReserved1 = ByteArray(12) {0},
appReserved2 = Array(3) {0u},
trainingHp = beData.trainingHp.toUShort(),
trainingAp = beData.trainingAp.toUShort(),
trainingBp = beData.trainingBp.toUShort(),
remainingTrainingTimeInMinutes = beData.remainingTrainingTimeInMinutes.toUShort(),
abilityRarity = beData.abilityRarity,
abilityType = beData.abilityType.toUShort(),
abilityBranch = beData.abilityBranch.toUShort(),
abilityReset = beData.abilityReset.toByte(),
rank = beData.rank.toByte(),
itemType = beData.itemType.toByte(),
itemMultiplier = beData.itemMultiplier.toByte(),
itemRemainingTime = beData.itemRemainingTime.toByte(),
otp0 = byteArrayOf(8),
otp1 = byteArrayOf(8),
characterCreationFirmwareVersion = FirmwareVersion(
minorVersion = beData.minorVersion.toByte(),
majorVersion = beData.majorVersion.toByte()
)
)
return nfcData
}
return null
}
@@ -1,23 +0,0 @@
package com.github.nacabaro.vbhelper.utils
import com.github.cfogrady.vbnfc.data.NfcCharacter
fun padTransformationArray(
transformationArray: Array<NfcCharacter.Transformation>
): Array<NfcCharacter.Transformation> {
if (transformationArray.size >= 8) {
return transformationArray
}
val paddedArray = Array(8) {
NfcCharacter.Transformation(
toCharIndex = 255u,
year = 65535u,
month = 255u,
day = 255u
)
}
System.arraycopy(transformationArray, 0, paddedArray, 0, transformationArray.size)
return paddedArray
}
@@ -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">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path <path
android:fillColor="#FFFFFF" android:pathData="M0,0h512v512h-512z"
android:fillType="nonZero" android:fillColor="#000000"/>
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" <path
android:strokeWidth="1" 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:fillColor="#EBEBEB"/>
<path
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"
android:fillColor="#000000"
android:strokeColor="#00000000"/> android:strokeColor="#00000000"/>
<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:fillColor="#EBEBEB"/>
<group>
<clip-path
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"/>
<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> </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.2"
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