mirror of
https://github.com/nacabaro/vbhelper.git
synced 2026-06-05 13:52:54 +00:00
Merge remote-tracking branch 'upstream/main'
This commit is contained in:
commit
44c6382356
101
README.md
101
README.md
@ -1,38 +1,69 @@
|
||||
# VBHelper
|
||||
|
||||
## Developer Setup
|
||||
Application to interact with the Vital series, VB, VH, VBC and VBBE.
|
||||
|
||||
1. Clone vb-nfc-reader (https://github.com/cfogrady/lib-vb-nfc)
|
||||
2. Run vb-nfc-reader/publishToMavenLocal gradle task in the lib-vb-nfc project.
|
||||
3. Clone vb-dim-reader (https://github.com/cfogrady/vb-dim-reader)
|
||||
4. Run publishToMavenLocal gradle task in the vb-dim-reader project.
|
||||
5. Create res/values/keys.xml within the app module.
|
||||
6. Populate with:
|
||||
```
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="password1">beHmacKey1</string>
|
||||
<string name="password2">beHmacKey2</string>
|
||||
<string name="decryptionKey">aesKey</string>
|
||||
<integer-array name="substitutionArray">
|
||||
<item>0</item>
|
||||
<item>1</item>
|
||||
<item>2</item>
|
||||
<item>3</item>
|
||||
<item>4</item>
|
||||
<item>5</item>
|
||||
<item>6</item>
|
||||
<item>7</item>
|
||||
<item>8</item>
|
||||
<item>9</item>
|
||||
<item>10</item>
|
||||
<item>11</item>
|
||||
<item>12</item>
|
||||
<item>13</item>
|
||||
<item>14</item>
|
||||
<item>15</item>
|
||||
</integer-array>
|
||||
</resources>
|
||||
```
|
||||
7. Replace the values in the keys.xml file with those extracted from the original app.
|
||||
8. Run
|
||||
## Current state of the project
|
||||
|
||||
Right now the project is still under development, and until further notice, any database updates will result in having to erase application data.
|
||||
|
||||
This document will be updated once the application does not need any more database resets.
|
||||
|
||||
## Features
|
||||
|
||||
As of now, the project allows you to read characters, view characters stats, and send them back to your watch.
|
||||
|
||||
You can also apply items to the characters read, such as special missions, or change timers, and store characters in the storage section.
|
||||
|
||||
You also earn new items every time an item such as a special mission (VB only) or a character completes an in-app adventure mission.
|
||||
|
||||
App also comes with a dex that will update every time a new character is added, and allows you to see evolution requirements and current adventure stage in the watch.
|
||||
|
||||
## How to set up
|
||||
|
||||
1. Download the latest version for VB Arena APK from a trustworthy source. If your download is a standalone APK, continue to step 2. Otherwise, if your download is an XAPK, do the following:
|
||||
|
||||
1. Using your phone file manager, rename the XAPK file to ZIP, and extract its contents. You can also do this with any other device, such as Windows, macOS and Linux.
|
||||
|
||||
2. Once the files are extracted, look for an APK called `com.bandai.vitalbraceletarena.apk`. Copy it somewhere else, you will need it.
|
||||
|
||||
2. Install an APK release for VB Arena. You will find the releases [here](http://github.com/nacabaro/vbhelper/releases). Download the latest release and install its APK.
|
||||
|
||||
Note, in the current stage of the project, you will have to delete the old application from your device. If the app keeps crashing after installing, clear application data and storage.
|
||||
|
||||
3. Import secrets in the app. These secrets will allow the app to talk to the watch. On the main screen, click on the gear icon, then `Import secrets`.
|
||||
|
||||
You will be prompted to choose a file. Choose the APK file that was previously obtained.
|
||||
|
||||
4. Import cards. Due to copyright laws, we cannot offer the characters and sprites themselves in the application. In order to import the cards do the following.
|
||||
|
||||
1. Using your own DiM/BEm cards, dump the cards to your device. You can get an in-depth tutorial in [here](http://mrblinky.net/digimon/vb/dimcardtool/dimcardtool.html). You can download the dump tool from [here](http://mrblinky.net/digimon/vb/dimcardtool/)
|
||||
|
||||
2. Once installed the tool and drivers, open the tool, connect your DiM/BEm reader hardware to yout computer and click on Read card.
|
||||
|
||||
3. Transfer the resulting file to your mobile device. You can put them anywhere, as long as they are accessible. My recommendation is to put them under a folder called `Cards` in your `Internal storage` or `SD Card`
|
||||
|
||||
4. In the app, click on import card. Next choose the BIN file corresponding to the card you want to import.
|
||||
|
||||
**Note: if you do not import the card, whenever you attempt to read a character from th watch, the character you read will get deleted.**
|
||||
|
||||
5. App will now be ready to be used.
|
||||
|
||||
## Planned features
|
||||
|
||||
- Online battles, undegoing development by `lightheel`.
|
||||
|
||||
- VitalWear compatibility, undergoing development by `cfogrady`.
|
||||
|
||||
- Support for multiple languages, not yet started.
|
||||
|
||||
- Database backup/restore.
|
||||
|
||||
## Credits
|
||||
|
||||
- `cyanic` for helping us understand more about the VB connection protocol.
|
||||
|
||||
- `cfogrady` for making both [`VB-DIM-Reader`](https://github.com/cfogrady/VB-DIM-Reader) and [`lib-vb-nfc`](https://github.com/cfogrady/lib-vb-nfc)
|
||||
|
||||
- `lightheel` for working on the online component in the application, both server and battle client.
|
||||
|
||||
- `shvstrz` for the app icon.
|
||||
@ -15,7 +15,7 @@ android {
|
||||
minSdk = 28
|
||||
targetSdk = 35
|
||||
versionCode = 1
|
||||
versionName = "Alpha 0.6"
|
||||
versionName = "Alpha 0.6.2"
|
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
@ -91,11 +91,5 @@ dependencies {
|
||||
implementation("com.google.android.material:material:1.2.0")
|
||||
implementation(libs.protobuf.javalite)
|
||||
implementation("androidx.compose.material:material")
|
||||
|
||||
implementation("com.squareup.retrofit2:retrofit:2.9.0")
|
||||
implementation("com.squareup.retrofit2:converter-gson:2.9.0")
|
||||
implementation("com.google.code.gson:gson:2.10.1")
|
||||
|
||||
// HTTP request logging
|
||||
implementation("com.squareup.okhttp3:logging-interceptor:4.11.0")
|
||||
implementation("androidx.datastore:datastore-preferences:1.1.7")
|
||||
}
|
||||
@ -12,6 +12,7 @@ import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.CardColors
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
@ -44,6 +45,9 @@ fun CharacterEntry(
|
||||
disabled: Boolean = false,
|
||||
shape: Shape = MaterialTheme.shapes.medium,
|
||||
multiplier: Int = 4,
|
||||
cardColors: CardColors = CardDefaults.cardColors(
|
||||
containerColor = MaterialTheme.colorScheme.surfaceContainerHighest
|
||||
),
|
||||
onClick: () -> Unit = { }
|
||||
) {
|
||||
val bitmap = remember (icon.bitmap) {
|
||||
@ -61,7 +65,8 @@ fun CharacterEntry(
|
||||
},
|
||||
modifier = modifier
|
||||
.aspectRatio(1f)
|
||||
.padding(8.dp)
|
||||
.padding(8.dp),
|
||||
colors = cardColors
|
||||
) {
|
||||
Box(
|
||||
contentAlignment = Alignment.BottomCenter,
|
||||
|
||||
@ -3,6 +3,7 @@ package com.github.nacabaro.vbhelper.daos
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Query
|
||||
import com.github.nacabaro.vbhelper.dtos.CharacterDtos
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
|
||||
@Dao
|
||||
@ -31,13 +32,13 @@ interface AdventureDao {
|
||||
a.finishesAdventure AS finishesAdventure,
|
||||
a.originalDuration AS originalTimeInMinutes
|
||||
FROM UserCharacter uc
|
||||
JOIN CharacterData c ON uc.charId = c.id
|
||||
JOIN CardCharacter c ON uc.charId = c.id
|
||||
JOIN Sprite s ON s.id = c.spriteId
|
||||
JOIN Card d ON c.cardId = d.id
|
||||
JOIN Adventure a ON uc.id = a.characterId
|
||||
"""
|
||||
)
|
||||
suspend fun getAdventureCharacters(): List<CharacterDtos.AdventureCharacterWithSprites>
|
||||
fun getAdventureCharacters(): Flow<List<CharacterDtos.AdventureCharacterWithSprites>>
|
||||
|
||||
@Query("""
|
||||
DELETE FROM Adventure
|
||||
|
||||
@ -0,0 +1,59 @@
|
||||
package com.github.nacabaro.vbhelper.daos
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Query
|
||||
import com.github.nacabaro.vbhelper.dtos.CardDtos
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Dao
|
||||
interface CardAdventureDao {
|
||||
@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
|
||||
""")
|
||||
fun getAdventureForCard(
|
||||
cardId: Long
|
||||
): Flow<List<CardDtos.CardAdventureWithSprites>>
|
||||
}
|
||||
@ -5,6 +5,7 @@ import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import com.github.nacabaro.vbhelper.domain.card.Card
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Dao
|
||||
interface CardDao {
|
||||
@ -21,12 +22,12 @@ interface CardDao {
|
||||
"""
|
||||
SELECT ca.*
|
||||
FROM Card ca
|
||||
JOIN CharacterData ch ON ca.id = ch.cardId
|
||||
JOIN CardCharacter ch ON ca.id = ch.cardId
|
||||
JOIN UserCharacter uc ON ch.id = uc.charId
|
||||
WHERE uc.id = :id
|
||||
"""
|
||||
)
|
||||
suspend fun getCardByCharacterId(id: Long): Card
|
||||
fun getCardByCharacterId(id: Long): Flow<Card>
|
||||
|
||||
@Query("UPDATE Card SET name = :newName WHERE id = :id")
|
||||
suspend fun renameCard(id: Int, newName: String)
|
||||
|
||||
@ -0,0 +1,48 @@
|
||||
package com.github.nacabaro.vbhelper.daos
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Query
|
||||
import com.github.cfogrady.vbnfc.data.NfcCharacter
|
||||
import com.github.nacabaro.vbhelper.dtos.CharacterDtos
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Dao
|
||||
interface CardFusionsDao {
|
||||
@Query("""
|
||||
INSERT INTO
|
||||
CardFusions (
|
||||
fromCharaId,
|
||||
attribute,
|
||||
toCharaId
|
||||
)
|
||||
SELECT
|
||||
(SELECT id FROM CardCharacter WHERE cardId = :cardId AND charaIndex = :fromCharaId),
|
||||
:attribute,
|
||||
(SELECT id FROM CardCharacter WHERE cardId = :cardId AND charaIndex = :toCharaId)
|
||||
""")
|
||||
suspend fun insertNewFusion(
|
||||
cardId: Long,
|
||||
fromCharaId: Int,
|
||||
attribute: NfcCharacter.Attribute,
|
||||
toCharaId: Int
|
||||
)
|
||||
|
||||
@Query("""
|
||||
SELECT
|
||||
cf.toCharaId as charaId,
|
||||
cf.fromCharaId as fromCharaId,
|
||||
s.spriteIdle1 as spriteIdle,
|
||||
cc.attribute as attribute,
|
||||
s.width as spriteWidth,
|
||||
s.height as spriteHeight,
|
||||
d.discoveredOn as discoveredOn,
|
||||
cf.attribute as fusionAttribute
|
||||
FROM CardFusions cf
|
||||
JOIN CardCharacter cc ON cc.id = cf.toCharaId
|
||||
JOIN Sprite s ON s.id = cc.id
|
||||
LEFT JOIN Dex d ON d.id = cc.id
|
||||
WHERE cf.fromCharaId = :charaId
|
||||
ORDER BY cc.charaIndex
|
||||
""")
|
||||
fun getFusionsForCharacter(charaId: Long): Flow<List<CharacterDtos.FusionsWithSpritesAndObtained>>
|
||||
}
|
||||
@ -1,17 +1,29 @@
|
||||
package com.github.nacabaro.vbhelper.daos
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.Query
|
||||
import androidx.room.Upsert
|
||||
import com.github.nacabaro.vbhelper.domain.card.CardProgress
|
||||
import com.github.nacabaro.vbhelper.dtos.CharacterDtos
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Dao
|
||||
interface CardProgressDao {
|
||||
@Upsert
|
||||
fun updateDimProgress(vararg cardProgresses: CardProgress)
|
||||
@Query("""
|
||||
UPDATE CardProgress
|
||||
SET
|
||||
currentStage = :currentStage,
|
||||
unlocked = :unlocked
|
||||
WHERE cardId = :cardId AND
|
||||
currentStage < :currentStage
|
||||
""")
|
||||
fun updateCardProgress(currentStage: Int, cardId: Long, unlocked: Boolean)
|
||||
|
||||
@Query(
|
||||
"SELECT currentStage FROM CardProgress WHERE cardId = :cardId"
|
||||
)
|
||||
fun getCardProgress(cardId: Int): Int
|
||||
fun getCardProgress(cardId: Long): Flow<Int>
|
||||
|
||||
@Insert
|
||||
fun insertCardProgress(cardProgress: CardProgress)
|
||||
}
|
||||
@ -3,17 +3,18 @@ package com.github.nacabaro.vbhelper.daos
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.Query
|
||||
import com.github.nacabaro.vbhelper.domain.card.CharacterData
|
||||
import com.github.nacabaro.vbhelper.domain.card.CardCharacter
|
||||
import com.github.nacabaro.vbhelper.domain.characters.Sprite
|
||||
import com.github.nacabaro.vbhelper.dtos.CharacterDtos
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Dao
|
||||
interface CharacterDao {
|
||||
@Insert
|
||||
suspend fun insertCharacter(vararg characterData: CharacterData)
|
||||
suspend fun insertCharacter(vararg characterData: CardCharacter)
|
||||
|
||||
@Query("SELECT * FROM CharacterData WHERE charaIndex = :monIndex AND cardId = :dimId LIMIT 1")
|
||||
fun getCharacterByMonIndex(monIndex: Int, dimId: Long): CharacterData
|
||||
@Query("SELECT * FROM CardCharacter WHERE charaIndex = :monIndex AND cardId = :dimId LIMIT 1")
|
||||
fun getCharacterByMonIndex(monIndex: Int, dimId: Long): CardCharacter
|
||||
|
||||
@Insert
|
||||
suspend fun insertSprite(vararg sprite: Sprite)
|
||||
@ -24,10 +25,12 @@ interface CharacterDao {
|
||||
d.cardId as cardId,
|
||||
c.charaIndex as charId,
|
||||
c.stage as stage,
|
||||
c.attribute as attribute
|
||||
FROM CharacterData c
|
||||
c.attribute as attribute,
|
||||
cp.currentStage as currentStage
|
||||
FROM CardCharacter c
|
||||
JOIN UserCharacter uc ON c.id = uc.charId
|
||||
JOIN Card d ON c.cardId = d.id
|
||||
JOIN CardProgress cp ON d.id = cp.cardId
|
||||
WHERE c.id = :charId
|
||||
"""
|
||||
)
|
||||
@ -37,14 +40,14 @@ interface CharacterDao {
|
||||
"""
|
||||
INSERT INTO PossibleTransformations (charaId, requiredVitals, requiredTrophies, requiredBattles, requiredWinRate, changeTimerHours, requiredAdventureLevelCompleted, toCharaId)
|
||||
SELECT
|
||||
(SELECT id FROM CharacterData WHERE charaIndex = :fromChraraIndex AND cardId = :cardId),
|
||||
(SELECT id FROM CardCharacter WHERE charaIndex = :fromChraraIndex AND cardId = :cardId),
|
||||
:requiredVitals,
|
||||
:requiredTrophies,
|
||||
:requiredBattles,
|
||||
:requiredWinRate,
|
||||
:changeTimerHours,
|
||||
:requiredAdventureLevelCompleted,
|
||||
(SELECT id FROM CharacterData WHERE charaIndex = :toChraraIndex AND cardId = :cardId)
|
||||
(SELECT id FROM CardCharacter WHERE charaIndex = :toChraraIndex AND cardId = :cardId)
|
||||
"""
|
||||
)
|
||||
suspend fun insertPossibleTransformation(
|
||||
@ -76,12 +79,12 @@ interface CharacterDao {
|
||||
pt.requiredAdventureLevelCompleted as requiredAdventureLevelCompleted
|
||||
FROM
|
||||
PossibleTransformations pt
|
||||
JOIN CharacterData c on pt.toCharaId = c.id
|
||||
JOIN CardCharacter c on pt.toCharaId = c.id
|
||||
JOIN Sprite s ON s.id = c.spriteId
|
||||
LEFT JOIN Dex d ON d.id = pt.toCharaId
|
||||
WHERE
|
||||
c.cardId = :cardId
|
||||
pt.charaId = :characterId
|
||||
"""
|
||||
)
|
||||
suspend fun getEvolutionRequirementsForCard(cardId: Long): List<CharacterDtos.EvolutionRequirementsWithSpritesAndObtained>
|
||||
fun getEvolutionRequirementsForCard(characterId: Long): Flow<List<CharacterDtos.EvolutionRequirementsWithSpritesAndObtained>>
|
||||
}
|
||||
@ -4,6 +4,7 @@ import androidx.room.Dao
|
||||
import androidx.room.Query
|
||||
import com.github.nacabaro.vbhelper.dtos.CardDtos
|
||||
import com.github.nacabaro.vbhelper.dtos.CharacterDtos
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Dao
|
||||
interface DexDao {
|
||||
@ -11,7 +12,7 @@ interface DexDao {
|
||||
"""
|
||||
INSERT OR IGNORE INTO Dex(id, discoveredOn)
|
||||
VALUES (
|
||||
(SELECT id FROM CharacterData WHERE charaIndex = :charIndex AND cardId = :cardId),
|
||||
(SELECT id FROM CardCharacter WHERE charaIndex = :charIndex AND cardId = :cardId),
|
||||
:discoveredOn
|
||||
)
|
||||
"""
|
||||
@ -34,13 +35,13 @@ interface DexDao {
|
||||
c.baseAp as baseAp,
|
||||
c.stage as stage,
|
||||
c.attribute as attribute
|
||||
FROM CharacterData c
|
||||
FROM CardCharacter c
|
||||
JOIN Sprite s ON c.spriteId = s.id
|
||||
LEFT JOIN dex d ON c.id = d.id
|
||||
WHERE c.cardId = :cardId
|
||||
"""
|
||||
)
|
||||
suspend fun getSingleCardProgress(cardId: Long): List<CharacterDtos.CardCharaProgress>
|
||||
fun getSingleCardProgress(cardId: Long): Flow<List<CharacterDtos.CardCharaProgress>>
|
||||
|
||||
@Query(
|
||||
"""
|
||||
@ -50,10 +51,10 @@ interface DexDao {
|
||||
c.logo as cardLogo,
|
||||
c.logoWidth as logoWidth,
|
||||
c.logoHeight as logoHeight,
|
||||
(SELECT COUNT(*) FROM CharacterData cc WHERE cc.cardId = c.id) AS totalCharacters,
|
||||
(SELECT COUNT(*) FROM Dex d JOIN CharacterData cc ON d.id = cc.id WHERE cc.cardId = c.id AND d.discoveredOn IS NOT NULL) AS obtainedCharacters
|
||||
(SELECT COUNT(*) FROM CardCharacter cc WHERE cc.cardId = c.id) AS totalCharacters,
|
||||
(SELECT COUNT(*) FROM Dex d JOIN CardCharacter cc ON d.id = cc.id WHERE cc.cardId = c.id AND d.discoveredOn IS NOT NULL) AS obtainedCharacters
|
||||
FROM Card c
|
||||
"""
|
||||
)
|
||||
suspend fun getCardsWithProgress(): List<CardDtos.CardProgress>
|
||||
fun getCardsWithProgress(): Flow<List<CardDtos.CardProgress>>
|
||||
}
|
||||
@ -3,6 +3,7 @@ package com.github.nacabaro.vbhelper.daos
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Query
|
||||
import com.github.nacabaro.vbhelper.dtos.ItemDtos
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Dao
|
||||
interface ItemDao {
|
||||
@ -13,7 +14,7 @@ interface ItemDao {
|
||||
ORDER BY Items.itemIcon ASC
|
||||
"""
|
||||
)
|
||||
suspend fun getAllItems(): List<ItemDtos.ItemsWithQuantities>
|
||||
fun getAllItems(): Flow<List<ItemDtos.ItemsWithQuantities>>
|
||||
|
||||
@Query(
|
||||
"""
|
||||
@ -22,7 +23,7 @@ interface ItemDao {
|
||||
WHERE quantity > 0
|
||||
"""
|
||||
)
|
||||
suspend fun getAllUserItems(): List<ItemDtos.ItemsWithQuantities>
|
||||
fun getAllUserItems(): Flow<List<ItemDtos.ItemsWithQuantities>>
|
||||
|
||||
@Query(
|
||||
"""
|
||||
|
||||
@ -5,7 +5,7 @@ import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import androidx.room.Upsert
|
||||
import com.github.nacabaro.vbhelper.domain.card.CharacterData
|
||||
import com.github.nacabaro.vbhelper.domain.card.CardCharacter
|
||||
import com.github.nacabaro.vbhelper.domain.device_data.UserCharacter
|
||||
import com.github.nacabaro.vbhelper.domain.device_data.BECharacterData
|
||||
import com.github.nacabaro.vbhelper.domain.device_data.SpecialMissions
|
||||
@ -13,6 +13,7 @@ import com.github.nacabaro.vbhelper.domain.device_data.TransformationHistory
|
||||
import com.github.nacabaro.vbhelper.domain.device_data.VBCharacterData
|
||||
import com.github.nacabaro.vbhelper.domain.device_data.VitalsHistory
|
||||
import com.github.nacabaro.vbhelper.dtos.CharacterDtos
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Dao
|
||||
interface UserCharacterDao {
|
||||
@ -47,7 +48,7 @@ interface UserCharacterDao {
|
||||
c.charaIndex AS monIndex,
|
||||
t.transformationDate AS transformationDate
|
||||
FROM TransformationHistory t
|
||||
JOIN CharacterData c ON c.id = t.stageId
|
||||
JOIN CardCharacter c ON c.id = t.stageId
|
||||
JOIN Sprite s ON s.id = c.spriteId
|
||||
WHERE monId = :monId
|
||||
"""
|
||||
@ -68,15 +69,16 @@ interface UserCharacterDao {
|
||||
c.nameWidth as nameSpriteWidth,
|
||||
c.nameHeight as nameSpriteHeight,
|
||||
d.isBEm as isBemCard,
|
||||
a.characterId = uc.id as isInAdventure
|
||||
a.characterId = uc.id as isInAdventure,
|
||||
uc.isActive as active
|
||||
FROM UserCharacter uc
|
||||
JOIN CharacterData c ON uc.charId = c.id
|
||||
JOIN CardCharacter c ON uc.charId = c.id
|
||||
JOIN Card d ON d.id = c.cardId
|
||||
JOIN Sprite s ON s.id = c.spriteId
|
||||
LEFT JOIN Adventure a ON a.characterId = uc.id
|
||||
"""
|
||||
)
|
||||
suspend fun getAllCharacters(): List<CharacterDtos.CharacterWithSprites>
|
||||
fun getAllCharacters(): Flow<List<CharacterDtos.CharacterWithSprites>>
|
||||
|
||||
@Query(
|
||||
"""
|
||||
@ -92,9 +94,10 @@ interface UserCharacterDao {
|
||||
c.nameWidth as nameSpriteWidth,
|
||||
c.nameHeight as nameSpriteHeight,
|
||||
d.isBEm as isBemCard,
|
||||
a.characterId = uc.id as isInAdventure
|
||||
a.characterId = uc.id as isInAdventure,
|
||||
uc.isActive as active
|
||||
FROM UserCharacter uc
|
||||
JOIN CharacterData c ON uc.charId = c.id
|
||||
JOIN CardCharacter c ON uc.charId = c.id
|
||||
JOIN Card d ON c.cardId = d.id
|
||||
JOIN Sprite s ON s.id = c.spriteId
|
||||
LEFT JOIN Adventure a ON a.characterId = uc.id
|
||||
@ -129,9 +132,10 @@ interface UserCharacterDao {
|
||||
c.nameWidth as nameSpriteWidth,
|
||||
c.nameHeight as nameSpriteHeight,
|
||||
d.isBEm as isBemCard,
|
||||
a.characterId as isInAdventure
|
||||
a.characterId as isInAdventure,
|
||||
uc.isActive as active
|
||||
FROM UserCharacter uc
|
||||
JOIN CharacterData c ON uc.charId = c.id
|
||||
JOIN CardCharacter c ON uc.charId = c.id
|
||||
JOIN Card d ON c.cardId = d.id
|
||||
JOIN Sprite s ON s.id = c.spriteId
|
||||
LEFT JOIN Adventure a ON a.characterId = uc.id
|
||||
@ -153,13 +157,13 @@ interface UserCharacterDao {
|
||||
@Query(
|
||||
"""
|
||||
SELECT c.*
|
||||
FROM CharacterData c
|
||||
FROM CardCharacter c
|
||||
join UserCharacter uc on c.id = uc.charId
|
||||
where uc.id = :charId
|
||||
LIMIT 1
|
||||
"""
|
||||
)
|
||||
suspend fun getCharacterInfo(charId: Long): CharacterData
|
||||
suspend fun getCharacterInfo(charId: Long): CardCharacter
|
||||
|
||||
|
||||
@Query(
|
||||
@ -167,7 +171,7 @@ interface UserCharacterDao {
|
||||
INSERT INTO TransformationHistory(monId, stageId, transformationDate)
|
||||
VALUES
|
||||
(:monId,
|
||||
(SELECT id FROM CharacterData WHERE charaIndex = :stage AND cardId = :dimId),
|
||||
(SELECT id FROM CardCharacter WHERE charaIndex = :stage AND cardId = :dimId),
|
||||
:transformationDate)
|
||||
"""
|
||||
)
|
||||
@ -193,9 +197,10 @@ interface UserCharacterDao {
|
||||
c.nameWidth as nameSpriteWidth,
|
||||
c.nameHeight as nameSpriteHeight,
|
||||
d.isBEm as isBemCard,
|
||||
a.characterId = uc.id as isInAdventure
|
||||
a.characterId = uc.id as isInAdventure,
|
||||
uc.isActive as active
|
||||
FROM UserCharacter uc
|
||||
JOIN CharacterData c ON uc.charId = c.id
|
||||
JOIN CardCharacter c ON uc.charId = c.id
|
||||
JOIN Card d ON d.id = c.cardId
|
||||
JOIN Sprite s ON s.id = c.spriteId
|
||||
LEFT JOIN Adventure a ON a.characterId = uc.id
|
||||
@ -218,9 +223,10 @@ interface UserCharacterDao {
|
||||
c.nameWidth as nameSpriteWidth,
|
||||
c.nameHeight as nameSpriteHeight,
|
||||
d.isBEm as isBemCard,
|
||||
a.characterId = uc.id as isInAdventure
|
||||
a.characterId = uc.id as isInAdventure,
|
||||
uc.isActive as active
|
||||
FROM UserCharacter uc
|
||||
JOIN CharacterData c ON uc.charId = c.id
|
||||
JOIN CardCharacter c ON uc.charId = c.id
|
||||
JOIN Card d ON d.id = c.cardId
|
||||
JOIN Sprite s ON s.id = c.spriteId
|
||||
LEFT JOIN Adventure a ON a.characterId = uc.id
|
||||
|
||||
@ -3,17 +3,21 @@ package com.github.nacabaro.vbhelper.database
|
||||
import androidx.room.Database
|
||||
import androidx.room.RoomDatabase
|
||||
import com.github.nacabaro.vbhelper.daos.AdventureDao
|
||||
import com.github.nacabaro.vbhelper.daos.CardAdventureDao
|
||||
import com.github.nacabaro.vbhelper.daos.CharacterDao
|
||||
import com.github.nacabaro.vbhelper.daos.DexDao
|
||||
import com.github.nacabaro.vbhelper.daos.CardDao
|
||||
import com.github.nacabaro.vbhelper.daos.CardFusionsDao
|
||||
import com.github.nacabaro.vbhelper.daos.CardProgressDao
|
||||
import com.github.nacabaro.vbhelper.daos.ItemDao
|
||||
import com.github.nacabaro.vbhelper.daos.SpecialMissionDao
|
||||
import com.github.nacabaro.vbhelper.daos.SpriteDao
|
||||
import com.github.nacabaro.vbhelper.daos.UserCharacterDao
|
||||
import com.github.nacabaro.vbhelper.domain.card.Background
|
||||
import com.github.nacabaro.vbhelper.domain.card.CharacterData
|
||||
import com.github.nacabaro.vbhelper.domain.card.CardCharacter
|
||||
import com.github.nacabaro.vbhelper.domain.card.Card
|
||||
import com.github.nacabaro.vbhelper.domain.card.CardAdventure
|
||||
import com.github.nacabaro.vbhelper.domain.card.CardFusions
|
||||
import com.github.nacabaro.vbhelper.domain.card.CardProgress
|
||||
import com.github.nacabaro.vbhelper.domain.card.PossibleTransformations
|
||||
import com.github.nacabaro.vbhelper.domain.characters.Sprite
|
||||
@ -32,7 +36,9 @@ import com.github.nacabaro.vbhelper.domain.items.Items
|
||||
entities = [
|
||||
Card::class,
|
||||
CardProgress::class,
|
||||
CharacterData::class,
|
||||
CardCharacter::class,
|
||||
CardAdventure::class,
|
||||
CardFusions::class,
|
||||
Sprite::class,
|
||||
UserCharacter::class,
|
||||
BECharacterData::class,
|
||||
@ -57,4 +63,6 @@ abstract class AppDatabase : RoomDatabase() {
|
||||
abstract fun adventureDao(): AdventureDao
|
||||
abstract fun spriteDao(): SpriteDao
|
||||
abstract fun specialMissionDao(): SpecialMissionDao
|
||||
abstract fun cardAdventureDao(): CardAdventureDao
|
||||
abstract fun cardFusionsDao(): CardFusionsDao
|
||||
}
|
||||
@ -1,9 +1,11 @@
|
||||
package com.github.nacabaro.vbhelper.di
|
||||
|
||||
import com.github.nacabaro.vbhelper.database.AppDatabase
|
||||
import com.github.nacabaro.vbhelper.source.CurrencyRepository
|
||||
import com.github.nacabaro.vbhelper.source.DataStoreSecretsRepository
|
||||
|
||||
interface AppContainer {
|
||||
val db: AppDatabase
|
||||
val dataStoreSecretsRepository: DataStoreSecretsRepository
|
||||
val currencyRepository: CurrencyRepository
|
||||
}
|
||||
@ -1,20 +1,27 @@
|
||||
import android.content.Context
|
||||
import androidx.datastore.core.DataStore
|
||||
import androidx.datastore.dataStore
|
||||
import androidx.datastore.preferences.core.Preferences
|
||||
import androidx.datastore.preferences.preferencesDataStore
|
||||
import androidx.room.Room
|
||||
import com.github.nacabaro.vbhelper.database.AppDatabase
|
||||
import com.github.nacabaro.vbhelper.di.AppContainer
|
||||
import com.github.nacabaro.vbhelper.source.CurrencyRepository
|
||||
import com.github.nacabaro.vbhelper.source.DataStoreSecretsRepository
|
||||
import com.github.nacabaro.vbhelper.source.SecretsSerializer
|
||||
import com.github.nacabaro.vbhelper.source.proto.Secrets
|
||||
|
||||
private const val SECRETS_DATA_STORE_NAME = "secrets.pb"
|
||||
|
||||
private const val USER_PREFERENCES_NAME = "user_preferences"
|
||||
val Context.secretsStore: DataStore<Secrets> by dataStore(
|
||||
fileName = SECRETS_DATA_STORE_NAME,
|
||||
serializer = SecretsSerializer
|
||||
)
|
||||
|
||||
val Context.currencyStore: DataStore<Preferences> by preferencesDataStore(
|
||||
name = USER_PREFERENCES_NAME
|
||||
)
|
||||
|
||||
class DefaultAppContainer(private val context: Context) : AppContainer {
|
||||
|
||||
override val db: AppDatabase by lazy {
|
||||
@ -29,5 +36,6 @@ class DefaultAppContainer(private val context: Context) : AppContainer {
|
||||
|
||||
override val dataStoreSecretsRepository = DataStoreSecretsRepository(context.secretsStore)
|
||||
|
||||
override val currencyRepository = CurrencyRepository(context.currencyStore)
|
||||
}
|
||||
|
||||
|
||||
@ -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?
|
||||
)
|
||||
@ -28,7 +28,7 @@ import com.github.nacabaro.vbhelper.domain.characters.Sprite
|
||||
* and monIndex.
|
||||
* TODO: Customs will mean this should be unique per cardName and monIndex
|
||||
*/
|
||||
data class CharacterData (
|
||||
data class CardCharacter (
|
||||
@PrimaryKey(autoGenerate = true) val id: Long = 0,
|
||||
val cardId: Long,
|
||||
val spriteId: Long,
|
||||
@ -0,0 +1,29 @@
|
||||
package com.github.nacabaro.vbhelper.domain.card
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.ForeignKey
|
||||
import androidx.room.PrimaryKey
|
||||
import com.github.cfogrady.vbnfc.data.NfcCharacter
|
||||
|
||||
@Entity(
|
||||
foreignKeys = [
|
||||
ForeignKey(
|
||||
entity = CardCharacter::class,
|
||||
parentColumns = ["id"],
|
||||
childColumns = ["fromCharaId"],
|
||||
onDelete = ForeignKey.CASCADE
|
||||
),
|
||||
ForeignKey(
|
||||
entity = CardCharacter::class,
|
||||
parentColumns = ["id"],
|
||||
childColumns = ["toCharaId"],
|
||||
onDelete = ForeignKey.CASCADE
|
||||
)
|
||||
]
|
||||
)
|
||||
data class CardFusions(
|
||||
@PrimaryKey(autoGenerate = true) val id: Long,
|
||||
val fromCharaId: Long,
|
||||
val attribute: NfcCharacter.Attribute,
|
||||
val toCharaId: Long
|
||||
)
|
||||
@ -7,13 +7,13 @@ import androidx.room.PrimaryKey
|
||||
@Entity(
|
||||
foreignKeys = [
|
||||
ForeignKey(
|
||||
entity = CharacterData::class,
|
||||
entity = CardCharacter::class,
|
||||
parentColumns = ["id"],
|
||||
childColumns = ["charaId"],
|
||||
onDelete = ForeignKey.CASCADE
|
||||
),
|
||||
ForeignKey(
|
||||
entity = CharacterData::class,
|
||||
entity = CardCharacter::class,
|
||||
parentColumns = ["id"],
|
||||
childColumns = ["toCharaId"],
|
||||
onDelete = ForeignKey.CASCADE
|
||||
|
||||
@ -3,12 +3,12 @@ package com.github.nacabaro.vbhelper.domain.characters
|
||||
import androidx.room.Entity
|
||||
import androidx.room.ForeignKey
|
||||
import androidx.room.PrimaryKey
|
||||
import com.github.nacabaro.vbhelper.domain.card.CharacterData
|
||||
import com.github.nacabaro.vbhelper.domain.card.CardCharacter
|
||||
|
||||
@Entity(
|
||||
foreignKeys = [
|
||||
ForeignKey(
|
||||
entity = CharacterData::class,
|
||||
entity = CardCharacter::class,
|
||||
parentColumns = ["id"],
|
||||
childColumns = ["id"],
|
||||
onDelete = ForeignKey.CASCADE
|
||||
|
||||
@ -3,7 +3,7 @@ package com.github.nacabaro.vbhelper.domain.device_data
|
||||
import androidx.room.Entity
|
||||
import androidx.room.ForeignKey
|
||||
import androidx.room.PrimaryKey
|
||||
import com.github.nacabaro.vbhelper.domain.card.CharacterData
|
||||
import com.github.nacabaro.vbhelper.domain.card.CardCharacter
|
||||
|
||||
@Entity(
|
||||
foreignKeys = [
|
||||
@ -14,7 +14,7 @@ import com.github.nacabaro.vbhelper.domain.card.CharacterData
|
||||
onDelete = ForeignKey.CASCADE
|
||||
),
|
||||
ForeignKey(
|
||||
entity = CharacterData::class,
|
||||
entity = CardCharacter::class,
|
||||
parentColumns = ["id"],
|
||||
childColumns = ["stageId"],
|
||||
onDelete = ForeignKey.CASCADE
|
||||
|
||||
@ -5,12 +5,12 @@ import androidx.room.ForeignKey
|
||||
import androidx.room.PrimaryKey
|
||||
import com.github.cfogrady.vbnfc.data.NfcCharacter
|
||||
import com.github.nacabaro.vbhelper.utils.DeviceType
|
||||
import com.github.nacabaro.vbhelper.domain.card.CharacterData
|
||||
import com.github.nacabaro.vbhelper.domain.card.CardCharacter
|
||||
|
||||
@Entity(
|
||||
foreignKeys = [
|
||||
ForeignKey(
|
||||
entity = CharacterData::class,
|
||||
entity = CardCharacter::class,
|
||||
parentColumns = ["id"],
|
||||
childColumns = ["charId"],
|
||||
onDelete = ForeignKey.CASCADE
|
||||
|
||||
@ -10,4 +10,18 @@ object CardDtos {
|
||||
val totalCharacters: Int,
|
||||
val obtainedCharacters: Int,
|
||||
)
|
||||
|
||||
data class CardAdventureWithSprites (
|
||||
val characterName: ByteArray,
|
||||
val characterNameWidth: Int,
|
||||
val characterNameHeight: Int,
|
||||
val characterIdleSprite: ByteArray,
|
||||
val characterIdleSpriteWidth: Int,
|
||||
val characterIdleSpriteHeight: Int,
|
||||
val characterAp: Int,
|
||||
val characterBp: Int?,
|
||||
val characterDp: Int,
|
||||
val characterHp: Int,
|
||||
val steps: Int,
|
||||
)
|
||||
}
|
||||
@ -31,14 +31,16 @@ object CharacterDtos {
|
||||
val nameSpriteWidth: Int,
|
||||
val nameSpriteHeight: Int,
|
||||
val isBemCard: Boolean,
|
||||
val isInAdventure: Boolean
|
||||
val isInAdventure: Boolean,
|
||||
val active: Boolean
|
||||
)
|
||||
|
||||
data class CardCharacterInfo(
|
||||
val cardId: Int,
|
||||
val cardId: Long,
|
||||
val charId: Int,
|
||||
val stage: Int,
|
||||
val attribute: NfcCharacter.Attribute
|
||||
val attribute: NfcCharacter.Attribute,
|
||||
val currentStage: Int
|
||||
)
|
||||
|
||||
data class TransformationHistory(
|
||||
@ -113,4 +115,14 @@ object CharacterDtos {
|
||||
val changeTimerHours: Int,
|
||||
val requiredAdventureLevelCompleted: Int
|
||||
)
|
||||
|
||||
data class FusionsWithSpritesAndObtained(
|
||||
val charaId: Long,
|
||||
val fromCharaId: Long,
|
||||
val spriteIdle: ByteArray,
|
||||
val spriteWidth: Int,
|
||||
val spriteHeight: Int,
|
||||
val discoveredOn: Long?,
|
||||
val fusionAttribute: NfcCharacter.Attribute
|
||||
)
|
||||
}
|
||||
@ -34,6 +34,7 @@ import com.github.nacabaro.vbhelper.screens.itemsScreen.ItemsScreenControllerImp
|
||||
import com.github.nacabaro.vbhelper.screens.settingsScreen.SettingsScreenControllerImpl
|
||||
import com.github.nacabaro.vbhelper.screens.adventureScreen.AdventureScreen
|
||||
import com.github.nacabaro.vbhelper.screens.adventureScreen.AdventureScreenControllerImpl
|
||||
import com.github.nacabaro.vbhelper.screens.cardScreen.CardAdventureScreen
|
||||
import com.github.nacabaro.vbhelper.screens.cardScreen.CardScreenControllerImpl
|
||||
import com.github.nacabaro.vbhelper.screens.settingsScreen.CreditsScreen
|
||||
import com.github.nacabaro.vbhelper.screens.spriteViewer.SpriteViewerControllerImpl
|
||||
@ -145,7 +146,7 @@ fun AppNavigation(
|
||||
if (cardId != null) {
|
||||
CardViewScreen(
|
||||
navController = navController,
|
||||
dimId = cardId.toLong()
|
||||
cardId = cardId.toLong()
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -177,6 +178,17 @@ fun AppNavigation(
|
||||
navController = navController
|
||||
)
|
||||
}
|
||||
composable(NavigationItems.CardAdventure.route) {
|
||||
val cardId = it.arguments?.getString("cardId")
|
||||
if (cardId != null) {
|
||||
CardAdventureScreen(
|
||||
navController = navController,
|
||||
cardId = cardId.toLong(),
|
||||
cardScreenController = applicationNavigationHandlers
|
||||
.cardScreenController
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,6 +11,7 @@ sealed class NavigationItems (
|
||||
object Battles : NavigationItems("Battle", R.drawable.baseline_swords_24, "Battle")
|
||||
object Home : NavigationItems("Home", R.drawable.baseline_cottage_24, "Home")
|
||||
object Dex : NavigationItems("Dex", R.drawable.baseline_menu_book_24, "Dex")
|
||||
object CardAdventure : NavigationItems("CardAdventure/{cardId}", R.drawable.baseline_fort_24, "Card adventure")
|
||||
object Storage : NavigationItems("Storage", R.drawable.baseline_catching_pokemon_24, "Storage")
|
||||
object Settings : NavigationItems("Settings", R.drawable.baseline_settings_24, "Settings")
|
||||
object Viewer : NavigationItems("Viewer", R.drawable.baseline_image_24, "Viewer")
|
||||
|
||||
@ -9,12 +9,11 @@ import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.produceState
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
@ -29,7 +28,6 @@ import com.github.nacabaro.vbhelper.navigation.NavigationItems
|
||||
import com.github.nacabaro.vbhelper.source.StorageRepository
|
||||
import com.github.nacabaro.vbhelper.utils.BitmapData
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import java.time.Instant
|
||||
|
||||
@Composable
|
||||
@ -37,16 +35,17 @@ fun AdventureScreen(
|
||||
navController: NavController,
|
||||
storageScreenController: AdventureScreenControllerImpl
|
||||
) {
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
val application = LocalContext.current.applicationContext as VBHelper
|
||||
val database = application.container.db
|
||||
val storageRepository = StorageRepository(database)
|
||||
val characterList = remember {
|
||||
mutableStateOf<List<CharacterDtos.AdventureCharacterWithSprites>>(emptyList())
|
||||
}
|
||||
val characterList by storageRepository.getAdventureCharacters().collectAsState(emptyList())
|
||||
|
||||
var obtainedItem by remember {
|
||||
mutableStateOf<ItemDtos.PurchasedItem?>(null)
|
||||
}
|
||||
var obtainedCurrency by remember {
|
||||
mutableStateOf(0)
|
||||
}
|
||||
|
||||
val currentTime by produceState(initialValue = Instant.now().epochSecond) {
|
||||
while (true) {
|
||||
@ -59,13 +58,6 @@ fun AdventureScreen(
|
||||
mutableStateOf<CharacterDtos.AdventureCharacterWithSprites?>(null)
|
||||
}
|
||||
|
||||
LaunchedEffect(storageRepository) {
|
||||
coroutineScope.launch {
|
||||
characterList.value = storageRepository
|
||||
.getAdventureCharacters()
|
||||
}
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopBanner(
|
||||
@ -76,7 +68,7 @@ fun AdventureScreen(
|
||||
)
|
||||
}
|
||||
) { contentPadding ->
|
||||
if (characterList.value.isEmpty()) {
|
||||
if (characterList.isEmpty()) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
@ -91,7 +83,7 @@ fun AdventureScreen(
|
||||
modifier = Modifier
|
||||
.padding(top = contentPadding.calculateTopPadding())
|
||||
) {
|
||||
items(characterList.value) {
|
||||
items(characterList) {
|
||||
AdventureEntry(
|
||||
icon = BitmapData(
|
||||
bitmap = it.spriteIdle,
|
||||
@ -102,8 +94,9 @@ fun AdventureScreen(
|
||||
onClick = {
|
||||
if (it.finishesAdventure < currentTime) {
|
||||
storageScreenController
|
||||
.getItemFromAdventure(it.id) { adventureResult ->
|
||||
.getItemFromAdventure(it.id) { adventureResult, generatedCurrency ->
|
||||
obtainedItem = adventureResult
|
||||
obtainedCurrency = generatedCurrency
|
||||
}
|
||||
} else {
|
||||
cancelAdventureDialog = it
|
||||
@ -118,6 +111,7 @@ fun AdventureScreen(
|
||||
if (obtainedItem != null) {
|
||||
ObtainedItemDialog(
|
||||
obtainedItem = obtainedItem!!,
|
||||
obtainedCurrency = obtainedCurrency,
|
||||
onClickDismiss = {
|
||||
obtainedItem = null
|
||||
}
|
||||
|
||||
@ -4,6 +4,6 @@ import com.github.nacabaro.vbhelper.dtos.ItemDtos
|
||||
|
||||
interface AdventureScreenController {
|
||||
fun sendCharacterToAdventure(characterId: Long, timeInMinutes: Long)
|
||||
fun getItemFromAdventure(characterId: Long, onResult: (ItemDtos.PurchasedItem) -> Unit)
|
||||
fun getItemFromAdventure(characterId: Long, onResult: (ItemDtos.PurchasedItem, Int) -> Unit)
|
||||
fun cancelAdventure(characterId: Long, onResult: () -> Unit)
|
||||
}
|
||||
@ -6,6 +6,7 @@ import androidx.lifecycle.lifecycleScope
|
||||
import com.github.nacabaro.vbhelper.di.VBHelper
|
||||
import com.github.nacabaro.vbhelper.dtos.ItemDtos
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlin.math.roundToInt
|
||||
import kotlin.random.Random
|
||||
@ -37,19 +38,29 @@ class AdventureScreenControllerImpl(
|
||||
|
||||
override fun getItemFromAdventure(
|
||||
characterId: Long,
|
||||
onResult: (ItemDtos.PurchasedItem) -> Unit
|
||||
onResult: (ItemDtos.PurchasedItem, Int) -> Unit
|
||||
) {
|
||||
componentActivity.lifecycleScope.launch(Dispatchers.IO) {
|
||||
database
|
||||
.adventureDao()
|
||||
.deleteAdventure(characterId)
|
||||
|
||||
val generatedCurrency = generateRandomCurrency()
|
||||
|
||||
val generatedItem = generateItem(characterId)
|
||||
|
||||
onResult(generatedItem)
|
||||
onResult(generatedItem, generatedCurrency)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun generateRandomCurrency(): Int {
|
||||
val currentValue = application.container.currencyRepository.currencyValue.first()
|
||||
val random = (2..6).random() * 1000
|
||||
application.container.currencyRepository.setCurrencyValue(currentValue + random)
|
||||
|
||||
return random
|
||||
}
|
||||
|
||||
override fun cancelAdventure(characterId: Long, onResult: () -> Unit) {
|
||||
componentActivity.lifecycleScope.launch(Dispatchers.IO) {
|
||||
database
|
||||
@ -77,6 +88,7 @@ class AdventureScreenControllerImpl(
|
||||
val randomItem = database
|
||||
.itemDao()
|
||||
.getAllItems()
|
||||
.first()
|
||||
.random()
|
||||
|
||||
val random = ((Random.nextFloat() * character.stage) + 3).roundToInt()
|
||||
|
||||
@ -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,51 @@
|
||||
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.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.navigation.NavController
|
||||
import com.github.nacabaro.vbhelper.components.TopBanner
|
||||
|
||||
@Composable
|
||||
fun CardAdventureScreen(
|
||||
navController: NavController,
|
||||
cardScreenController: CardScreenControllerImpl,
|
||||
cardId: Long
|
||||
) {
|
||||
val cardAdventureMissions by cardScreenController
|
||||
.getCardAdventureMissions(cardId)
|
||||
.collectAsState(emptyList())
|
||||
val currentCardAdventure by cardScreenController
|
||||
.getCardProgress(cardId)
|
||||
.collectAsState(0)
|
||||
|
||||
Scaffold (
|
||||
topBar = {
|
||||
TopBanner(
|
||||
text = "Adventure missions",
|
||||
onBackClick = {
|
||||
navController.popBackStack()
|
||||
}
|
||||
)
|
||||
}
|
||||
) { contentPadding ->
|
||||
Column (
|
||||
modifier = Modifier
|
||||
.padding(top = contentPadding.calculateTopPadding())
|
||||
.verticalScroll(state = rememberScrollState())
|
||||
) {
|
||||
cardAdventureMissions.mapIndexed { index, it ->
|
||||
CardAdventureEntry(
|
||||
cardAdventureEntry = it,
|
||||
obscure = index > currentCardAdventure - 1
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,13 @@
|
||||
package com.github.nacabaro.vbhelper.screens.cardScreen
|
||||
|
||||
import com.github.nacabaro.vbhelper.dtos.CardDtos
|
||||
import com.github.nacabaro.vbhelper.dtos.CharacterDtos
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface CardScreenController {
|
||||
fun renameCard(cardId: Long, newName: String, onRenamed: (String) -> Unit)
|
||||
fun deleteCard(cardId: Long, onDeleted: () -> Unit)
|
||||
fun getCardAdventureMissions(cardId: Long): Flow<List<CardDtos.CardAdventureWithSprites>>
|
||||
fun getCardProgress(cardId: Long): Flow<Int>
|
||||
fun getFusionsForCharacters(characterId: Long): Flow<List<CharacterDtos.FusionsWithSpritesAndObtained>>
|
||||
}
|
||||
@ -3,6 +3,9 @@ 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 com.github.nacabaro.vbhelper.dtos.CharacterDtos
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class CardScreenControllerImpl(
|
||||
@ -11,7 +14,6 @@ class CardScreenControllerImpl(
|
||||
private val application = componentActivity.applicationContext as VBHelper
|
||||
private val database = application.container.db
|
||||
|
||||
|
||||
override fun renameCard(cardId: Long, newName: String, onRenamed: (String) -> Unit) {
|
||||
componentActivity.lifecycleScope.launch {
|
||||
database
|
||||
@ -31,4 +33,23 @@ class CardScreenControllerImpl(
|
||||
onDeleted()
|
||||
}
|
||||
}
|
||||
|
||||
override fun getCardAdventureMissions(cardId: Long): Flow<List<CardDtos.CardAdventureWithSprites>> {
|
||||
return database
|
||||
.cardAdventureDao()
|
||||
.getAdventureForCard(cardId)
|
||||
}
|
||||
|
||||
override fun getCardProgress(cardId: Long): Flow<Int> {
|
||||
return database
|
||||
.cardProgressDao()
|
||||
.getCardProgress(cardId)
|
||||
}
|
||||
|
||||
override fun getFusionsForCharacters(characterId: Long): Flow<List<CharacterDtos.FusionsWithSpritesAndObtained>> {
|
||||
return database
|
||||
.cardFusionsDao()
|
||||
.getFusionsForCharacter(characterId)
|
||||
}
|
||||
|
||||
}
|
||||
@ -5,10 +5,10 @@ import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
|
||||
import androidx.compose.foundation.lazy.grid.items
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.navigation.NavController
|
||||
import com.github.nacabaro.vbhelper.utils.BitmapData
|
||||
@ -16,40 +16,39 @@ import com.github.nacabaro.vbhelper.components.CharacterEntry
|
||||
import com.github.nacabaro.vbhelper.components.TopBanner
|
||||
import com.github.nacabaro.vbhelper.di.VBHelper
|
||||
import com.github.nacabaro.vbhelper.dtos.CharacterDtos
|
||||
import com.github.nacabaro.vbhelper.navigation.NavigationItems
|
||||
import com.github.nacabaro.vbhelper.screens.cardScreen.dialogs.DexCharaDetailsDialog
|
||||
import com.github.nacabaro.vbhelper.source.DexRepository
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Composable
|
||||
fun CardViewScreen(
|
||||
navController: NavController,
|
||||
dimId: Long
|
||||
cardId: Long
|
||||
) {
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
val application = LocalContext.current.applicationContext as VBHelper
|
||||
val dexRepository = DexRepository(application.container.db)
|
||||
|
||||
val characterList = remember { mutableStateOf<List<CharacterDtos.CardCharaProgress>>(emptyList()) }
|
||||
val cardPossibleTransformations = remember { mutableStateOf<List<CharacterDtos.EvolutionRequirementsWithSpritesAndObtained>>(emptyList()) }
|
||||
val characterList by dexRepository.getCharactersByCardId(cardId).collectAsState(emptyList())
|
||||
|
||||
val selectedCharacter = remember { mutableStateOf<CharacterDtos.CardCharaProgress?>(null) }
|
||||
|
||||
LaunchedEffect(dexRepository) {
|
||||
coroutineScope.launch {
|
||||
val newCharacterList = dexRepository.getCharactersByCardId(dimId)
|
||||
characterList.value = newCharacterList
|
||||
|
||||
val newCardPossibleTransformations = dexRepository.getCardPossibleTransformations(dimId)
|
||||
cardPossibleTransformations.value = newCardPossibleTransformations
|
||||
}
|
||||
}
|
||||
|
||||
Scaffold (
|
||||
topBar = {
|
||||
TopBanner(
|
||||
text = "Discovered characters",
|
||||
onBackClick = {
|
||||
navController.popBackStack()
|
||||
},
|
||||
onAdventureClick = {
|
||||
navController
|
||||
.navigate(route = NavigationItems
|
||||
.CardAdventure
|
||||
.route
|
||||
.replace(
|
||||
"{cardId}",
|
||||
cardId.toString()
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
@ -58,7 +57,7 @@ fun CardViewScreen(
|
||||
columns = GridCells.Fixed(3),
|
||||
contentPadding = contentPadding
|
||||
) {
|
||||
items(characterList.value) { character ->
|
||||
items(characterList) { character ->
|
||||
CharacterEntry(
|
||||
onClick = {
|
||||
selectedCharacter.value = character
|
||||
@ -76,7 +75,6 @@ fun CardViewScreen(
|
||||
if (selectedCharacter.value != null) {
|
||||
DexCharaDetailsDialog(
|
||||
currentChara = selectedCharacter.value!!,
|
||||
possibleTransformations = cardPossibleTransformations.value,
|
||||
obscure = selectedCharacter.value!!.discoveredOn == null,
|
||||
onClickClose = {
|
||||
selectedCharacter.value = null
|
||||
|
||||
@ -7,11 +7,10 @@ import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
@ -25,17 +24,15 @@ import com.github.nacabaro.vbhelper.navigation.NavigationItems
|
||||
import com.github.nacabaro.vbhelper.screens.cardScreen.dialogs.CardDeleteDialog
|
||||
import com.github.nacabaro.vbhelper.screens.cardScreen.dialogs.CardRenameDialog
|
||||
import com.github.nacabaro.vbhelper.source.DexRepository
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Composable
|
||||
fun CardsScreen(
|
||||
navController: NavController,
|
||||
cardScreenController: CardScreenControllerImpl
|
||||
) {
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
val application = LocalContext.current.applicationContext as VBHelper
|
||||
val dexRepository = DexRepository(application.container.db)
|
||||
val cardList = remember { mutableStateOf<List<CardDtos.CardProgress>>(emptyList()) }
|
||||
val cardList by dexRepository.getAllDims().collectAsState(emptyList())
|
||||
|
||||
val selectedCard = remember { mutableStateOf<CardDtos.CardProgress?>(null) }
|
||||
var clickedDelete by remember { mutableStateOf(false) }
|
||||
@ -43,13 +40,6 @@ fun CardsScreen(
|
||||
|
||||
var modifyCards by remember { mutableStateOf(false) }
|
||||
|
||||
LaunchedEffect(dexRepository) {
|
||||
coroutineScope.launch {
|
||||
val newDimList = dexRepository.getAllDims()
|
||||
cardList.value = newDimList
|
||||
}
|
||||
}
|
||||
|
||||
Scaffold (
|
||||
topBar = {
|
||||
TopBanner(
|
||||
@ -64,7 +54,7 @@ fun CardsScreen(
|
||||
modifier = Modifier
|
||||
.padding(top = contentPadding.calculateTopPadding())
|
||||
) {
|
||||
items(cardList.value) {
|
||||
items(cardList) {
|
||||
CardEntry(
|
||||
name = it.cardName,
|
||||
logo = BitmapData(
|
||||
|
||||
@ -16,6 +16,11 @@ import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.contentColorFor
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.ColorFilter
|
||||
@ -23,7 +28,9 @@ import androidx.compose.ui.graphics.FilterQuality
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.window.Dialog
|
||||
import com.github.nacabaro.vbhelper.di.VBHelper
|
||||
import com.github.nacabaro.vbhelper.dtos.CharacterDtos
|
||||
import com.github.nacabaro.vbhelper.source.DexRepository
|
||||
import com.github.nacabaro.vbhelper.utils.BitmapData
|
||||
import com.github.nacabaro.vbhelper.utils.getImageBitmap
|
||||
|
||||
@ -31,14 +38,25 @@ import com.github.nacabaro.vbhelper.utils.getImageBitmap
|
||||
@Composable
|
||||
fun DexCharaDetailsDialog(
|
||||
currentChara: CharacterDtos.CardCharaProgress,
|
||||
possibleTransformations: List<CharacterDtos.EvolutionRequirementsWithSpritesAndObtained>,
|
||||
obscure: Boolean,
|
||||
onClickClose: () -> Unit
|
||||
) {
|
||||
val nameMultiplier = 3
|
||||
val charaMultiplier = 4
|
||||
|
||||
val currentCharaPossibleTransformations = possibleTransformations.filter { it.fromCharaId == currentChara.id }
|
||||
val application = LocalContext.current.applicationContext as VBHelper
|
||||
val database = application.container.db
|
||||
val dexRepository = DexRepository(database)
|
||||
|
||||
var showFusions by remember { mutableStateOf(false) }
|
||||
|
||||
val currentCharaPossibleTransformations by dexRepository
|
||||
.getCharacterPossibleTransformations(currentChara.id)
|
||||
.collectAsState(emptyList())
|
||||
|
||||
val currentCharaPossibleFusions by dexRepository
|
||||
.getCharacterPossibleFusions(currentChara.id)
|
||||
.collectAsState(emptyList())
|
||||
|
||||
val romanNumeralsStage = when (currentChara.stage) {
|
||||
1 -> "II"
|
||||
@ -204,12 +222,40 @@ fun DexCharaDetailsDialog(
|
||||
}
|
||||
}
|
||||
|
||||
Button(
|
||||
onClick = onClickClose
|
||||
) {
|
||||
Text("Close")
|
||||
Row {
|
||||
if (currentCharaPossibleFusions.isNotEmpty()) {
|
||||
Button(
|
||||
onClick = {
|
||||
showFusions = true
|
||||
}
|
||||
) {
|
||||
Text("Fusions")
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(
|
||||
modifier = Modifier
|
||||
.padding(4.dp)
|
||||
)
|
||||
|
||||
Button(
|
||||
onClick = onClickClose
|
||||
) {
|
||||
Text("Close")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (showFusions) {
|
||||
DexCharaFusionsDialog(
|
||||
currentChara = currentChara,
|
||||
currentCharaPossibleFusions = currentCharaPossibleFusions,
|
||||
onClickDismiss = {
|
||||
showFusions = false
|
||||
},
|
||||
obscure = obscure
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,191 @@
|
||||
package com.github.nacabaro.vbhelper.screens.cardScreen.dialogs
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.CardColors
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.contentColorFor
|
||||
import androidx.compose.runtime.Composable
|
||||
import com.github.nacabaro.vbhelper.dtos.CharacterDtos
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.ColorFilter
|
||||
import androidx.compose.ui.graphics.FilterQuality
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.window.Dialog
|
||||
import com.github.nacabaro.vbhelper.utils.BitmapData
|
||||
import com.github.nacabaro.vbhelper.utils.getImageBitmap
|
||||
|
||||
@Composable
|
||||
fun DexCharaFusionsDialog(
|
||||
currentChara: CharacterDtos.CardCharaProgress,
|
||||
currentCharaPossibleFusions: List<CharacterDtos.FusionsWithSpritesAndObtained>,
|
||||
obscure: Boolean,
|
||||
onClickDismiss: () -> Unit,
|
||||
) {
|
||||
val nameMultiplier = 3
|
||||
val charaMultiplier = 4
|
||||
|
||||
val charaBitmapData = BitmapData(
|
||||
bitmap = currentChara.spriteIdle,
|
||||
width = currentChara.spriteWidth,
|
||||
height = currentChara.spriteHeight
|
||||
)
|
||||
val charaImageBitmapData = charaBitmapData.getImageBitmap(
|
||||
context = LocalContext.current,
|
||||
multiplier = charaMultiplier,
|
||||
obscure = obscure
|
||||
)
|
||||
|
||||
val nameBitmapData = BitmapData(
|
||||
bitmap = currentChara.nameSprite,
|
||||
width = currentChara.nameSpriteWidth,
|
||||
height = currentChara.nameSpriteHeight
|
||||
)
|
||||
val nameImageBitmapData = nameBitmapData.getImageBitmap(
|
||||
context = LocalContext.current,
|
||||
multiplier = nameMultiplier,
|
||||
obscure = obscure
|
||||
)
|
||||
|
||||
Dialog(
|
||||
onDismissRequest = onClickDismiss,
|
||||
) {
|
||||
Card(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(16.dp)
|
||||
) {
|
||||
Row {
|
||||
Card (
|
||||
colors = CardColors(
|
||||
containerColor = MaterialTheme.colorScheme.surfaceVariant,
|
||||
contentColor = MaterialTheme.colorScheme.contentColorFor(
|
||||
backgroundColor = MaterialTheme.colorScheme.surfaceVariant,
|
||||
),
|
||||
disabledContainerColor = MaterialTheme.colorScheme.surfaceVariant,
|
||||
disabledContentColor = MaterialTheme.colorScheme.contentColorFor(
|
||||
backgroundColor = MaterialTheme.colorScheme.surfaceVariant,
|
||||
)
|
||||
)
|
||||
) {
|
||||
Image(
|
||||
bitmap = charaImageBitmapData.imageBitmap,
|
||||
contentDescription = "Icon",
|
||||
modifier = Modifier
|
||||
.size(charaImageBitmapData.dpWidth)
|
||||
.padding(8.dp),
|
||||
colorFilter = when (obscure) {
|
||||
true -> ColorFilter.tint(color = MaterialTheme.colorScheme.secondary)
|
||||
false -> null
|
||||
},
|
||||
filterQuality = FilterQuality.None
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(
|
||||
modifier = Modifier
|
||||
.padding(16.dp)
|
||||
)
|
||||
|
||||
if (!obscure) {
|
||||
Column {
|
||||
Image(
|
||||
bitmap = nameImageBitmapData.imageBitmap,
|
||||
contentDescription = "Icon",
|
||||
modifier = Modifier
|
||||
.width(nameImageBitmapData.dpWidth)
|
||||
.height(nameImageBitmapData.dpHeight),
|
||||
filterQuality = FilterQuality.None
|
||||
)
|
||||
}
|
||||
} else {
|
||||
Column {
|
||||
Text(text = "????????????????")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.padding(16.dp))
|
||||
Column {
|
||||
currentCharaPossibleFusions.map {
|
||||
val selectedCharaBitmap = BitmapData(
|
||||
bitmap = it.spriteIdle,
|
||||
width = it.spriteWidth,
|
||||
height = it.spriteHeight
|
||||
)
|
||||
val selectedCharaImageBitmap = selectedCharaBitmap.getImageBitmap(
|
||||
context = LocalContext.current,
|
||||
multiplier = 4,
|
||||
obscure = it.discoveredOn == null
|
||||
)
|
||||
|
||||
Card (
|
||||
modifier = Modifier
|
||||
.padding(vertical = 8.dp)
|
||||
) {
|
||||
Row (
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
) {
|
||||
Card (
|
||||
colors = CardColors(
|
||||
containerColor = MaterialTheme.colorScheme.surfaceVariant,
|
||||
contentColor = MaterialTheme.colorScheme.contentColorFor(
|
||||
backgroundColor = MaterialTheme.colorScheme.surfaceVariant,
|
||||
),
|
||||
disabledContainerColor = MaterialTheme.colorScheme.surfaceVariant,
|
||||
disabledContentColor = MaterialTheme.colorScheme.contentColorFor(
|
||||
backgroundColor = MaterialTheme.colorScheme.surfaceVariant,
|
||||
)
|
||||
)
|
||||
) {
|
||||
Image(
|
||||
bitmap = selectedCharaImageBitmap.imageBitmap,
|
||||
contentDescription = "Icon",
|
||||
modifier = Modifier
|
||||
.size(selectedCharaImageBitmap.dpWidth)
|
||||
.padding(8.dp),
|
||||
colorFilter = when (it.discoveredOn == null) {
|
||||
true -> ColorFilter.tint(color = MaterialTheme.colorScheme.secondary)
|
||||
false -> null
|
||||
},
|
||||
filterQuality = FilterQuality.None
|
||||
)
|
||||
}
|
||||
Spacer(
|
||||
modifier = Modifier
|
||||
.padding(16.dp)
|
||||
)
|
||||
Column {
|
||||
Text("Combine with ${it.fusionAttribute}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Button(
|
||||
onClick = onClickDismiss
|
||||
) {
|
||||
Text("Close")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,5 @@
|
||||
package com.github.nacabaro.vbhelper.screens.homeScreens
|
||||
|
||||
import android.util.Log
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
@ -56,6 +55,7 @@ fun HomeScreen(
|
||||
var adventureMissionsFinished by rememberSaveable { mutableStateOf(false) }
|
||||
var betaWarning by rememberSaveable { mutableStateOf(true) }
|
||||
var collectedItem by remember { mutableStateOf<ItemDtos.PurchasedItem?>(null) }
|
||||
var collectedCurrency by remember { mutableStateOf<Int?>(null) }
|
||||
|
||||
LaunchedEffect(storageRepository, activeMon, collectedItem) {
|
||||
withContext(Dispatchers.IO) {
|
||||
@ -92,7 +92,6 @@ fun HomeScreen(
|
||||
}
|
||||
) { contentPadding ->
|
||||
if (activeMon.value == null || (beData.value == null && vbData.value == null) || transformationHistory.value == null) {
|
||||
Log.d("TetTet", "Something is null")
|
||||
Column (
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Center,
|
||||
@ -103,7 +102,6 @@ fun HomeScreen(
|
||||
Text(text = "Nothing to see here")
|
||||
}
|
||||
} else {
|
||||
Log.d("TetTet", "Something is not null")
|
||||
if (activeMon.value!!.isBemCard) {
|
||||
BEBEmHomeScreen(
|
||||
activeMon = activeMon.value!!,
|
||||
@ -126,8 +124,9 @@ fun HomeScreen(
|
||||
contentPadding = contentPadding,
|
||||
specialMissions = vbSpecialMissions.value,
|
||||
homeScreenController = homeScreenController,
|
||||
onClickCollect = { item ->
|
||||
onClickCollect = { item, currency ->
|
||||
collectedItem = item
|
||||
collectedCurrency = currency
|
||||
}
|
||||
)
|
||||
}
|
||||
@ -137,8 +136,10 @@ fun HomeScreen(
|
||||
if (collectedItem != null) {
|
||||
ObtainedItemDialog(
|
||||
obtainedItem = collectedItem!!,
|
||||
obtainedCurrency = collectedCurrency!!,
|
||||
onClickDismiss = {
|
||||
collectedItem = null
|
||||
collectedCurrency = null
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@ -5,5 +5,5 @@ import com.github.nacabaro.vbhelper.dtos.ItemDtos
|
||||
|
||||
interface HomeScreenController {
|
||||
fun didAdventureMissionsFinish(onCompletion: (Boolean) -> Unit)
|
||||
fun clearSpecialMission(missionId: Long, missionCompletion: SpecialMission.Status, onCleared: (ItemDtos.PurchasedItem?) -> Unit)
|
||||
fun clearSpecialMission(missionId: Long, missionCompletion: SpecialMission.Status, onCleared: (ItemDtos.PurchasedItem?, Int?) -> Unit)
|
||||
}
|
||||
@ -5,6 +5,7 @@ import androidx.lifecycle.lifecycleScope
|
||||
import com.github.cfogrady.vbnfc.vb.SpecialMission
|
||||
import com.github.nacabaro.vbhelper.di.VBHelper
|
||||
import com.github.nacabaro.vbhelper.dtos.ItemDtos
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.launch
|
||||
import java.time.Instant
|
||||
import kotlin.math.roundToInt
|
||||
@ -22,6 +23,7 @@ class HomeScreenControllerImpl(
|
||||
val adventureCharacters = database
|
||||
.adventureDao()
|
||||
.getAdventureCharacters()
|
||||
.first()
|
||||
|
||||
val finishedAdventureCharacters = adventureCharacters.filter { character ->
|
||||
character.finishesAdventure <= currentTime
|
||||
@ -31,7 +33,7 @@ class HomeScreenControllerImpl(
|
||||
}
|
||||
}
|
||||
|
||||
override fun clearSpecialMission(missionId: Long, missionCompletion: SpecialMission.Status, onCleared: (ItemDtos.PurchasedItem?) -> Unit) {
|
||||
override fun clearSpecialMission(missionId: Long, missionCompletion: SpecialMission.Status, onCleared: (ItemDtos.PurchasedItem?, Int?) -> Unit) {
|
||||
componentActivity.lifecycleScope.launch {
|
||||
database
|
||||
.specialMissionDao()
|
||||
@ -41,6 +43,7 @@ class HomeScreenControllerImpl(
|
||||
val randomItem = database
|
||||
.itemDao()
|
||||
.getAllItems()
|
||||
.first()
|
||||
.random()
|
||||
|
||||
val randomItemAmount = (Random.nextFloat() * 5).roundToInt()
|
||||
@ -62,9 +65,13 @@ class HomeScreenControllerImpl(
|
||||
itemType = randomItem.itemType
|
||||
)
|
||||
|
||||
onCleared(purchasedItem)
|
||||
val randomAmount = (2..6).random() * 1000
|
||||
val currentCurrency = application.container.currencyRepository.currencyValue.first()
|
||||
application.container.currencyRepository.setCurrencyValue(currentCurrency + randomAmount)
|
||||
|
||||
onCleared(purchasedItem, randomAmount)
|
||||
} else {
|
||||
onCleared(null)
|
||||
onCleared(null, null)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -34,7 +34,7 @@ fun VBDiMHomeScreen(
|
||||
homeScreenController: HomeScreenControllerImpl,
|
||||
transformationHistory: List<CharacterDtos.TransformationHistory>,
|
||||
contentPadding: PaddingValues,
|
||||
onClickCollect: (ItemDtos.PurchasedItem?) -> Unit
|
||||
onClickCollect: (ItemDtos.PurchasedItem?, Int?) -> Unit
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
|
||||
@ -24,6 +24,7 @@ import com.github.nacabaro.vbhelper.dtos.CharacterDtos
|
||||
import com.github.nacabaro.vbhelper.dtos.ItemDtos
|
||||
import com.github.nacabaro.vbhelper.source.StorageRepository
|
||||
import com.github.nacabaro.vbhelper.utils.BitmapData
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
|
||||
@ -35,7 +36,7 @@ fun ChooseCharacterScreen(
|
||||
) {
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
val application = LocalContext.current.applicationContext as VBHelper
|
||||
val storageRepository = StorageRepository(application.container.db)
|
||||
val storageRepository = StorageRepository(application.container.db, )
|
||||
val characterList = remember {
|
||||
mutableStateOf<List<CharacterDtos.CharacterWithSprites>>(emptyList())
|
||||
}
|
||||
@ -57,7 +58,7 @@ fun ChooseCharacterScreen(
|
||||
characterList.value = storageRepository.getVBCharacters()
|
||||
}
|
||||
else -> {
|
||||
characterList.value = storageRepository.getAllCharacters()
|
||||
characterList.value = storageRepository.getAllCharacters().first()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,164 @@
|
||||
package com.github.nacabaro.vbhelper.screens.itemsScreen
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.window.Dialog
|
||||
import androidx.compose.ui.window.DialogProperties
|
||||
import com.github.nacabaro.vbhelper.R
|
||||
import com.github.nacabaro.vbhelper.domain.items.ItemType
|
||||
import com.github.nacabaro.vbhelper.dtos.ItemDtos
|
||||
import com.github.nacabaro.vbhelper.ui.theme.VBHelperTheme
|
||||
|
||||
@Composable
|
||||
fun ItemDialog(
|
||||
item: ItemDtos.ItemsWithQuantities,
|
||||
onClickCancel: () -> Unit,
|
||||
onClickUse: (() -> Unit)? = null,
|
||||
onClickPurchase: (() -> Unit)? = null,
|
||||
) {
|
||||
Dialog(
|
||||
onDismissRequest = onClickCancel,
|
||||
properties = DialogProperties(
|
||||
dismissOnBackPress = true,
|
||||
dismissOnClickOutside = true
|
||||
)
|
||||
) {
|
||||
Card (
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(16.dp)
|
||||
) {
|
||||
Column (
|
||||
modifier = Modifier
|
||||
.padding(16.dp)
|
||||
) {
|
||||
Row {
|
||||
Box(modifier = Modifier) {
|
||||
// Background image (full size)
|
||||
Icon(
|
||||
painter = painterResource(id = getIconResource(item.itemIcon)),
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.size(96.dp)
|
||||
.align(Alignment.Center)
|
||||
)
|
||||
Icon(
|
||||
painter = painterResource(id = getLengthResource(item.itemLength)),
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.outline,
|
||||
modifier = Modifier
|
||||
.size(64.dp) // Set the size of the overlay image
|
||||
.align(Alignment.BottomEnd) // Align to the top end (top-right corner)
|
||||
)
|
||||
}
|
||||
Column (
|
||||
modifier = Modifier
|
||||
.padding(16.dp)
|
||||
) {
|
||||
Text(
|
||||
fontSize = MaterialTheme.typography.titleLarge.fontSize,
|
||||
text = item.name,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
)
|
||||
}
|
||||
}
|
||||
Text(
|
||||
textAlign = TextAlign.Center,
|
||||
fontSize = MaterialTheme.typography.bodyMedium.fontSize,
|
||||
fontFamily = MaterialTheme.typography.bodyMedium.fontFamily,
|
||||
text = item.description,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(4.dp)
|
||||
)
|
||||
if (onClickPurchase != null) {
|
||||
Text(
|
||||
textAlign = TextAlign.Center,
|
||||
fontSize = MaterialTheme.typography.bodySmall.fontSize,
|
||||
fontFamily = MaterialTheme.typography.bodySmall.fontFamily,
|
||||
text = "Costs ${item.price} credits",
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
)
|
||||
}
|
||||
Text(
|
||||
textAlign = TextAlign.Center,
|
||||
fontSize = MaterialTheme.typography.bodySmall.fontSize,
|
||||
fontFamily = MaterialTheme.typography.bodySmall.fontFamily,
|
||||
text = "You have ${item.quantity} of this item",
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
)
|
||||
|
||||
Row (
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
) {
|
||||
if (onClickUse != null) {
|
||||
Button(
|
||||
onClick = onClickUse
|
||||
) {
|
||||
Text("Use item")
|
||||
}
|
||||
}
|
||||
|
||||
if (onClickPurchase != null) {
|
||||
Button(
|
||||
onClick = onClickPurchase
|
||||
) {
|
||||
Text("Purchase")
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.size(8.dp))
|
||||
Button(
|
||||
onClick = onClickCancel
|
||||
) {
|
||||
Text("Cancel")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
fun PreviewItemDialog() {
|
||||
VBHelperTheme {
|
||||
ItemDialog(
|
||||
item = ItemDtos.ItemsWithQuantities(
|
||||
name = "AP Training x3 (60 min)",
|
||||
description = "Boosts AP during training (for 60 minutes)",
|
||||
itemIcon = R.drawable.baseline_attack_24,
|
||||
itemLength = R.drawable.baseline_60_min_timer,
|
||||
quantity = 19,
|
||||
id = 1,
|
||||
price = 500,
|
||||
itemType = ItemType.BEITEM
|
||||
),
|
||||
onClickUse = { },
|
||||
onClickCancel = { }
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -1,36 +1,23 @@
|
||||
package com.github.nacabaro.vbhelper.screens.itemsScreen
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.aspectRatio
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.window.Dialog
|
||||
import androidx.compose.ui.window.DialogProperties
|
||||
import com.github.nacabaro.vbhelper.R
|
||||
import com.github.nacabaro.vbhelper.ui.theme.VBHelperTheme
|
||||
import com.github.nacabaro.vbhelper.dtos.ItemDtos
|
||||
|
||||
@Composable
|
||||
fun ItemElement(
|
||||
itemIcon: Int,
|
||||
lengthIcon: Int,
|
||||
item: ItemDtos.ItemsWithQuantities,
|
||||
modifier: Modifier = Modifier,
|
||||
onClick: (() -> Unit) = { }
|
||||
) {
|
||||
@ -41,7 +28,7 @@ fun ItemElement(
|
||||
) {
|
||||
Box(modifier = Modifier.fillMaxSize()) {
|
||||
Icon(
|
||||
painter = painterResource(id = itemIcon),
|
||||
painter = painterResource(id = getIconResource(item.itemIcon)),
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.size(96.dp)
|
||||
@ -49,164 +36,14 @@ fun ItemElement(
|
||||
.padding(16.dp)
|
||||
)
|
||||
Icon(
|
||||
painter = painterResource(id = lengthIcon),
|
||||
painter = painterResource(id = getLengthResource(item.itemLength)),
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.surfaceTint,
|
||||
modifier = Modifier
|
||||
.size(48.dp) // Set the size of the overlay image
|
||||
.align(Alignment.TopStart) // Align to the top end (top-right corner)
|
||||
.size(48.dp)
|
||||
.align(Alignment.TopStart)
|
||||
.padding(8.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ItemDialog(
|
||||
name: String,
|
||||
description: String,
|
||||
itemIcon: Int,
|
||||
lengthIcon: Int,
|
||||
amount: Int,
|
||||
onClickUse: () -> Unit,
|
||||
onClickCancel: () -> Unit
|
||||
) {
|
||||
Dialog(
|
||||
onDismissRequest = onClickCancel,
|
||||
properties = DialogProperties(
|
||||
dismissOnBackPress = true,
|
||||
dismissOnClickOutside = true
|
||||
)
|
||||
) {
|
||||
Card (
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(16.dp)
|
||||
) {
|
||||
Column (
|
||||
modifier = Modifier
|
||||
.padding(16.dp)
|
||||
) {
|
||||
Row {
|
||||
Box(modifier = Modifier) {
|
||||
// Background image (full size)
|
||||
Icon(
|
||||
painter = painterResource(id = itemIcon),
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.size(96.dp)
|
||||
.align(Alignment.Center)
|
||||
)
|
||||
Icon(
|
||||
painter = painterResource(id = lengthIcon),
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.outline,
|
||||
modifier = Modifier
|
||||
.size(64.dp) // Set the size of the overlay image
|
||||
.align(Alignment.BottomEnd) // Align to the top end (top-right corner)
|
||||
)
|
||||
}
|
||||
Column (
|
||||
modifier = Modifier
|
||||
.padding(16.dp)
|
||||
) {
|
||||
Text(
|
||||
fontSize = MaterialTheme.typography.titleLarge.fontSize,
|
||||
text = name,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
)
|
||||
}
|
||||
}
|
||||
Text(
|
||||
textAlign = TextAlign.Center,
|
||||
fontSize = MaterialTheme.typography.bodyMedium.fontSize,
|
||||
fontFamily = MaterialTheme.typography.bodyMedium.fontFamily,
|
||||
text = description,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(4.dp)
|
||||
)
|
||||
Text(
|
||||
textAlign = TextAlign.Center,
|
||||
fontSize = MaterialTheme.typography.bodySmall.fontSize,
|
||||
fontFamily = MaterialTheme.typography.bodySmall.fontFamily,
|
||||
text = "You have $amount of this item",
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(8.dp)
|
||||
)
|
||||
Row (
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
) {
|
||||
Button(
|
||||
onClick = onClickUse
|
||||
) {
|
||||
Text("Use item")
|
||||
}
|
||||
Spacer(modifier = Modifier.size(8.dp))
|
||||
Button(
|
||||
onClick = onClickCancel
|
||||
) {
|
||||
Text("Cancel")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getIconResource(index: Int): Int {
|
||||
return when (index) {
|
||||
ItemsScreenControllerImpl.ItemTypes.PPTraining.id -> R.drawable.baseline_agility_24
|
||||
ItemsScreenControllerImpl.ItemTypes.APTraining.id -> R.drawable.baseline_attack_24
|
||||
ItemsScreenControllerImpl.ItemTypes.HPTraining.id -> R.drawable.baseline_shield_24
|
||||
ItemsScreenControllerImpl.ItemTypes.BPTraining.id -> R.drawable.baseline_trophy_24
|
||||
ItemsScreenControllerImpl.ItemTypes.AllTraining.id -> R.drawable.baseline_arrow_up_24
|
||||
6 -> R.drawable.baseline_timer_24
|
||||
7 -> R.drawable.baseline_rank_24
|
||||
8 -> R.drawable.baseline_vitals_24
|
||||
else -> R.drawable.baseline_question_mark_24
|
||||
}
|
||||
}
|
||||
|
||||
fun getLengthResource(index: Int): Int {
|
||||
return when (index) {
|
||||
15 -> R.drawable.baseline_15_min_timer
|
||||
30 -> R.drawable.baseline_30_min_timer
|
||||
60 -> R.drawable.baseline_60_min_timer
|
||||
-60 -> R.drawable.baseline_60_min_timer
|
||||
300 -> R.drawable.baseline_5_hour_timer
|
||||
600 -> R.drawable.baseline_10_hour_timer
|
||||
-720 -> R.drawable.baseline_12_hour_timer
|
||||
-1440 -> R.drawable.baseline_24_hour_timer
|
||||
6000 -> R.drawable.baseline_reset_24
|
||||
1000 -> R.drawable.baseline_single_arrow_up
|
||||
2500 -> R.drawable.baseline_double_arrow_up
|
||||
5000 -> R.drawable.baseline_triple_arrow_up
|
||||
9999 -> R.drawable.baseline_health_24
|
||||
-500 -> R.drawable.baseline_single_arrow_down
|
||||
-1000 -> R.drawable.baseline_double_arrow_down
|
||||
-2500 -> R.drawable.baseline_triple_arrow_down
|
||||
-9999 -> R.drawable.baseline_reset_24
|
||||
else -> R.drawable.baseline_question_mark_24
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
fun PreviewItemDialog() {
|
||||
VBHelperTheme {
|
||||
ItemDialog(
|
||||
name = "AP Training x3 (60 min)",
|
||||
description = "Boosts AP during training (for 60 minutes)",
|
||||
itemIcon = R.drawable.baseline_attack_24,
|
||||
lengthIcon = R.drawable.baseline_60_min_timer,
|
||||
onClickUse = { },
|
||||
onClickCancel = { },
|
||||
amount = 19
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,40 @@
|
||||
package com.github.nacabaro.vbhelper.screens.itemsScreen
|
||||
|
||||
import com.github.nacabaro.vbhelper.R
|
||||
|
||||
fun getIconResource(index: Int): Int {
|
||||
return when (index) {
|
||||
ItemsScreenControllerImpl.ItemTypes.PPTraining.id -> R.drawable.baseline_agility_24
|
||||
ItemsScreenControllerImpl.ItemTypes.APTraining.id -> R.drawable.baseline_attack_24
|
||||
ItemsScreenControllerImpl.ItemTypes.HPTraining.id -> R.drawable.baseline_shield_24
|
||||
ItemsScreenControllerImpl.ItemTypes.BPTraining.id -> R.drawable.baseline_trophy_24
|
||||
ItemsScreenControllerImpl.ItemTypes.AllTraining.id -> R.drawable.baseline_arrow_up_24
|
||||
6 -> R.drawable.baseline_timer_24
|
||||
7 -> R.drawable.baseline_rank_24
|
||||
8 -> R.drawable.baseline_vitals_24
|
||||
else -> R.drawable.baseline_question_mark_24
|
||||
}
|
||||
}
|
||||
|
||||
fun getLengthResource(index: Int): Int {
|
||||
return when (index) {
|
||||
15 -> R.drawable.baseline_15_min_timer
|
||||
30 -> R.drawable.baseline_30_min_timer
|
||||
60 -> R.drawable.baseline_60_min_timer
|
||||
-60 -> R.drawable.baseline_60_min_timer
|
||||
300 -> R.drawable.baseline_5_hour_timer
|
||||
600 -> R.drawable.baseline_10_hour_timer
|
||||
-720 -> R.drawable.baseline_12_hour_timer
|
||||
-1440 -> R.drawable.baseline_24_hour_timer
|
||||
6000 -> R.drawable.baseline_reset_24
|
||||
1000 -> R.drawable.baseline_single_arrow_up
|
||||
2500 -> R.drawable.baseline_double_arrow_up
|
||||
5000 -> R.drawable.baseline_triple_arrow_up
|
||||
9999 -> R.drawable.baseline_health_24
|
||||
-500 -> R.drawable.baseline_single_arrow_down
|
||||
-1000 -> R.drawable.baseline_double_arrow_down
|
||||
-2500 -> R.drawable.baseline_triple_arrow_down
|
||||
-9999 -> R.drawable.baseline_reset_24
|
||||
else -> R.drawable.baseline_question_mark_24
|
||||
}
|
||||
}
|
||||
@ -1,25 +1,35 @@
|
||||
package com.github.nacabaro.vbhelper.screens.itemsScreen
|
||||
|
||||
import android.widget.Toast
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.grid.GridCells
|
||||
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
|
||||
import androidx.compose.foundation.lazy.grid.items
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.navigation.NavController
|
||||
import com.github.nacabaro.vbhelper.database.AppDatabase
|
||||
import com.github.nacabaro.vbhelper.di.VBHelper
|
||||
import com.github.nacabaro.vbhelper.dtos.ItemDtos
|
||||
import com.github.nacabaro.vbhelper.source.CurrencyRepository
|
||||
import com.github.nacabaro.vbhelper.source.ItemsRepository
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Composable
|
||||
fun ItemsStore(
|
||||
@ -27,45 +37,98 @@ fun ItemsStore(
|
||||
) {
|
||||
val application = LocalContext.current.applicationContext as VBHelper
|
||||
val itemsRepository = ItemsRepository(application.container.db)
|
||||
val myItems = remember { mutableStateOf(emptyList<ItemDtos.ItemsWithQuantities>()) }
|
||||
val myItems by itemsRepository.getAllItems().collectAsState(emptyList())
|
||||
|
||||
var selectedElementIndex by remember { mutableStateOf<Int?>(null) }
|
||||
|
||||
LaunchedEffect(itemsRepository) {
|
||||
withContext(Dispatchers.IO) {
|
||||
myItems.value = itemsRepository.getAllItems()
|
||||
}
|
||||
}
|
||||
val currencyRepository = application.container.currencyRepository
|
||||
val currentCurrency = currencyRepository.currencyValue.collectAsState(0)
|
||||
|
||||
if (myItems.value.isEmpty()) {
|
||||
Text("No items")
|
||||
} else {
|
||||
LazyVerticalGrid(
|
||||
columns = GridCells.Fixed(3),
|
||||
modifier = Modifier
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
if (myItems.isEmpty()) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
items(myItems.value) { index ->
|
||||
ItemElement(
|
||||
itemIcon = getIconResource(index.itemIcon),
|
||||
lengthIcon = getLengthResource(index.itemLength),
|
||||
Text("No items")
|
||||
}
|
||||
} else {
|
||||
Column() {
|
||||
Card(
|
||||
modifier = Modifier
|
||||
.padding(8.dp)
|
||||
.fillMaxWidth()
|
||||
) {
|
||||
Text(
|
||||
text = "${currentCurrency.value} credits",
|
||||
modifier = Modifier
|
||||
.padding(8.dp),
|
||||
onClick = {
|
||||
selectedElementIndex = myItems.value.indexOf(index)
|
||||
}
|
||||
.padding(8.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (selectedElementIndex != null) {
|
||||
ItemDialog(
|
||||
name = myItems.value[selectedElementIndex!!].name,
|
||||
description = myItems.value[selectedElementIndex!!].description,
|
||||
itemIcon = getIconResource(myItems.value[selectedElementIndex!!].itemIcon),
|
||||
lengthIcon = getLengthResource(myItems.value[selectedElementIndex!!].itemLength),
|
||||
amount = myItems.value[selectedElementIndex!!].quantity,
|
||||
onClickUse = { },
|
||||
onClickCancel = { selectedElementIndex = null }
|
||||
)
|
||||
LazyVerticalGrid(
|
||||
columns = GridCells.Fixed(3),
|
||||
modifier = Modifier
|
||||
) {
|
||||
items(myItems) { index ->
|
||||
ItemElement(
|
||||
item = index,
|
||||
modifier = Modifier
|
||||
.padding(8.dp),
|
||||
onClick = {
|
||||
selectedElementIndex = myItems.indexOf(index)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (selectedElementIndex != null) {
|
||||
ItemDialog(
|
||||
item = myItems[selectedElementIndex!!],
|
||||
onClickPurchase = {
|
||||
scope.launch {
|
||||
Toast.makeText(
|
||||
application.applicationContext,
|
||||
purchaseItem(
|
||||
application.container.db,
|
||||
myItems[selectedElementIndex!!],
|
||||
currencyRepository
|
||||
),
|
||||
Toast.LENGTH_SHORT
|
||||
).show(
|
||||
)
|
||||
}
|
||||
},
|
||||
onClickCancel = { selectedElementIndex = null }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun purchaseItem(
|
||||
db: AppDatabase,
|
||||
item: ItemDtos.ItemsWithQuantities,
|
||||
currencyRepository: CurrencyRepository
|
||||
): String {
|
||||
if (currencyRepository.currencyValue.first() < item.price) {
|
||||
return "Not enough credits"
|
||||
} else {
|
||||
db
|
||||
.itemDao()
|
||||
.purchaseItem(
|
||||
item.id,
|
||||
1
|
||||
)
|
||||
|
||||
currencyRepository
|
||||
.setCurrencyValue(
|
||||
currencyRepository.currencyValue.first() - item.price
|
||||
)
|
||||
|
||||
return "Purchase successful!"
|
||||
}
|
||||
}
|
||||
@ -9,7 +9,7 @@ import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
|
||||
import androidx.compose.foundation.lazy.grid.items
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
@ -20,11 +20,8 @@ import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.navigation.NavController
|
||||
import com.github.nacabaro.vbhelper.di.VBHelper
|
||||
import com.github.nacabaro.vbhelper.dtos.ItemDtos
|
||||
import com.github.nacabaro.vbhelper.navigation.NavigationItems
|
||||
import com.github.nacabaro.vbhelper.source.ItemsRepository
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
@Composable
|
||||
fun MyItems(
|
||||
@ -32,16 +29,11 @@ fun MyItems(
|
||||
) {
|
||||
val application = LocalContext.current.applicationContext as VBHelper
|
||||
val itemsRepository = ItemsRepository(application.container.db)
|
||||
val myItems = remember { mutableStateOf(emptyList<ItemDtos.ItemsWithQuantities>()) }
|
||||
val myItems by itemsRepository.getUserItems().collectAsState(emptyList())
|
||||
|
||||
var selectedElementIndex by remember { mutableStateOf<Int?>(null) }
|
||||
|
||||
LaunchedEffect(itemsRepository) {
|
||||
withContext(Dispatchers.IO) {
|
||||
myItems.value = itemsRepository.getUserItems()
|
||||
}
|
||||
}
|
||||
|
||||
if (myItems.value.isEmpty()) {
|
||||
if (myItems.isEmpty()) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
@ -54,14 +46,13 @@ fun MyItems(
|
||||
columns = GridCells.Fixed(3),
|
||||
modifier = Modifier
|
||||
) {
|
||||
items(myItems.value) { index ->
|
||||
items(myItems) { index ->
|
||||
ItemElement(
|
||||
itemIcon = getIconResource(index.itemIcon),
|
||||
lengthIcon = getLengthResource(index.itemLength),
|
||||
item = index,
|
||||
modifier = Modifier
|
||||
.padding(8.dp),
|
||||
onClick = {
|
||||
selectedElementIndex = myItems.value.indexOf(index)
|
||||
selectedElementIndex = myItems.indexOf(index)
|
||||
}
|
||||
)
|
||||
}
|
||||
@ -69,11 +60,7 @@ fun MyItems(
|
||||
|
||||
if (selectedElementIndex != null) {
|
||||
ItemDialog(
|
||||
name = myItems.value[selectedElementIndex!!].name,
|
||||
description = myItems.value[selectedElementIndex!!].description,
|
||||
itemIcon = getIconResource(myItems.value[selectedElementIndex!!].itemIcon),
|
||||
lengthIcon = getLengthResource(myItems.value[selectedElementIndex!!].itemLength),
|
||||
amount = myItems.value[selectedElementIndex!!].quantity,
|
||||
item = myItems[selectedElementIndex!!],
|
||||
onClickUse = {
|
||||
navController
|
||||
.navigate(
|
||||
@ -81,7 +68,7 @@ fun MyItems(
|
||||
.ApplyItem.route
|
||||
.replace(
|
||||
"{itemId}",
|
||||
myItems.value[selectedElementIndex!!].id.toString()
|
||||
myItems[selectedElementIndex!!].id.toString()
|
||||
)
|
||||
)
|
||||
selectedElementIndex = null
|
||||
|
||||
@ -23,6 +23,7 @@ import com.github.nacabaro.vbhelper.dtos.ItemDtos
|
||||
@Composable
|
||||
fun ObtainedItemDialog(
|
||||
obtainedItem: ItemDtos.PurchasedItem,
|
||||
obtainedCurrency: Int,
|
||||
onClickDismiss: () -> Unit
|
||||
) {
|
||||
Dialog(
|
||||
@ -84,7 +85,16 @@ fun ObtainedItemDialog(
|
||||
text = "You have obtained ${obtainedItem.itemAmount} of this item",
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(5.dp)
|
||||
.padding(top = 4.dp)
|
||||
)
|
||||
Text(
|
||||
textAlign = TextAlign.Center,
|
||||
fontSize = MaterialTheme.typography.bodySmall.fontSize,
|
||||
fontFamily = MaterialTheme.typography.bodySmall.fontFamily,
|
||||
text = "You also got $obtainedCurrency credits",
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(bottom = 4.dp)
|
||||
)
|
||||
Button(
|
||||
onClick = onClickDismiss,
|
||||
|
||||
@ -0,0 +1,78 @@
|
||||
package com.github.nacabaro.vbhelper.screens.scanScreen
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.navigation.NavController
|
||||
import com.github.nacabaro.vbhelper.components.TopBanner
|
||||
|
||||
@Composable
|
||||
fun ChooseConnectOption(
|
||||
onClickRead: (() -> Unit)? = null,
|
||||
onClickWrite: (() -> Unit)? = null,
|
||||
navController: NavController
|
||||
) {
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopBanner(
|
||||
text = "Scan a Vital Bracelet",
|
||||
onBackClick = {
|
||||
navController.popBackStack()
|
||||
}
|
||||
)
|
||||
}
|
||||
) { contentPadding ->
|
||||
Column(
|
||||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(contentPadding)
|
||||
) {
|
||||
ScanButton(
|
||||
text = "Vital Bracelet to App",
|
||||
disabled = onClickRead == null,
|
||||
onClick = onClickRead?: { },
|
||||
)
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
ScanButton(
|
||||
text = "App to Vital Bracelet",
|
||||
disabled = onClickWrite == null,
|
||||
onClick = onClickWrite?: { },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Composable
|
||||
fun ScanButton(
|
||||
text: String,
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
disabled: Boolean = false,
|
||||
) {
|
||||
Button(
|
||||
onClick = onClick,
|
||||
modifier = modifier,
|
||||
enabled = !disabled,
|
||||
) {
|
||||
Text(
|
||||
text = text,
|
||||
fontSize = 16.sp,
|
||||
modifier = Modifier
|
||||
.padding(4.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -1,38 +1,24 @@
|
||||
package com.github.nacabaro.vbhelper.screens.scanScreen
|
||||
|
||||
import android.widget.Toast
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import com.github.cfogrady.vbnfc.data.NfcCharacter
|
||||
import com.github.nacabaro.vbhelper.ActivityLifecycleListener
|
||||
import com.github.nacabaro.vbhelper.components.TopBanner
|
||||
import com.github.nacabaro.vbhelper.di.VBHelper
|
||||
import com.github.nacabaro.vbhelper.domain.card.Card
|
||||
import com.github.nacabaro.vbhelper.navigation.NavigationItems
|
||||
import com.github.nacabaro.vbhelper.screens.cardScreen.ChooseCard
|
||||
import com.github.nacabaro.vbhelper.screens.scanScreen.screens.ReadingScreen
|
||||
import com.github.nacabaro.vbhelper.screens.scanScreen.screens.WritingScreen
|
||||
import com.github.nacabaro.vbhelper.source.StorageRepository
|
||||
import com.github.nacabaro.vbhelper.source.isMissingSecrets
|
||||
import com.github.nacabaro.vbhelper.source.proto.Secrets
|
||||
@ -55,8 +41,6 @@ fun ScanScreen(
|
||||
val storageRepository = StorageRepository(application.container.db)
|
||||
var nfcCharacter by remember { mutableStateOf<NfcCharacter?>(null) }
|
||||
|
||||
var cardsRead by remember { mutableStateOf<List<Card>?>(null) }
|
||||
|
||||
val context = LocalContext.current
|
||||
|
||||
LaunchedEffect(storageRepository) {
|
||||
@ -73,143 +57,33 @@ fun ScanScreen(
|
||||
}
|
||||
}
|
||||
|
||||
var readingScreen by remember { mutableStateOf(false) }
|
||||
var writingScreen by remember { mutableStateOf(false) }
|
||||
var cardSelectScreen by remember { mutableStateOf(false) }
|
||||
var isDoneReadingCharacter by remember { mutableStateOf(false) }
|
||||
var isDoneSendingCard by remember { mutableStateOf(false) }
|
||||
var isDoneWritingCharacter by remember { mutableStateOf(false) }
|
||||
var readingScreen by remember { mutableStateOf(false) }
|
||||
|
||||
DisposableEffect(readingScreen) {
|
||||
if(readingScreen) {
|
||||
scanScreenController.registerActivityLifecycleListener(
|
||||
SCAN_SCREEN_ACTIVITY_LIFECYCLE_LISTENER,
|
||||
object: ActivityLifecycleListener {
|
||||
override fun onPause() {
|
||||
scanScreenController.cancelRead()
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
scanScreenController.onClickRead(
|
||||
secrets = secrets!!,
|
||||
onComplete = {
|
||||
isDoneReadingCharacter = true
|
||||
},
|
||||
onMultipleCards = { cards ->
|
||||
cardsRead = cards
|
||||
readingScreen = false
|
||||
cardSelectScreen = true
|
||||
isDoneReadingCharacter = true
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
scanScreenController.onClickRead(
|
||||
secrets = secrets!!,
|
||||
onComplete = {
|
||||
isDoneReadingCharacter = true
|
||||
},
|
||||
onMultipleCards = { cards ->
|
||||
cardsRead = cards
|
||||
readingScreen = false
|
||||
cardSelectScreen = true
|
||||
isDoneReadingCharacter = true
|
||||
}
|
||||
)
|
||||
}
|
||||
onDispose {
|
||||
if(readingScreen) {
|
||||
scanScreenController.unregisterActivityLifecycleListener(
|
||||
SCAN_SCREEN_ACTIVITY_LIFECYCLE_LISTENER
|
||||
)
|
||||
scanScreenController.cancelRead()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DisposableEffect(writingScreen, isDoneSendingCard) {
|
||||
if (writingScreen) {
|
||||
scanScreenController.registerActivityLifecycleListener(
|
||||
SCAN_SCREEN_ACTIVITY_LIFECYCLE_LISTENER,
|
||||
object : ActivityLifecycleListener {
|
||||
override fun onPause() {
|
||||
scanScreenController.cancelRead()
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
if (!isDoneSendingCard) {
|
||||
scanScreenController.onClickCheckCard(secrets!!, nfcCharacter!!) {
|
||||
isDoneSendingCard = true
|
||||
}
|
||||
} else if (!isDoneWritingCharacter) {
|
||||
scanScreenController.onClickWrite(secrets!!, nfcCharacter!!) {
|
||||
isDoneWritingCharacter = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
if (secrets != null && nfcCharacter != null) {
|
||||
if (!isDoneSendingCard) {
|
||||
scanScreenController.onClickCheckCard(secrets!!, nfcCharacter!!) {
|
||||
isDoneSendingCard = true
|
||||
}
|
||||
} else if (!isDoneWritingCharacter) {
|
||||
scanScreenController.onClickWrite(secrets!!, nfcCharacter!!) {
|
||||
isDoneWritingCharacter = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onDispose {
|
||||
if(writingScreen) {
|
||||
scanScreenController.unregisterActivityLifecycleListener(SCAN_SCREEN_ACTIVITY_LIFECYCLE_LISTENER)
|
||||
scanScreenController.cancelRead()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isDoneReadingCharacter && !cardSelectScreen) {
|
||||
readingScreen = false
|
||||
navController.navigate(NavigationItems.Home.route)
|
||||
} else if (isDoneSendingCard && isDoneWritingCharacter) {
|
||||
writingScreen = false
|
||||
navController.navigate(NavigationItems.Home.route)
|
||||
LaunchedEffect(storageRepository) {
|
||||
withContext(Dispatchers.IO) {
|
||||
storageRepository
|
||||
.deleteCharacter(characterId!!)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (readingScreen) {
|
||||
ReadingCharacterScreen("Reading character") {
|
||||
readingScreen = false
|
||||
scanScreenController.cancelRead()
|
||||
}
|
||||
} else if (writingScreen) {
|
||||
if (!isDoneSendingCard) {
|
||||
ReadingCharacterScreen("Sending card") {
|
||||
if (writingScreen && nfcCharacter != null && characterId != null) {
|
||||
WritingScreen(
|
||||
scanScreenController = scanScreenController,
|
||||
nfcCharacter = nfcCharacter!!,
|
||||
characterId = characterId,
|
||||
onComplete = {
|
||||
writingScreen = false
|
||||
scanScreenController.cancelRead()
|
||||
}
|
||||
} else if (!isDoneWritingCharacter) {
|
||||
ReadingCharacterScreen("Writing character") {
|
||||
isDoneSendingCard = false
|
||||
navController.navigate(NavigationItems.Home.route)
|
||||
},
|
||||
onCancel = {
|
||||
writingScreen = false
|
||||
scanScreenController.cancelRead()
|
||||
navController.navigate(NavigationItems.Home.route)
|
||||
}
|
||||
}
|
||||
} else if (cardSelectScreen) {
|
||||
ChooseCard(
|
||||
cards = cardsRead!!,
|
||||
onCardSelected = { card ->
|
||||
cardSelectScreen = false
|
||||
scanScreenController.flushCharacter(card.id)
|
||||
)
|
||||
} else if (readingScreen) {
|
||||
ReadingScreen(
|
||||
scanScreenController = scanScreenController,
|
||||
onCancel = {
|
||||
readingScreen = false
|
||||
navController.navigate(NavigationItems.Home.route)
|
||||
},
|
||||
onComplete = {
|
||||
readingScreen = false
|
||||
navController.navigate(NavigationItems.Home.route)
|
||||
}
|
||||
)
|
||||
} else {
|
||||
@ -247,66 +121,8 @@ fun ScanScreen(
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ChooseConnectOption(
|
||||
onClickRead: (() -> Unit)? = null,
|
||||
onClickWrite: (() -> Unit)? = null,
|
||||
navController: NavController
|
||||
) {
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopBanner(
|
||||
text = "Scan a Vital Bracelet",
|
||||
onBackClick = {
|
||||
navController.popBackStack()
|
||||
}
|
||||
)
|
||||
}
|
||||
) { contentPadding ->
|
||||
Column(
|
||||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(contentPadding)
|
||||
) {
|
||||
ScanButton(
|
||||
text = "Vital Bracelet to App",
|
||||
disabled = onClickRead == null,
|
||||
onClick = onClickRead?: { },
|
||||
)
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
ScanButton(
|
||||
text = "App to Vital Bracelet",
|
||||
disabled = onClickWrite == null,
|
||||
onClick = onClickWrite?: { },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Composable
|
||||
fun ScanButton(
|
||||
text: String,
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
disabled: Boolean = false,
|
||||
) {
|
||||
Button(
|
||||
onClick = onClick,
|
||||
modifier = modifier,
|
||||
enabled = !disabled,
|
||||
) {
|
||||
Text(
|
||||
text = text,
|
||||
fontSize = 16.sp,
|
||||
modifier = Modifier
|
||||
.padding(4.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
fun ScanScreenPreview() {
|
||||
|
||||
@ -1,13 +1,11 @@
|
||||
package com.github.nacabaro.vbhelper.screens.scanScreen.converters
|
||||
|
||||
import android.util.Log
|
||||
import androidx.activity.ComponentActivity
|
||||
import com.github.cfogrady.vbnfc.be.BENfcCharacter
|
||||
import com.github.cfogrady.vbnfc.data.NfcCharacter
|
||||
import com.github.cfogrady.vbnfc.vb.VBNfcCharacter
|
||||
import com.github.nacabaro.vbhelper.di.VBHelper
|
||||
import com.github.nacabaro.vbhelper.domain.card.Card
|
||||
import com.github.nacabaro.vbhelper.domain.card.CardProgress
|
||||
import com.github.nacabaro.vbhelper.domain.device_data.BECharacterData
|
||||
import com.github.nacabaro.vbhelper.domain.device_data.SpecialMissions
|
||||
import com.github.nacabaro.vbhelper.domain.device_data.UserCharacter
|
||||
@ -152,15 +150,13 @@ class FromNfcConverter (
|
||||
nfcCharacter: NfcCharacter,
|
||||
cardData: Card
|
||||
) {
|
||||
val currentCardProgress = CardProgress(
|
||||
cardId = cardData.id,
|
||||
currentStage = nfcCharacter.nextAdventureMissionStage.toInt(),
|
||||
unlocked = nfcCharacter.nextAdventureMissionStage.toInt() > cardData.stageCount
|
||||
)
|
||||
|
||||
database
|
||||
.cardProgressDao()
|
||||
.updateDimProgress(currentCardProgress)
|
||||
.updateCardProgress(
|
||||
currentStage = nfcCharacter.nextAdventureMissionStage.toInt(),
|
||||
cardId = cardData.id,
|
||||
unlocked = nfcCharacter.nextAdventureMissionStage.toInt() > cardData.stageCount,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -252,10 +248,11 @@ class FromNfcConverter (
|
||||
) {
|
||||
val vitalsHistoryWatch = nfcCharacter.vitalHistory
|
||||
val vitalsHistory = vitalsHistoryWatch.map { historyElement ->
|
||||
Log.d("VitalsHistory", "${historyElement.year.toInt()} ${historyElement.month.toInt()} ${historyElement.day.toInt()}")
|
||||
val year = if (historyElement.year.toInt() !in 2021 .. 2035) 0 else historyElement.year.toInt()
|
||||
|
||||
VitalsHistory(
|
||||
charId = characterId,
|
||||
year = historyElement.year.toInt(),
|
||||
year = year,
|
||||
month = historyElement.month.toInt(),
|
||||
day = historyElement.day.toInt(),
|
||||
vitalPoints = historyElement.vitalsGained.toInt()
|
||||
|
||||
@ -14,6 +14,7 @@ import com.github.nacabaro.vbhelper.di.VBHelper
|
||||
import com.github.nacabaro.vbhelper.domain.device_data.UserCharacter
|
||||
import com.github.nacabaro.vbhelper.dtos.CharacterDtos
|
||||
import com.github.nacabaro.vbhelper.utils.DeviceType
|
||||
import kotlinx.coroutines.flow.first
|
||||
import java.util.Date
|
||||
|
||||
class ToNfcConverter(
|
||||
@ -38,14 +39,10 @@ class ToNfcConverter(
|
||||
.characterDao()
|
||||
.getCharacterInfo(userCharacter.charId)
|
||||
|
||||
val currentCardStage = database
|
||||
.cardProgressDao()
|
||||
.getCardProgress(characterInfo.cardId)
|
||||
|
||||
return if (userCharacter.characterType == DeviceType.BEDevice)
|
||||
nfcToBENfc(characterId, characterInfo, currentCardStage, userCharacter)
|
||||
nfcToBENfc(characterId, characterInfo, userCharacter)
|
||||
else
|
||||
nfcToVBNfc(characterId, characterInfo, currentCardStage, userCharacter)
|
||||
nfcToVBNfc(characterId, characterInfo, userCharacter)
|
||||
}
|
||||
|
||||
|
||||
@ -53,7 +50,6 @@ class ToNfcConverter(
|
||||
private suspend fun nfcToVBNfc(
|
||||
characterId: Long,
|
||||
characterInfo: CharacterDtos.CardCharacterInfo,
|
||||
currentCardStage: Int,
|
||||
userCharacter: UserCharacter
|
||||
): VBNfcCharacter {
|
||||
val vbData = database
|
||||
@ -70,7 +66,7 @@ class ToNfcConverter(
|
||||
stage = characterInfo.stage.toByte(),
|
||||
attribute = characterInfo.attribute,
|
||||
ageInDays = userCharacter.ageInDays.toByte(),
|
||||
nextAdventureMissionStage = currentCardStage.toByte(),
|
||||
nextAdventureMissionStage = characterInfo.currentStage.toByte(),
|
||||
mood = userCharacter.mood.toByte(),
|
||||
vitalPoints = userCharacter.vitalPoints.toUShort(),
|
||||
transformationCountdownInMinutes = userCharacter.transformationCountdown.toUShort(),
|
||||
@ -101,6 +97,7 @@ class ToNfcConverter(
|
||||
val cardData = database
|
||||
.cardDao()
|
||||
.getCardByCharacterId(userCharacter.id)
|
||||
.first()
|
||||
|
||||
val appReserved = Array<UShort>(3) {
|
||||
0u
|
||||
@ -173,7 +170,6 @@ class ToNfcConverter(
|
||||
private suspend fun nfcToBENfc(
|
||||
characterId: Long,
|
||||
characterInfo: CharacterDtos.CardCharacterInfo,
|
||||
currentCardStage: Int,
|
||||
userCharacter: UserCharacter
|
||||
): BENfcCharacter {
|
||||
val beData = database
|
||||
@ -188,7 +184,7 @@ class ToNfcConverter(
|
||||
stage = characterInfo.stage.toByte(),
|
||||
attribute = characterInfo.attribute,
|
||||
ageInDays = userCharacter.ageInDays.toByte(),
|
||||
nextAdventureMissionStage = currentCardStage.toByte(),
|
||||
nextAdventureMissionStage = characterInfo.currentStage.toByte(),
|
||||
mood = userCharacter.mood.toByte(),
|
||||
vitalPoints = userCharacter.vitalPoints.toUShort(),
|
||||
itemEffectMentalStateValue = beData.itemEffectMentalStateValue.toByte(),
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
package com.github.nacabaro.vbhelper.screens.scanScreen
|
||||
package com.github.nacabaro.vbhelper.screens.scanScreen.screens
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
@ -14,13 +14,16 @@ import androidx.compose.ui.unit.dp
|
||||
import com.github.nacabaro.vbhelper.components.TopBanner
|
||||
|
||||
@Composable
|
||||
fun ReadingCharacterScreen(
|
||||
fun ActionScreen(
|
||||
topBannerText: String,
|
||||
onClickCancel: () -> Unit,
|
||||
) {
|
||||
Scaffold (
|
||||
topBar = {
|
||||
TopBanner(topBannerText)
|
||||
TopBanner(
|
||||
text = topBannerText,
|
||||
onBackClick = onClickCancel
|
||||
)
|
||||
}
|
||||
) { innerPadding ->
|
||||
Column (
|
||||
@ -0,0 +1,59 @@
|
||||
package com.github.nacabaro.vbhelper.screens.scanScreen.screens
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.github.nacabaro.vbhelper.components.TopBanner
|
||||
|
||||
@Composable
|
||||
fun ReadCharacterScreen(
|
||||
onClickCancel: () -> Unit,
|
||||
onClickConfirm: () -> Unit
|
||||
) {
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopBanner(
|
||||
text = "Read character",
|
||||
onBackClick = onClickCancel
|
||||
)
|
||||
}
|
||||
) { innerPadding ->
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Center,
|
||||
modifier = Modifier
|
||||
.padding(innerPadding)
|
||||
.fillMaxSize()
|
||||
) {
|
||||
Text(
|
||||
text = "Prepare your device!",
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
|
||||
Text(
|
||||
text = "Go to connect and when ready press confirm!",
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
|
||||
Spacer(
|
||||
modifier = Modifier.padding(8.dp)
|
||||
)
|
||||
|
||||
Button(
|
||||
onClick = onClickConfirm,
|
||||
) {
|
||||
Text("Confirm")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,109 @@
|
||||
package com.github.nacabaro.vbhelper.screens.scanScreen.screens
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import com.github.nacabaro.vbhelper.ActivityLifecycleListener
|
||||
import com.github.nacabaro.vbhelper.domain.card.Card
|
||||
import com.github.nacabaro.vbhelper.screens.cardScreen.ChooseCard
|
||||
import com.github.nacabaro.vbhelper.screens.scanScreen.SCAN_SCREEN_ACTIVITY_LIFECYCLE_LISTENER
|
||||
import com.github.nacabaro.vbhelper.screens.scanScreen.ScanScreenController
|
||||
|
||||
@Composable
|
||||
fun ReadingScreen(
|
||||
scanScreenController: ScanScreenController,
|
||||
onCancel: () -> Unit,
|
||||
onComplete: () -> Unit
|
||||
) {
|
||||
val secrets by scanScreenController.secretsFlow.collectAsState(null)
|
||||
|
||||
var cardsRead by remember { mutableStateOf<List<Card>?>(null) }
|
||||
|
||||
var readingScreen by remember { mutableStateOf(false) }
|
||||
var isDoneReadingCharacter by remember { mutableStateOf(false) }
|
||||
var cardSelectScreen by remember { mutableStateOf(false) }
|
||||
|
||||
DisposableEffect(readingScreen) {
|
||||
if(readingScreen) {
|
||||
scanScreenController.registerActivityLifecycleListener(
|
||||
SCAN_SCREEN_ACTIVITY_LIFECYCLE_LISTENER,
|
||||
object: ActivityLifecycleListener {
|
||||
override fun onPause() {
|
||||
scanScreenController.cancelRead()
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
scanScreenController.onClickRead(
|
||||
secrets = secrets!!,
|
||||
onComplete = {
|
||||
isDoneReadingCharacter = true
|
||||
},
|
||||
onMultipleCards = { cards ->
|
||||
cardsRead = cards
|
||||
readingScreen = false
|
||||
cardSelectScreen = true
|
||||
isDoneReadingCharacter = true
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
scanScreenController.onClickRead(
|
||||
secrets = secrets!!,
|
||||
onComplete = {
|
||||
isDoneReadingCharacter = true
|
||||
},
|
||||
onMultipleCards = { cards ->
|
||||
cardsRead = cards
|
||||
readingScreen = false
|
||||
cardSelectScreen = true
|
||||
isDoneReadingCharacter = true
|
||||
}
|
||||
)
|
||||
}
|
||||
onDispose {
|
||||
if(readingScreen) {
|
||||
scanScreenController.unregisterActivityLifecycleListener(
|
||||
SCAN_SCREEN_ACTIVITY_LIFECYCLE_LISTENER
|
||||
)
|
||||
scanScreenController.cancelRead()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isDoneReadingCharacter && !cardSelectScreen) {
|
||||
readingScreen = false
|
||||
onComplete()
|
||||
}
|
||||
|
||||
if (!readingScreen) {
|
||||
ReadCharacterScreen(
|
||||
onClickConfirm = {
|
||||
readingScreen = true
|
||||
},
|
||||
onClickCancel = {
|
||||
onCancel()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
if (readingScreen) {
|
||||
ActionScreen("Reading character") {
|
||||
readingScreen = false
|
||||
scanScreenController.cancelRead()
|
||||
onCancel()
|
||||
}
|
||||
} else if (cardSelectScreen) {
|
||||
ChooseCard(
|
||||
cards = cardsRead!!,
|
||||
onCardSelected = { card ->
|
||||
cardSelectScreen = false
|
||||
scanScreenController.flushCharacter(card.id)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,132 @@
|
||||
package com.github.nacabaro.vbhelper.screens.scanScreen.screens
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.CardColors
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.contentColorFor
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.FilterQuality
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.github.nacabaro.vbhelper.components.TopBanner
|
||||
import com.github.nacabaro.vbhelper.di.VBHelper
|
||||
import com.github.nacabaro.vbhelper.domain.card.Card
|
||||
import com.github.nacabaro.vbhelper.source.ScanRepository
|
||||
import com.github.nacabaro.vbhelper.utils.BitmapData
|
||||
import com.github.nacabaro.vbhelper.utils.getImageBitmap
|
||||
|
||||
@Composable
|
||||
fun WriteCardScreen(
|
||||
characterId: Long,
|
||||
onClickCancel: () -> Unit,
|
||||
onClickConfirm: () -> Unit
|
||||
) {
|
||||
val application = LocalContext.current.applicationContext as VBHelper
|
||||
val database = application.container.db
|
||||
val scanRepository = ScanRepository(database)
|
||||
val cardDetails by scanRepository.getCardDetails(characterId).collectAsState(Card(
|
||||
id = 0,
|
||||
cardId = 0,
|
||||
name = "",
|
||||
logo = byteArrayOf(),
|
||||
logoHeight = 0,
|
||||
logoWidth = 0,
|
||||
stageCount = 0,
|
||||
isBEm = false
|
||||
))
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopBanner(
|
||||
text = "Writing card details",
|
||||
onBackClick = onClickCancel
|
||||
)
|
||||
}
|
||||
) { innerPadding ->
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Center,
|
||||
modifier = Modifier
|
||||
.padding(innerPadding)
|
||||
.fillMaxSize()
|
||||
) {
|
||||
Card (
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(8.dp)
|
||||
) {
|
||||
Row (
|
||||
modifier = Modifier.padding(16.dp),
|
||||
){
|
||||
if (cardDetails.logoHeight > 0 && cardDetails.logoWidth > 0) {
|
||||
val charaBitmapData = BitmapData(
|
||||
bitmap = cardDetails.logo,
|
||||
width = cardDetails.logoWidth,
|
||||
height = cardDetails.logoHeight
|
||||
)
|
||||
val charaImageBitmapData = charaBitmapData.getImageBitmap(
|
||||
context = LocalContext.current,
|
||||
multiplier = 4,
|
||||
obscure = false
|
||||
)
|
||||
|
||||
Card (
|
||||
colors = CardColors(
|
||||
containerColor = MaterialTheme.colorScheme.surfaceVariant,
|
||||
contentColor = MaterialTheme.colorScheme.contentColorFor(
|
||||
backgroundColor = MaterialTheme.colorScheme.surfaceVariant,
|
||||
),
|
||||
disabledContainerColor = MaterialTheme.colorScheme.surfaceVariant,
|
||||
disabledContentColor = MaterialTheme.colorScheme.contentColorFor(
|
||||
backgroundColor = MaterialTheme.colorScheme.surfaceVariant,
|
||||
)
|
||||
)
|
||||
) {
|
||||
Image(
|
||||
bitmap = charaImageBitmapData.imageBitmap,
|
||||
contentDescription = "Icon",
|
||||
modifier = Modifier
|
||||
.size(charaImageBitmapData.dpWidth)
|
||||
.padding(8.dp),
|
||||
filterQuality = FilterQuality.None
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(
|
||||
modifier = Modifier.width(8.dp)
|
||||
)
|
||||
|
||||
Column {
|
||||
Text("Get your device Ready!")
|
||||
Text("You will need ${cardDetails.name} card!")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Button(
|
||||
onClick = onClickConfirm,
|
||||
) {
|
||||
Text("Confirm")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,135 @@
|
||||
package com.github.nacabaro.vbhelper.screens.scanScreen.screens
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.CardColors
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.contentColorFor
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.FilterQuality
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.github.nacabaro.vbhelper.components.TopBanner
|
||||
import com.github.nacabaro.vbhelper.di.VBHelper
|
||||
import com.github.nacabaro.vbhelper.domain.card.Card
|
||||
import com.github.nacabaro.vbhelper.source.ScanRepository
|
||||
import com.github.nacabaro.vbhelper.utils.BitmapData
|
||||
import com.github.nacabaro.vbhelper.utils.getImageBitmap
|
||||
|
||||
@Composable
|
||||
fun WriteCharacterScreen(
|
||||
characterId: Long,
|
||||
onClickCancel: () -> Unit,
|
||||
onClickConfirm: () -> Unit
|
||||
) {
|
||||
val application = LocalContext.current.applicationContext as VBHelper
|
||||
val database = application.container.db
|
||||
val scanRepository = ScanRepository(database)
|
||||
val cardDetails by scanRepository.getCardDetails(characterId).collectAsState(Card(
|
||||
id = 0,
|
||||
cardId = 0,
|
||||
name = "",
|
||||
logo = byteArrayOf(),
|
||||
logoHeight = 0,
|
||||
logoWidth = 0,
|
||||
stageCount = 0,
|
||||
isBEm = false
|
||||
))
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopBanner(
|
||||
text = "Writing character",
|
||||
onBackClick = onClickCancel
|
||||
)
|
||||
}
|
||||
) { innerPadding ->
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Center,
|
||||
modifier = Modifier
|
||||
.padding(innerPadding)
|
||||
.fillMaxSize()
|
||||
) {
|
||||
Card (
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(8.dp)
|
||||
) {
|
||||
Row (
|
||||
modifier = Modifier.padding(16.dp),
|
||||
){
|
||||
if (cardDetails.logoHeight > 0 && cardDetails.logoWidth > 0) {
|
||||
val charaBitmapData = BitmapData(
|
||||
bitmap = cardDetails.logo,
|
||||
width = cardDetails.logoWidth,
|
||||
height = cardDetails.logoHeight
|
||||
)
|
||||
val charaImageBitmapData = charaBitmapData.getImageBitmap(
|
||||
context = LocalContext.current,
|
||||
multiplier = 4,
|
||||
obscure = false
|
||||
)
|
||||
|
||||
Card (
|
||||
colors = CardColors(
|
||||
containerColor = MaterialTheme.colorScheme.surfaceVariant,
|
||||
contentColor = MaterialTheme.colorScheme.contentColorFor(
|
||||
backgroundColor = MaterialTheme.colorScheme.surfaceVariant,
|
||||
),
|
||||
disabledContainerColor = MaterialTheme.colorScheme.surfaceVariant,
|
||||
disabledContentColor = MaterialTheme.colorScheme.contentColorFor(
|
||||
backgroundColor = MaterialTheme.colorScheme.surfaceVariant,
|
||||
)
|
||||
)
|
||||
) {
|
||||
Image(
|
||||
bitmap = charaImageBitmapData.imageBitmap,
|
||||
contentDescription = "Icon",
|
||||
modifier = Modifier
|
||||
.size(charaImageBitmapData.dpWidth)
|
||||
.padding(8.dp),
|
||||
filterQuality = FilterQuality.None
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(
|
||||
modifier = Modifier.width(8.dp)
|
||||
)
|
||||
|
||||
Column {
|
||||
Text("Card installed successfully!!")
|
||||
Text("Wait until your device is ready, then tap 'Confirm'")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
Button(
|
||||
onClick = onClickConfirm,
|
||||
) {
|
||||
Text("Confirm")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,140 @@
|
||||
package com.github.nacabaro.vbhelper.screens.scanScreen.screens
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import com.github.cfogrady.vbnfc.data.NfcCharacter
|
||||
import com.github.nacabaro.vbhelper.ActivityLifecycleListener
|
||||
import com.github.nacabaro.vbhelper.di.VBHelper
|
||||
import com.github.nacabaro.vbhelper.screens.scanScreen.SCAN_SCREEN_ACTIVITY_LIFECYCLE_LISTENER
|
||||
import com.github.nacabaro.vbhelper.screens.scanScreen.ScanScreenController
|
||||
import com.github.nacabaro.vbhelper.source.StorageRepository
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
@Composable
|
||||
fun WritingScreen(
|
||||
scanScreenController: ScanScreenController,
|
||||
characterId: Long,
|
||||
nfcCharacter: NfcCharacter,
|
||||
onComplete: () -> Unit,
|
||||
onCancel: () -> Unit,
|
||||
) {
|
||||
val secrets by scanScreenController.secretsFlow.collectAsState(null)
|
||||
|
||||
val application = LocalContext.current.applicationContext as VBHelper
|
||||
val storageRepository = StorageRepository(application.container.db)
|
||||
|
||||
var writing by remember { mutableStateOf(false) }
|
||||
var writingScreen by remember { mutableStateOf(false) }
|
||||
var writingConfirmScreen by remember { mutableStateOf(false) }
|
||||
var isDoneSendingCard by remember { mutableStateOf(false) }
|
||||
var isDoneWritingCharacter by remember { mutableStateOf(false) }
|
||||
|
||||
DisposableEffect(writing) {
|
||||
if (writing) {
|
||||
scanScreenController.registerActivityLifecycleListener(
|
||||
SCAN_SCREEN_ACTIVITY_LIFECYCLE_LISTENER,
|
||||
object : ActivityLifecycleListener {
|
||||
override fun onPause() {
|
||||
scanScreenController.cancelRead()
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
if (!isDoneSendingCard) {
|
||||
scanScreenController.onClickCheckCard(secrets!!, nfcCharacter) {
|
||||
isDoneSendingCard = true
|
||||
}
|
||||
} else if (!isDoneWritingCharacter) {
|
||||
scanScreenController.onClickWrite(secrets!!, nfcCharacter) {
|
||||
isDoneWritingCharacter = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
if (secrets != null) {
|
||||
if (!isDoneSendingCard) {
|
||||
scanScreenController.onClickCheckCard(secrets!!, nfcCharacter) {
|
||||
isDoneSendingCard = true
|
||||
}
|
||||
} else if (!isDoneWritingCharacter) {
|
||||
scanScreenController.onClickWrite(secrets!!, nfcCharacter) {
|
||||
isDoneWritingCharacter = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onDispose {
|
||||
if (writing) {
|
||||
scanScreenController.unregisterActivityLifecycleListener(
|
||||
SCAN_SCREEN_ACTIVITY_LIFECYCLE_LISTENER
|
||||
)
|
||||
scanScreenController.cancelRead()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!writingScreen) {
|
||||
writing = false
|
||||
WriteCardScreen (
|
||||
characterId = characterId,
|
||||
onClickCancel = {
|
||||
scanScreenController.cancelRead()
|
||||
onCancel()
|
||||
},
|
||||
onClickConfirm = {
|
||||
writingScreen = true
|
||||
}
|
||||
)
|
||||
} else if (!isDoneSendingCard) {
|
||||
writing = true
|
||||
ActionScreen("Sending card") {
|
||||
scanScreenController.cancelRead()
|
||||
onCancel()
|
||||
}
|
||||
} else if (!writingConfirmScreen) {
|
||||
writing = false
|
||||
WriteCharacterScreen (
|
||||
characterId = characterId,
|
||||
onClickCancel = {
|
||||
scanScreenController.cancelRead()
|
||||
onCancel()
|
||||
},
|
||||
onClickConfirm = {
|
||||
writingConfirmScreen = true
|
||||
}
|
||||
)
|
||||
} else if (!isDoneWritingCharacter) {
|
||||
writing = true
|
||||
ActionScreen("Writing character") {
|
||||
isDoneSendingCard = false
|
||||
scanScreenController.cancelRead()
|
||||
onCancel()
|
||||
}
|
||||
}
|
||||
|
||||
var completedWriting by remember { mutableStateOf(false) }
|
||||
|
||||
LaunchedEffect(isDoneSendingCard, isDoneWritingCharacter) {
|
||||
withContext(Dispatchers.IO) {
|
||||
if (isDoneSendingCard && isDoneWritingCharacter) {
|
||||
storageRepository
|
||||
.deleteCharacter(characterId)
|
||||
completedWriting = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (completedWriting) {
|
||||
onComplete()
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,7 @@
|
||||
package com.github.nacabaro.vbhelper.screens.settingsScreen
|
||||
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
@ -13,6 +15,7 @@ import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
@ -25,6 +28,8 @@ fun SettingsScreen(
|
||||
navController: NavController,
|
||||
settingsScreenController: SettingsScreenControllerImpl,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
|
||||
Scaffold (
|
||||
topBar = {
|
||||
TopBanner(
|
||||
@ -44,19 +49,22 @@ fun SettingsScreen(
|
||||
.verticalScroll(rememberScrollState())
|
||||
) {
|
||||
SettingsSection("NFC Communication")
|
||||
SettingsEntry(title = "Import APK", description = "Import Secrets From Vital Arean 2.1.0 APK") {
|
||||
SettingsEntry(title = "Import APK", description = "Import Secrets From Vital Arena 2.1.0 APK") {
|
||||
settingsScreenController.onClickImportApk()
|
||||
}
|
||||
SettingsSection("DiM/BEm management")
|
||||
SettingsEntry(title = "Import card", description = "Import DiM/BEm card file") {
|
||||
settingsScreenController.onClickImportCard()
|
||||
}
|
||||
SettingsEntry(title = "Rename DiM/BEm", description = "Set card name") { }
|
||||
SettingsSection("About and credits")
|
||||
SettingsEntry(title = "Credits", description = "Credits") {
|
||||
navController.navigate(NavigationItems.Credits.route)
|
||||
}
|
||||
SettingsEntry(title = "About", description = "About") { }
|
||||
SettingsEntry(title = "About", description = "About") {
|
||||
val browserIntent = Intent(
|
||||
Intent.ACTION_VIEW, Uri.parse("https://github.com/nacabaro/vbhelper/"))
|
||||
context.startActivity(browserIntent)
|
||||
}
|
||||
SettingsSection("Data management")
|
||||
SettingsEntry(title = "Export data", description = "Export application database") {
|
||||
settingsScreenController.onClickOpenDirectory()
|
||||
|
||||
@ -3,36 +3,24 @@ package com.github.nacabaro.vbhelper.screens.settingsScreen
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import kotlinx.coroutines.launch
|
||||
import android.net.Uri
|
||||
import android.provider.OpenableColumns
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import com.github.cfogrady.vb.dim.card.BemCard
|
||||
import com.github.cfogrady.vb.dim.card.DimCard
|
||||
import com.github.cfogrady.vb.dim.card.DimReader
|
||||
import com.github.cfogrady.vbnfc.data.NfcCharacter
|
||||
import com.github.nacabaro.vbhelper.database.AppDatabase
|
||||
import com.github.nacabaro.vbhelper.di.VBHelper
|
||||
import com.github.nacabaro.vbhelper.domain.characters.Sprite
|
||||
import com.github.nacabaro.vbhelper.domain.card.Card
|
||||
import com.github.nacabaro.vbhelper.domain.card.CardProgress
|
||||
import com.github.nacabaro.vbhelper.domain.card.CharacterData
|
||||
import com.github.nacabaro.vbhelper.screens.settingsScreen.controllers.CardImportController
|
||||
import com.github.nacabaro.vbhelper.screens.settingsScreen.controllers.DatabaseManagementController
|
||||
import com.github.nacabaro.vbhelper.source.ApkSecretsImporter
|
||||
import com.github.nacabaro.vbhelper.source.SecretsImporter
|
||||
import com.github.nacabaro.vbhelper.source.SecretsRepository
|
||||
import com.github.nacabaro.vbhelper.source.proto.Secrets
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import java.io.File
|
||||
import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
|
||||
|
||||
class SettingsScreenControllerImpl(
|
||||
private val context: ComponentActivity,
|
||||
): SettingsScreenController {
|
||||
private val roomDbName = "internalDb"
|
||||
private val filePickerLauncher: ActivityResultLauncher<String>
|
||||
private val filePickerOpenerLauncher: ActivityResultLauncher<Array<String>>
|
||||
private val filePickerApk: ActivityResultLauncher<Array<String>>
|
||||
@ -41,13 +29,17 @@ class SettingsScreenControllerImpl(
|
||||
private val application = context.applicationContext as VBHelper
|
||||
private val secretsRepository: SecretsRepository = application.container.dataStoreSecretsRepository
|
||||
private val database: AppDatabase = application.container.db
|
||||
private val databaseManagementController = DatabaseManagementController(
|
||||
componentActivity = context,
|
||||
application = application
|
||||
)
|
||||
|
||||
init {
|
||||
filePickerLauncher = context.registerForActivityResult(
|
||||
ActivityResultContracts.CreateDocument("application/octet-stream")
|
||||
) { uri ->
|
||||
if (uri != null) {
|
||||
exportDatabase(uri)
|
||||
databaseManagementController.exportDatabase(uri)
|
||||
} else {
|
||||
context.runOnUiThread {
|
||||
Toast.makeText(context, "No destination selected", Toast.LENGTH_SHORT)
|
||||
@ -60,7 +52,7 @@ class SettingsScreenControllerImpl(
|
||||
ActivityResultContracts.OpenDocument()
|
||||
) { uri ->
|
||||
if (uri != null) {
|
||||
importDatabase(uri)
|
||||
databaseManagementController.importDatabase(uri)
|
||||
} else {
|
||||
context.runOnUiThread {
|
||||
Toast.makeText(context, "No source selected", Toast.LENGTH_SHORT).show()
|
||||
@ -109,212 +101,14 @@ class SettingsScreenControllerImpl(
|
||||
filePickerCard.launch(arrayOf("*/*"))
|
||||
}
|
||||
|
||||
private suspend fun importEvoData(
|
||||
cardId: Long,
|
||||
card: com.github.cfogrady.vb.dim.card.Card<*, *, *, *, *, *>
|
||||
) {
|
||||
for (index in 0 until card.transformationRequirements.transformationEntries.size) {
|
||||
val evo = card.transformationRequirements.transformationEntries[index]
|
||||
|
||||
var transformationTimerHours: Int
|
||||
var unlockAdventureLevel: Int
|
||||
|
||||
if (card is BemCard) {
|
||||
transformationTimerHours = card
|
||||
.transformationRequirements
|
||||
.transformationEntries[index]
|
||||
.minutesUntilTransformation / 60
|
||||
unlockAdventureLevel = if (
|
||||
card
|
||||
.transformationRequirements
|
||||
.transformationEntries[index]
|
||||
.requiredCompletedAdventureLevel == 65535
|
||||
) {
|
||||
0
|
||||
} else {
|
||||
card
|
||||
.transformationRequirements
|
||||
.transformationEntries[index]
|
||||
.requiredCompletedAdventureLevel
|
||||
}
|
||||
} else {
|
||||
transformationTimerHours = (card as DimCard)
|
||||
.transformationRequirements
|
||||
.transformationEntries[index]
|
||||
.hoursUntilEvolution
|
||||
unlockAdventureLevel = if (
|
||||
card
|
||||
.adventureLevels
|
||||
.levels
|
||||
.last()
|
||||
.bossCharacterIndex == card.transformationRequirements.transformationEntries[index].toCharacterIndex
|
||||
) {
|
||||
14
|
||||
/*
|
||||
Magic number incoming!!
|
||||
|
||||
In the case of DiMCards, stage 15 is the one that unlocks the locked character.
|
||||
We know it is a locked character if the last adventure level's boss character index
|
||||
is the current index. If it is, we add stage 15 complete as a requirement for transformation.
|
||||
*/
|
||||
} else {
|
||||
0
|
||||
/*
|
||||
Another magic number...
|
||||
|
||||
The rest of the characters are not locked.
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
database
|
||||
.characterDao()
|
||||
.insertPossibleTransformation(
|
||||
cardId = cardId,
|
||||
fromChraraIndex = evo.fromCharacterIndex,
|
||||
toChraraIndex = evo.toCharacterIndex,
|
||||
requiredVitals = evo.requiredVitalValues,
|
||||
requiredTrophies = evo.requiredTrophies,
|
||||
requiredBattles = evo.requiredBattles,
|
||||
requiredWinRate = evo.requiredWinRatio,
|
||||
requiredAdventureLevelCompleted = unlockAdventureLevel,
|
||||
changeTimerHours = transformationTimerHours
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun importCharacterData(
|
||||
cardId: Long,
|
||||
card: com.github.cfogrady.vb.dim.card.Card<*, *, *, *, *, *>
|
||||
) {
|
||||
var spriteCounter = when (card is BemCard) {
|
||||
true -> 54
|
||||
false -> 10
|
||||
}
|
||||
|
||||
val domainCharacters = mutableListOf<CharacterData>()
|
||||
|
||||
val characters = card
|
||||
.characterStats
|
||||
.characterEntries
|
||||
|
||||
for (index in 0 until characters.size) {
|
||||
var domainSprite: Sprite?
|
||||
if (index < 2 && card is DimCard) {
|
||||
domainSprite = Sprite(
|
||||
width = card.spriteData.sprites[spriteCounter + 1].spriteDimensions.width,
|
||||
height = card.spriteData.sprites[spriteCounter + 1].spriteDimensions.height,
|
||||
spriteIdle1 = card.spriteData.sprites[spriteCounter + 1].pixelData,
|
||||
spriteIdle2 = card.spriteData.sprites[spriteCounter + 2].pixelData,
|
||||
spriteWalk1 = card.spriteData.sprites[spriteCounter + 1].pixelData,
|
||||
spriteWalk2 = card.spriteData.sprites[spriteCounter + 3].pixelData,
|
||||
spriteRun1 = card.spriteData.sprites[spriteCounter + 1].pixelData,
|
||||
spriteRun2 = card.spriteData.sprites[spriteCounter + 3].pixelData,
|
||||
spriteTrain1 = card.spriteData.sprites[spriteCounter + 1].pixelData,
|
||||
spriteTrain2 = card.spriteData.sprites[spriteCounter + 3].pixelData,
|
||||
spriteHappy = card.spriteData.sprites[spriteCounter + 4].pixelData,
|
||||
spriteSleep = card.spriteData.sprites[spriteCounter + 5].pixelData,
|
||||
spriteAttack = card.spriteData.sprites[spriteCounter + 2].pixelData,
|
||||
spriteDodge = card.spriteData.sprites[spriteCounter + 3].pixelData
|
||||
)
|
||||
} else {
|
||||
domainSprite = Sprite(
|
||||
width = card.spriteData.sprites[spriteCounter + 1].spriteDimensions.width,
|
||||
height = card.spriteData.sprites[spriteCounter + 1].spriteDimensions.height,
|
||||
spriteIdle1 = card.spriteData.sprites[spriteCounter + 1].pixelData,
|
||||
spriteIdle2 = card.spriteData.sprites[spriteCounter + 2].pixelData,
|
||||
spriteWalk1 = card.spriteData.sprites[spriteCounter + 3].pixelData,
|
||||
spriteWalk2 = card.spriteData.sprites[spriteCounter + 4].pixelData,
|
||||
spriteRun1 = card.spriteData.sprites[spriteCounter + 5].pixelData,
|
||||
spriteRun2 = card.spriteData.sprites[spriteCounter + 6].pixelData,
|
||||
spriteTrain1 = card.spriteData.sprites[spriteCounter + 7].pixelData,
|
||||
spriteTrain2 = card.spriteData.sprites[spriteCounter + 8].pixelData,
|
||||
spriteHappy = card.spriteData.sprites[spriteCounter + 9].pixelData,
|
||||
spriteSleep = card.spriteData.sprites[spriteCounter + 10].pixelData,
|
||||
spriteAttack = card.spriteData.sprites[spriteCounter + 11].pixelData,
|
||||
spriteDodge = card.spriteData.sprites[spriteCounter + 12].pixelData
|
||||
)
|
||||
}
|
||||
|
||||
val spriteId = database
|
||||
.spriteDao()
|
||||
.insertSprite(domainSprite)
|
||||
|
||||
|
||||
domainCharacters.add(
|
||||
CharacterData(
|
||||
cardId = cardId,
|
||||
spriteId = spriteId,
|
||||
charaIndex = index,
|
||||
nameSprite = card.spriteData.sprites[spriteCounter].pixelData,
|
||||
stage = characters[index].stage,
|
||||
attribute = NfcCharacter.Attribute.entries[characters[index].attribute],
|
||||
baseHp = characters[index].hp,
|
||||
baseBp = characters[index].dp,
|
||||
baseAp = characters[index].ap,
|
||||
nameWidth = card.spriteData.sprites[spriteCounter].spriteDimensions.width,
|
||||
nameHeight = card.spriteData.sprites[spriteCounter].spriteDimensions.height
|
||||
)
|
||||
)
|
||||
|
||||
spriteCounter += if (card is BemCard) {
|
||||
14
|
||||
} else {
|
||||
when (index) {
|
||||
0 -> 6
|
||||
1 -> 7
|
||||
else -> 14
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
database
|
||||
.characterDao()
|
||||
.insertCharacter(*domainCharacters.toTypedArray())
|
||||
}
|
||||
|
||||
private fun updateCardProgress(
|
||||
cardId: Long,
|
||||
) {
|
||||
val cardProgress = CardProgress(
|
||||
cardId = cardId,
|
||||
currentStage = 0,
|
||||
unlocked = false
|
||||
)
|
||||
|
||||
database
|
||||
.cardProgressDao()
|
||||
.updateDimProgress(cardProgress)
|
||||
}
|
||||
|
||||
private fun importCard(uri: Uri) {
|
||||
context.lifecycleScope.launch(Dispatchers.IO) {
|
||||
val contentResolver = context.contentResolver
|
||||
val inputStream = contentResolver.openInputStream(uri)
|
||||
|
||||
inputStream.use { fileReader ->
|
||||
val dimReader = DimReader()
|
||||
val card = dimReader.readCard(fileReader, false)
|
||||
|
||||
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)
|
||||
val cardImportController = CardImportController(database)
|
||||
cardImportController.importCard(fileReader)
|
||||
}
|
||||
|
||||
inputStream?.close()
|
||||
@ -324,100 +118,6 @@ class SettingsScreenControllerImpl(
|
||||
}
|
||||
}
|
||||
|
||||
private fun exportDatabase(destinationUri: Uri) {
|
||||
context.lifecycleScope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
val dbFile = File(context.getDatabasePath(roomDbName).absolutePath)
|
||||
if (!dbFile.exists()) {
|
||||
throw IllegalStateException("Database file does not exist!")
|
||||
}
|
||||
|
||||
application.container.db.close()
|
||||
|
||||
context.contentResolver.openOutputStream(destinationUri)?.use { outputStream ->
|
||||
dbFile.inputStream().use { inputStream ->
|
||||
copyFile(inputStream, outputStream)
|
||||
}
|
||||
} ?: throw IllegalArgumentException("Unable to open destination Uri for writing")
|
||||
|
||||
context.runOnUiThread {
|
||||
Toast.makeText(context, "Database exported successfully!", Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(context, "Closing application to avoid changes.", Toast.LENGTH_LONG).show()
|
||||
context.finishAffinity()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e("ScanScreenController", "Error exporting database $e")
|
||||
context.runOnUiThread {
|
||||
Toast.makeText(context, "Error exporting database: ${e.message}", Toast.LENGTH_LONG).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun importDatabase(sourceUri: Uri) {
|
||||
context.lifecycleScope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
if (!getFileNameFromUri(sourceUri)!!.endsWith(".vbhelper")) {
|
||||
context.runOnUiThread {
|
||||
Toast.makeText(context, "Invalid file format", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
return@launch
|
||||
}
|
||||
|
||||
application.container.db.close()
|
||||
|
||||
val dbPath = context.getDatabasePath(roomDbName)
|
||||
val shmFile = File(dbPath.parent, "$roomDbName-shm")
|
||||
val walFile = File(dbPath.parent, "$roomDbName-wal")
|
||||
|
||||
// Delete existing database files
|
||||
if (dbPath.exists()) dbPath.delete()
|
||||
if (shmFile.exists()) shmFile.delete()
|
||||
if (walFile.exists()) walFile.delete()
|
||||
|
||||
val dbFile = File(dbPath.absolutePath)
|
||||
|
||||
context.contentResolver.openInputStream(sourceUri)?.use { inputStream ->
|
||||
dbFile.outputStream().use { outputStream ->
|
||||
copyFile(inputStream, outputStream)
|
||||
}
|
||||
} ?: throw IllegalArgumentException("Unable to open source Uri for reading")
|
||||
|
||||
context.runOnUiThread {
|
||||
Toast.makeText(context, "Database imported successfully!", Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(context, "Reopen the app to finish import process!", Toast.LENGTH_LONG).show()
|
||||
context.finishAffinity()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e("ScanScreenController", "Error importing database $e")
|
||||
context.runOnUiThread {
|
||||
Toast.makeText(context, "Error importing database: ${e.message}", Toast.LENGTH_LONG).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getFileNameFromUri(uri: Uri): String? {
|
||||
var fileName: String? = null
|
||||
val cursor = context.contentResolver.query(uri, null, null, null, null)
|
||||
cursor?.use {
|
||||
if (it.moveToFirst()) {
|
||||
val nameIndex = it.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME)
|
||||
fileName = it.getString(nameIndex)
|
||||
}
|
||||
}
|
||||
return fileName
|
||||
}
|
||||
|
||||
private fun copyFile(inputStream: InputStream, outputStream: OutputStream) {
|
||||
val buffer = ByteArray(1024)
|
||||
var bytesRead: Int
|
||||
while (inputStream.read(buffer).also { bytesRead = it } != -1) {
|
||||
outputStream.write(buffer, 0, bytesRead)
|
||||
}
|
||||
outputStream.flush()
|
||||
}
|
||||
|
||||
private fun importApk(uri: Uri) {
|
||||
context.lifecycleScope.launch(Dispatchers.IO) {
|
||||
context.contentResolver.openInputStream(uri).use {
|
||||
|
||||
@ -0,0 +1,320 @@
|
||||
package com.github.nacabaro.vbhelper.screens.settingsScreen.controllers
|
||||
|
||||
import android.util.Log
|
||||
import com.github.cfogrady.vb.dim.card.BemCard
|
||||
import com.github.cfogrady.vb.dim.card.DimCard
|
||||
import com.github.cfogrady.vb.dim.card.DimReader
|
||||
import com.github.cfogrady.vbnfc.data.NfcCharacter
|
||||
import com.github.nacabaro.vbhelper.database.AppDatabase
|
||||
import com.github.nacabaro.vbhelper.domain.card.Card
|
||||
import com.github.nacabaro.vbhelper.domain.card.CardCharacter
|
||||
import com.github.nacabaro.vbhelper.domain.card.CardProgress
|
||||
import com.github.nacabaro.vbhelper.domain.characters.Sprite
|
||||
import java.io.InputStream
|
||||
|
||||
class CardImportController(
|
||||
private val database: AppDatabase
|
||||
) {
|
||||
suspend fun importCard(
|
||||
fileReader: InputStream?
|
||||
) {
|
||||
val dimReader = DimReader()
|
||||
val card = dimReader.readCard(fileReader, false)
|
||||
|
||||
val cardModel = Card(
|
||||
cardId = card.header.dimId,
|
||||
logo = card.spriteData.sprites[0].pixelData,
|
||||
name = card.spriteData.text,
|
||||
stageCount = card.adventureLevels.levels.size,
|
||||
logoHeight = card.spriteData.sprites[0].height,
|
||||
logoWidth = card.spriteData.sprites[0].width,
|
||||
isBEm = card is BemCard
|
||||
)
|
||||
|
||||
val cardId = database
|
||||
.cardDao()
|
||||
.insertNewCard(cardModel)
|
||||
|
||||
updateCardProgress(cardId = cardId)
|
||||
|
||||
importCharacterData(cardId, card)
|
||||
|
||||
importEvoData(cardId, card)
|
||||
|
||||
importAdventureMissions(cardId, card)
|
||||
|
||||
importCardFusions(cardId, card)
|
||||
}
|
||||
|
||||
private fun updateCardProgress(
|
||||
cardId: Long,
|
||||
) {
|
||||
database
|
||||
.cardProgressDao()
|
||||
.insertCardProgress(
|
||||
CardProgress(
|
||||
cardId = cardId,
|
||||
currentStage = 1,
|
||||
unlocked = false
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun importCharacterData(
|
||||
cardId: Long,
|
||||
card: com.github.cfogrady.vb.dim.card.Card<*, *, *, *, *, *>
|
||||
) {
|
||||
var spriteCounter = when (card is BemCard) {
|
||||
true -> 54
|
||||
false -> 10
|
||||
}
|
||||
|
||||
val domainCharacters = mutableListOf<CardCharacter>()
|
||||
|
||||
val characters = card
|
||||
.characterStats
|
||||
.characterEntries
|
||||
|
||||
for (index in 0 until characters.size) {
|
||||
var domainSprite: Sprite?
|
||||
if (index < 2 && card is DimCard) {
|
||||
domainSprite = Sprite(
|
||||
width = card.spriteData.sprites[spriteCounter + 1].spriteDimensions.width,
|
||||
height = card.spriteData.sprites[spriteCounter + 1].spriteDimensions.height,
|
||||
spriteIdle1 = card.spriteData.sprites[spriteCounter + 1].pixelData,
|
||||
spriteIdle2 = card.spriteData.sprites[spriteCounter + 2].pixelData,
|
||||
spriteWalk1 = card.spriteData.sprites[spriteCounter + 1].pixelData,
|
||||
spriteWalk2 = card.spriteData.sprites[spriteCounter + 3].pixelData,
|
||||
spriteRun1 = card.spriteData.sprites[spriteCounter + 1].pixelData,
|
||||
spriteRun2 = card.spriteData.sprites[spriteCounter + 3].pixelData,
|
||||
spriteTrain1 = card.spriteData.sprites[spriteCounter + 1].pixelData,
|
||||
spriteTrain2 = card.spriteData.sprites[spriteCounter + 3].pixelData,
|
||||
spriteHappy = card.spriteData.sprites[spriteCounter + 4].pixelData,
|
||||
spriteSleep = card.spriteData.sprites[spriteCounter + 5].pixelData,
|
||||
spriteAttack = card.spriteData.sprites[spriteCounter + 2].pixelData,
|
||||
spriteDodge = card.spriteData.sprites[spriteCounter + 3].pixelData
|
||||
)
|
||||
} else {
|
||||
domainSprite = Sprite(
|
||||
width = card.spriteData.sprites[spriteCounter + 1].spriteDimensions.width,
|
||||
height = card.spriteData.sprites[spriteCounter + 1].spriteDimensions.height,
|
||||
spriteIdle1 = card.spriteData.sprites[spriteCounter + 1].pixelData,
|
||||
spriteIdle2 = card.spriteData.sprites[spriteCounter + 2].pixelData,
|
||||
spriteWalk1 = card.spriteData.sprites[spriteCounter + 3].pixelData,
|
||||
spriteWalk2 = card.spriteData.sprites[spriteCounter + 4].pixelData,
|
||||
spriteRun1 = card.spriteData.sprites[spriteCounter + 5].pixelData,
|
||||
spriteRun2 = card.spriteData.sprites[spriteCounter + 6].pixelData,
|
||||
spriteTrain1 = card.spriteData.sprites[spriteCounter + 7].pixelData,
|
||||
spriteTrain2 = card.spriteData.sprites[spriteCounter + 8].pixelData,
|
||||
spriteHappy = card.spriteData.sprites[spriteCounter + 9].pixelData,
|
||||
spriteSleep = card.spriteData.sprites[spriteCounter + 10].pixelData,
|
||||
spriteAttack = card.spriteData.sprites[spriteCounter + 11].pixelData,
|
||||
spriteDodge = card.spriteData.sprites[spriteCounter + 12].pixelData
|
||||
)
|
||||
}
|
||||
|
||||
val spriteId = database
|
||||
.spriteDao()
|
||||
.insertSprite(domainSprite)
|
||||
|
||||
|
||||
domainCharacters.add(
|
||||
CardCharacter(
|
||||
cardId = cardId,
|
||||
spriteId = spriteId,
|
||||
charaIndex = index,
|
||||
nameSprite = card.spriteData.sprites[spriteCounter].pixelData,
|
||||
stage = characters[index].stage,
|
||||
attribute = NfcCharacter.Attribute.entries[characters[index].attribute],
|
||||
baseHp = characters[index].hp,
|
||||
baseBp = characters[index].dp,
|
||||
baseAp = characters[index].ap,
|
||||
nameWidth = card.spriteData.sprites[spriteCounter].spriteDimensions.width,
|
||||
nameHeight = card.spriteData.sprites[spriteCounter].spriteDimensions.height
|
||||
)
|
||||
)
|
||||
|
||||
spriteCounter += if (card is BemCard) {
|
||||
14
|
||||
} else {
|
||||
when (index) {
|
||||
0 -> 6
|
||||
1 -> 7
|
||||
else -> 14
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
database
|
||||
.characterDao()
|
||||
.insertCharacter(*domainCharacters.toTypedArray())
|
||||
}
|
||||
|
||||
private suspend fun importAdventureMissions(
|
||||
cardId: Long,
|
||||
card: com.github.cfogrady.vb.dim.card.Card<*, *, *, *, *, *>
|
||||
) {
|
||||
Log.d("importAdventureMissions", "Importing adventure missions")
|
||||
if (card is BemCard) {
|
||||
card.adventureLevels.levels.forEach {
|
||||
database
|
||||
.cardAdventureDao()
|
||||
.insertNewAdventure(
|
||||
cardId = cardId,
|
||||
characterId = it.bossCharacterIndex,
|
||||
steps = it.steps,
|
||||
bossAp = it.bossAp,
|
||||
bossHp = it.bossHp,
|
||||
bossDp = it.bossDp,
|
||||
bossBp = it.bossBp
|
||||
)
|
||||
}
|
||||
} else if (card is DimCard) {
|
||||
card.adventureLevels.levels.map {
|
||||
database
|
||||
.cardAdventureDao()
|
||||
.insertNewAdventure(
|
||||
cardId = cardId,
|
||||
characterId = it.bossCharacterIndex,
|
||||
steps = it.steps,
|
||||
bossAp = it.bossAp,
|
||||
bossHp = it.bossHp,
|
||||
bossDp = it.bossDp,
|
||||
bossBp = null
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun importCardFusions(
|
||||
cardId: Long,
|
||||
card: com.github.cfogrady.vb.dim.card.Card<*, *, *, *, *, *>
|
||||
) {
|
||||
Log.d("importCardFusions", "Importing card fusions")
|
||||
if (card is DimCard) {
|
||||
card
|
||||
.attributeFusions
|
||||
.entries
|
||||
.forEach {
|
||||
Log.d("importCardFusions", "Importing fusion: ${it.attribute1Fusion}")
|
||||
if (it.attribute1Fusion != 65535 && it.characterIndex != 65535) {
|
||||
database
|
||||
.cardFusionsDao()
|
||||
.insertNewFusion(
|
||||
cardId = cardId,
|
||||
fromCharaId = it.characterIndex,
|
||||
attribute = NfcCharacter.Attribute.Virus,
|
||||
toCharaId = it.attribute1Fusion,
|
||||
)
|
||||
}
|
||||
|
||||
if (it.attribute2Fusion != 65535 && it.characterIndex != 65535) {
|
||||
database
|
||||
.cardFusionsDao()
|
||||
.insertNewFusion(
|
||||
cardId = cardId,
|
||||
fromCharaId = it.characterIndex,
|
||||
attribute = NfcCharacter.Attribute.Data,
|
||||
toCharaId = it.attribute2Fusion,
|
||||
)
|
||||
}
|
||||
|
||||
if (it.attribute3Fusion != 65535 && it.characterIndex != 65535) {
|
||||
database
|
||||
.cardFusionsDao()
|
||||
.insertNewFusion(
|
||||
cardId = cardId,
|
||||
fromCharaId = it.characterIndex,
|
||||
attribute = NfcCharacter.Attribute.Vaccine,
|
||||
toCharaId = it.attribute3Fusion,
|
||||
)
|
||||
}
|
||||
|
||||
if (it.attribute4Fusion != 65535 && it.characterIndex != 65535) {
|
||||
database
|
||||
.cardFusionsDao()
|
||||
.insertNewFusion(
|
||||
cardId = cardId,
|
||||
fromCharaId = it.characterIndex,
|
||||
attribute = NfcCharacter.Attribute.Free,
|
||||
toCharaId = it.attribute4Fusion,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun importEvoData(
|
||||
cardId: Long,
|
||||
card: com.github.cfogrady.vb.dim.card.Card<*, *, *, *, *, *>
|
||||
) {
|
||||
for (index in 0 until card.transformationRequirements.transformationEntries.size) {
|
||||
val evo = card.transformationRequirements.transformationEntries[index]
|
||||
|
||||
var transformationTimerHours: Int
|
||||
var unlockAdventureLevel: Int
|
||||
|
||||
if (card is BemCard) {
|
||||
transformationTimerHours = card
|
||||
.transformationRequirements
|
||||
.transformationEntries[index]
|
||||
.minutesUntilTransformation / 60
|
||||
unlockAdventureLevel = if (
|
||||
card
|
||||
.transformationRequirements
|
||||
.transformationEntries[index]
|
||||
.requiredCompletedAdventureLevel == 65535
|
||||
) {
|
||||
0
|
||||
} else {
|
||||
card
|
||||
.transformationRequirements
|
||||
.transformationEntries[index]
|
||||
.requiredCompletedAdventureLevel
|
||||
}
|
||||
} else {
|
||||
transformationTimerHours = (card as DimCard)
|
||||
.transformationRequirements
|
||||
.transformationEntries[index]
|
||||
.hoursUntilEvolution
|
||||
unlockAdventureLevel = if (
|
||||
card
|
||||
.adventureLevels
|
||||
.levels
|
||||
.last()
|
||||
.bossCharacterIndex == card.transformationRequirements.transformationEntries[index].toCharacterIndex
|
||||
) {
|
||||
14
|
||||
/*
|
||||
Magic number incoming!!
|
||||
|
||||
In the case of DiMCards, stage 15 is the one that unlocks the locked character.
|
||||
We know it is a locked character if the last adventure level's boss character index
|
||||
is the current index. If it is, we add stage 15 complete as a requirement for transformation.
|
||||
*/
|
||||
} else {
|
||||
0
|
||||
/*
|
||||
Another magic number...
|
||||
|
||||
The rest of the characters are not locked.
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
database
|
||||
.characterDao()
|
||||
.insertPossibleTransformation(
|
||||
cardId = cardId,
|
||||
fromChraraIndex = evo.fromCharacterIndex,
|
||||
toChraraIndex = evo.toCharacterIndex,
|
||||
requiredVitals = evo.requiredVitalValues,
|
||||
requiredTrophies = evo.requiredTrophies,
|
||||
requiredBattles = evo.requiredBattles,
|
||||
requiredWinRate = evo.requiredWinRatio,
|
||||
requiredAdventureLevelCompleted = unlockAdventureLevel,
|
||||
changeTimerHours = transformationTimerHours
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,115 @@
|
||||
package com.github.nacabaro.vbhelper.screens.settingsScreen.controllers
|
||||
|
||||
import android.net.Uri
|
||||
import android.provider.OpenableColumns
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.github.nacabaro.vbhelper.di.VBHelper
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import java.io.File
|
||||
import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
|
||||
class DatabaseManagementController(
|
||||
val componentActivity: ComponentActivity,
|
||||
val application: VBHelper
|
||||
) {
|
||||
private val roomDbName = "internalDb"
|
||||
|
||||
fun exportDatabase( destinationUri: Uri) {
|
||||
componentActivity.lifecycleScope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
val dbFile = File(componentActivity.getDatabasePath(roomDbName).absolutePath)
|
||||
if (!dbFile.exists()) {
|
||||
throw IllegalStateException("Database file does not exist!")
|
||||
}
|
||||
|
||||
application.container.db.close()
|
||||
|
||||
componentActivity.contentResolver.openOutputStream(destinationUri)?.use { outputStream ->
|
||||
dbFile.inputStream().use { inputStream ->
|
||||
copyFile(inputStream, outputStream)
|
||||
}
|
||||
} ?: throw IllegalArgumentException("Unable to open destination Uri for writing")
|
||||
|
||||
componentActivity.runOnUiThread {
|
||||
Toast.makeText(componentActivity, "Database exported successfully!", Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(componentActivity, "Closing application to avoid changes.", Toast.LENGTH_LONG).show()
|
||||
componentActivity.finishAffinity()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e("ScanScreenController", "Error exporting database $e")
|
||||
componentActivity.runOnUiThread {
|
||||
Toast.makeText(componentActivity, "Error exporting database: ${e.message}", Toast.LENGTH_LONG).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun importDatabase(sourceUri: Uri) {
|
||||
componentActivity.lifecycleScope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
if (!getFileNameFromUri(sourceUri)!!.endsWith(".vbhelper")) {
|
||||
componentActivity.runOnUiThread {
|
||||
Toast.makeText(componentActivity, "Invalid file format", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
return@launch
|
||||
}
|
||||
|
||||
application.container.db.close()
|
||||
|
||||
val dbPath = componentActivity.getDatabasePath(roomDbName)
|
||||
val shmFile = File(dbPath.parent, "$roomDbName-shm")
|
||||
val walFile = File(dbPath.parent, "$roomDbName-wal")
|
||||
|
||||
// Delete existing database files
|
||||
if (dbPath.exists()) dbPath.delete()
|
||||
if (shmFile.exists()) shmFile.delete()
|
||||
if (walFile.exists()) walFile.delete()
|
||||
|
||||
val dbFile = File(dbPath.absolutePath)
|
||||
|
||||
componentActivity.contentResolver.openInputStream(sourceUri)?.use { inputStream ->
|
||||
dbFile.outputStream().use { outputStream ->
|
||||
copyFile(inputStream, outputStream)
|
||||
}
|
||||
} ?: throw IllegalArgumentException("Unable to open source Uri for reading")
|
||||
|
||||
componentActivity.runOnUiThread {
|
||||
Toast.makeText(componentActivity, "Database imported successfully!", Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(componentActivity, "Reopen the app to finish import process!", Toast.LENGTH_LONG).show()
|
||||
componentActivity.finishAffinity()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e("ScanScreenController", "Error importing database $e")
|
||||
componentActivity.runOnUiThread {
|
||||
Toast.makeText(componentActivity, "Error importing database: ${e.message}", Toast.LENGTH_LONG).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun copyFile(inputStream: InputStream, outputStream: OutputStream) {
|
||||
val buffer = ByteArray(1024)
|
||||
var bytesRead: Int
|
||||
while (inputStream.read(buffer).also { bytesRead = it } != -1) {
|
||||
outputStream.write(buffer, 0, bytesRead)
|
||||
}
|
||||
outputStream.flush()
|
||||
}
|
||||
|
||||
private fun getFileNameFromUri(uri: Uri): String? {
|
||||
var fileName: String? = null
|
||||
val cursor = componentActivity.contentResolver.query(uri, null, null, null, null)
|
||||
cursor?.use {
|
||||
if (it.moveToFirst()) {
|
||||
val nameIndex = it.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME)
|
||||
fileName = it.getString(nameIndex)
|
||||
}
|
||||
}
|
||||
return fileName
|
||||
}
|
||||
}
|
||||
@ -38,6 +38,7 @@ import kotlinx.coroutines.launch
|
||||
fun StorageDialog(
|
||||
characterId: Long,
|
||||
onDismissRequest: () -> Unit,
|
||||
onClickDelete: () -> Unit,
|
||||
onSendToBracelet: () -> Unit,
|
||||
onClickSetActive: () -> Unit,
|
||||
onClickSendToAdventure: (time: Long) -> Unit
|
||||
@ -141,6 +142,13 @@ fun StorageDialog(
|
||||
) {
|
||||
Text(text = "Send on adventure")
|
||||
}
|
||||
Button(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(),
|
||||
onClick = onClickDelete
|
||||
) {
|
||||
Text(text = "Delete character")
|
||||
}
|
||||
Button(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(),
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
package com.github.nacabaro.vbhelper.screens.storageScreen
|
||||
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import androidx.compose.foundation.gestures.Orientation
|
||||
import androidx.compose.foundation.gestures.scrollable
|
||||
@ -11,14 +12,15 @@ import androidx.compose.foundation.lazy.grid.GridCells
|
||||
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
|
||||
import androidx.compose.foundation.lazy.grid.items
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
@ -28,12 +30,10 @@ import androidx.navigation.NavController
|
||||
import com.github.nacabaro.vbhelper.components.CharacterEntry
|
||||
import com.github.nacabaro.vbhelper.components.TopBanner
|
||||
import com.github.nacabaro.vbhelper.di.VBHelper
|
||||
import com.github.nacabaro.vbhelper.dtos.CharacterDtos
|
||||
import com.github.nacabaro.vbhelper.navigation.NavigationItems
|
||||
import com.github.nacabaro.vbhelper.screens.adventureScreen.AdventureScreenControllerImpl
|
||||
import com.github.nacabaro.vbhelper.source.StorageRepository
|
||||
import com.github.nacabaro.vbhelper.utils.BitmapData
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
|
||||
@Composable
|
||||
@ -42,18 +42,11 @@ fun StorageScreen(
|
||||
storageScreenController: StorageScreenControllerImpl,
|
||||
adventureScreenController: AdventureScreenControllerImpl
|
||||
) {
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
val application = LocalContext.current.applicationContext as VBHelper
|
||||
val storageRepository = StorageRepository(application.container.db)
|
||||
val monList = remember { mutableStateOf<List<CharacterDtos.CharacterWithSprites>>(emptyList()) }
|
||||
var selectedCharacter by remember { mutableStateOf<Long?>(null) }
|
||||
val characterList by storageRepository.getAllCharacters().collectAsState(initial = emptyList())
|
||||
|
||||
LaunchedEffect(storageRepository, selectedCharacter) {
|
||||
coroutineScope.launch {
|
||||
val characterList = storageRepository.getAllCharacters()
|
||||
monList.value = characterList
|
||||
}
|
||||
}
|
||||
var selectedCharacter by remember { mutableStateOf<Long?>(null) }
|
||||
|
||||
Scaffold (
|
||||
topBar = {
|
||||
@ -65,7 +58,7 @@ fun StorageScreen(
|
||||
)
|
||||
}
|
||||
) { contentPadding ->
|
||||
if (monList.value.isEmpty()) {
|
||||
if (characterList.isEmpty()) {
|
||||
Column (
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Center,
|
||||
@ -86,7 +79,7 @@ fun StorageScreen(
|
||||
.scrollable(state = rememberScrollState(), orientation = Orientation.Vertical)
|
||||
.padding(top = contentPadding.calculateTopPadding())
|
||||
) {
|
||||
items(monList.value) { index ->
|
||||
items(characterList) { index ->
|
||||
CharacterEntry(
|
||||
icon = BitmapData(
|
||||
bitmap = index.spriteIdle,
|
||||
@ -107,6 +100,16 @@ fun StorageScreen(
|
||||
)
|
||||
}
|
||||
},
|
||||
cardColors = if (index.active) {
|
||||
CardDefaults.cardColors(
|
||||
containerColor = MaterialTheme.colorScheme.primary
|
||||
)
|
||||
} else {
|
||||
CardDefaults.cardColors(
|
||||
containerColor = MaterialTheme.colorScheme.surfaceContainerHighest
|
||||
)
|
||||
}
|
||||
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -138,6 +141,16 @@ fun StorageScreen(
|
||||
timeInMinutes = time
|
||||
)
|
||||
selectedCharacter = null
|
||||
},
|
||||
onClickDelete = {
|
||||
storageScreenController
|
||||
.deleteCharacter(
|
||||
characterId = selectedCharacter!!,
|
||||
onCompletion = {
|
||||
Log.d("StorageScreen", "Character deleted")
|
||||
}
|
||||
)
|
||||
selectedCharacter = null
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@ -2,4 +2,5 @@ package com.github.nacabaro.vbhelper.screens.storageScreen
|
||||
|
||||
interface StorageScreenController {
|
||||
fun setActive(characterId: Long, onCompletion: () -> Unit)
|
||||
fun deleteCharacter(characterId: Long, onCompletion: () -> Unit)
|
||||
}
|
||||
@ -28,4 +28,21 @@ class StorageScreenControllerImpl(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun deleteCharacter(characterId: Long, onCompletion: () -> Unit) {
|
||||
componentActivity.lifecycleScope.launch(Dispatchers.IO) {
|
||||
database
|
||||
.userCharacterDao()
|
||||
.deleteCharacterById(characterId)
|
||||
|
||||
componentActivity.runOnUiThread {
|
||||
Toast.makeText(
|
||||
componentActivity,
|
||||
"Character deleted!",
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
onCompletion()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,28 @@
|
||||
package com.github.nacabaro.vbhelper.source
|
||||
|
||||
import androidx.datastore.core.DataStore
|
||||
import androidx.datastore.preferences.core.Preferences
|
||||
import androidx.datastore.preferences.core.edit
|
||||
import androidx.datastore.preferences.core.intPreferencesKey
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
||||
class CurrencyRepository (
|
||||
private val dataStore: DataStore<Preferences>
|
||||
) {
|
||||
|
||||
private companion object {
|
||||
val CURRENCY_VALUE = intPreferencesKey("currency_value")
|
||||
}
|
||||
|
||||
val currencyValue: Flow<Int> = dataStore.data
|
||||
.map { preferences ->
|
||||
preferences[CURRENCY_VALUE] ?: 10000
|
||||
}
|
||||
|
||||
suspend fun setCurrencyValue(newValue: Int) {
|
||||
dataStore.edit { preferences ->
|
||||
preferences[CURRENCY_VALUE] = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3,19 +3,24 @@ package com.github.nacabaro.vbhelper.source
|
||||
import com.github.nacabaro.vbhelper.database.AppDatabase
|
||||
import com.github.nacabaro.vbhelper.dtos.CardDtos
|
||||
import com.github.nacabaro.vbhelper.dtos.CharacterDtos
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
class DexRepository (
|
||||
private val db: AppDatabase
|
||||
) {
|
||||
suspend fun getAllDims(): List<CardDtos.CardProgress> {
|
||||
fun getAllDims(): Flow<List<CardDtos.CardProgress>> {
|
||||
return db.dexDao().getCardsWithProgress()
|
||||
}
|
||||
|
||||
suspend fun getCharactersByCardId(cardId: Long): List<CharacterDtos.CardCharaProgress> {
|
||||
fun getCharactersByCardId(cardId: Long): Flow<List<CharacterDtos.CardCharaProgress>> {
|
||||
return db.dexDao().getSingleCardProgress(cardId)
|
||||
}
|
||||
|
||||
suspend fun getCardPossibleTransformations(cardId: Long): List<CharacterDtos.EvolutionRequirementsWithSpritesAndObtained> {
|
||||
return db.characterDao().getEvolutionRequirementsForCard(cardId)
|
||||
fun getCharacterPossibleTransformations(characterId: Long): Flow<List<CharacterDtos.EvolutionRequirementsWithSpritesAndObtained>> {
|
||||
return db.characterDao().getEvolutionRequirementsForCard(characterId)
|
||||
}
|
||||
|
||||
fun getCharacterPossibleFusions(characterId: Long): Flow<List<CharacterDtos.FusionsWithSpritesAndObtained>> {
|
||||
return db.cardFusionsDao().getFusionsForCharacter(characterId)
|
||||
}
|
||||
}
|
||||
@ -1,17 +1,17 @@
|
||||
package com.github.nacabaro.vbhelper.source
|
||||
|
||||
import com.github.nacabaro.vbhelper.database.AppDatabase
|
||||
import com.github.nacabaro.vbhelper.domain.items.Items
|
||||
import com.github.nacabaro.vbhelper.dtos.ItemDtos
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
class ItemsRepository(
|
||||
private val db: AppDatabase
|
||||
) {
|
||||
suspend fun getAllItems(): List<ItemDtos.ItemsWithQuantities> {
|
||||
fun getAllItems(): Flow<List<ItemDtos.ItemsWithQuantities>> {
|
||||
return db.itemDao().getAllItems()
|
||||
}
|
||||
|
||||
suspend fun getUserItems(): List<ItemDtos.ItemsWithQuantities> {
|
||||
fun getUserItems(): Flow<List<ItemDtos.ItemsWithQuantities>> {
|
||||
return db.itemDao().getAllUserItems()
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
package com.github.nacabaro.vbhelper.source
|
||||
|
||||
import com.github.nacabaro.vbhelper.database.AppDatabase
|
||||
import com.github.nacabaro.vbhelper.domain.card.Card
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
class ScanRepository(
|
||||
val database: AppDatabase
|
||||
) {
|
||||
fun getCardDetails(characterId: Long): Flow<Card> {
|
||||
return database.cardDao().getCardByCharacterId(characterId)
|
||||
}
|
||||
}
|
||||
@ -6,11 +6,12 @@ import com.github.nacabaro.vbhelper.domain.device_data.SpecialMissions
|
||||
import com.github.nacabaro.vbhelper.domain.device_data.VBCharacterData
|
||||
import com.github.nacabaro.vbhelper.dtos.CharacterDtos
|
||||
import com.github.nacabaro.vbhelper.dtos.ItemDtos
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
class StorageRepository (
|
||||
private val db: AppDatabase
|
||||
) {
|
||||
suspend fun getAllCharacters(): List<CharacterDtos.CharacterWithSprites> {
|
||||
fun getAllCharacters(): Flow<List<CharacterDtos.CharacterWithSprites>> {
|
||||
return db.userCharacterDao().getAllCharacters()
|
||||
}
|
||||
|
||||
@ -46,7 +47,7 @@ class StorageRepository (
|
||||
return db.userCharacterDao().deleteCharacterById(id)
|
||||
}
|
||||
|
||||
suspend fun getAdventureCharacters(): List<CharacterDtos.AdventureCharacterWithSprites> {
|
||||
fun getAdventureCharacters(): Flow<List<CharacterDtos.AdventureCharacterWithSprites>> {
|
||||
return db.adventureDao().getAdventureCharacters()
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
[versions]
|
||||
agp = "8.12.2"
|
||||
agp = "8.13.1"
|
||||
datastore = "1.1.2"
|
||||
kotlin = "2.0.0"
|
||||
coreKtx = "1.15.0"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user