M app/src/main/java/com/github/nacabaro/vbhelper/daos/CardAdventureDao.kt => app/src/main/java/com/github/nacabaro/vbhelper/daos/CardAdventureDao.kt +3 -2
@@ 3,6 3,7 @@ package com.github.nacabaro.vbhelper.daos
import androidx.room.Dao
import androidx.room.Query
import com.github.nacabaro.vbhelper.dtos.CardDtos
+import kotlinx.coroutines.flow.Flow
@Dao
interface CardAdventureDao {
@@ 52,7 53,7 @@ interface CardAdventureDao {
WHERE
cc.cardId = :cardId
""")
- suspend fun getAdventureForCard(
+ fun getAdventureForCard(
cardId: Long
- ): List<CardDtos.CardAdventureWithSprites>
+ ): Flow<List<CardDtos.CardAdventureWithSprites>>
}=
\ No newline at end of file
M app/src/main/java/com/github/nacabaro/vbhelper/daos/CardFusionsDao.kt => app/src/main/java/com/github/nacabaro/vbhelper/daos/CardFusionsDao.kt +28 -12
@@ 2,6 2,9 @@ package com.github.nacabaro.vbhelper.daos
import androidx.room.Dao
import androidx.room.Query
+import com.github.cfogrady.vbnfc.data.NfcCharacter
+import com.github.nacabaro.vbhelper.dtos.CharacterDtos
+import kotlinx.coroutines.flow.Flow
@Dao
interface CardFusionsDao {
@@ 9,24 12,37 @@ interface CardFusionsDao {
INSERT INTO
CardFusions (
fromCharaId,
- attribute1Fusion,
- attribute2Fusion,
- attribute3Fusion,
- attribute4Fusion
+ attribute,
+ toCharaId
)
SELECT
(SELECT id FROM CardCharacter WHERE cardId = :cardId AND charaIndex = :fromCharaId),
- (SELECT id FROM CardCharacter WHERE cardId = :cardId AND charaIndex = :toCharaIdAttr1),
- (SELECT id FROM CardCharacter WHERE cardId = :cardId AND charaIndex = :toCharaIdAttr2),
- (SELECT id FROM CardCharacter WHERE cardId = :cardId AND charaIndex = :toCharaIdAttr3),
- (SELECT id FROM CardCharacter WHERE cardId = :cardId AND charaIndex = :toCharaIdAttr4)
+ :attribute,
+ (SELECT id FROM CardCharacter WHERE cardId = :cardId AND charaIndex = :toCharaId)
""")
suspend fun insertNewFusion(
cardId: Long,
fromCharaId: Int,
- toCharaIdAttr1: Int,
- toCharaIdAttr2: Int,
- toCharaIdAttr3: Int,
- toCharaIdAttr4: Int
+ attribute: NfcCharacter.Attribute,
+ toCharaId: Int
)
+
+ @Query("""
+ SELECT
+ cf.toCharaId as charaId,
+ cf.fromCharaId as fromCharaId,
+ s.spriteIdle1 as spriteIdle,
+ cc.attribute as attribute,
+ s.width as spriteWidth,
+ s.height as spriteHeight,
+ d.discoveredOn as discoveredOn,
+ cf.attribute as fusionAttribute
+ FROM CardFusions cf
+ JOIN CardCharacter cc ON cc.id = cf.toCharaId
+ JOIN Sprite s ON s.id = cc.id
+ LEFT JOIN Dex d ON d.id = cc.id
+ WHERE cf.fromCharaId = :charaId
+ ORDER BY cc.charaIndex
+ """)
+ fun getFusionsForCharacter(charaId: Long): Flow<List<CharacterDtos.FusionsWithSpritesAndObtained>>
}=
\ No newline at end of file
M app/src/main/java/com/github/nacabaro/vbhelper/daos/CardProgressDao.kt => app/src/main/java/com/github/nacabaro/vbhelper/daos/CardProgressDao.kt +2 -1
@@ 5,6 5,7 @@ import androidx.room.Insert
import androidx.room.Query
import com.github.nacabaro.vbhelper.domain.card.CardProgress
import com.github.nacabaro.vbhelper.dtos.CharacterDtos
+import kotlinx.coroutines.flow.Flow
@Dao
interface CardProgressDao {
@@ 21,7 22,7 @@ interface CardProgressDao {
@Query(
"SELECT currentStage FROM CardProgress WHERE cardId = :cardId"
)
- fun getCardProgress(cardId: Long): Int
+ fun getCardProgress(cardId: Long): Flow<Int>
@Insert
fun insertCardProgress(cardProgress: CardProgress)
M app/src/main/java/com/github/nacabaro/vbhelper/daos/CharacterDao.kt => app/src/main/java/com/github/nacabaro/vbhelper/daos/CharacterDao.kt +3 -2
@@ 6,6 6,7 @@ import androidx.room.Query
import com.github.nacabaro.vbhelper.domain.card.CardCharacter
import com.github.nacabaro.vbhelper.domain.characters.Sprite
import com.github.nacabaro.vbhelper.dtos.CharacterDtos
+import kotlinx.coroutines.flow.Flow
@Dao
interface CharacterDao {
@@ 82,8 83,8 @@ interface CharacterDao {
JOIN Sprite s ON s.id = c.spriteId
LEFT JOIN Dex d ON d.id = pt.toCharaId
WHERE
- c.cardId = :cardId
+ pt.charaId = :characterId
"""
)
- suspend fun getEvolutionRequirementsForCard(cardId: Long): List<CharacterDtos.EvolutionRequirementsWithSpritesAndObtained>
+ fun getEvolutionRequirementsForCard(characterId: Long): Flow<List<CharacterDtos.EvolutionRequirementsWithSpritesAndObtained>>
}=
\ No newline at end of file
M app/src/main/java/com/github/nacabaro/vbhelper/daos/DexDao.kt => app/src/main/java/com/github/nacabaro/vbhelper/daos/DexDao.kt +3 -2
@@ 4,6 4,7 @@ import androidx.room.Dao
import androidx.room.Query
import com.github.nacabaro.vbhelper.dtos.CardDtos
import com.github.nacabaro.vbhelper.dtos.CharacterDtos
+import kotlinx.coroutines.flow.Flow
@Dao
interface DexDao {
@@ 40,7 41,7 @@ interface DexDao {
WHERE c.cardId = :cardId
"""
)
- suspend fun getSingleCardProgress(cardId: Long): List<CharacterDtos.CardCharaProgress>
+ fun getSingleCardProgress(cardId: Long): Flow<List<CharacterDtos.CardCharaProgress>>
@Query(
"""
@@ 55,5 56,5 @@ interface DexDao {
FROM Card c
"""
)
- suspend fun getCardsWithProgress(): List<CardDtos.CardProgress>
+ fun getCardsWithProgress(): Flow<List<CardDtos.CardProgress>>
}=
\ No newline at end of file
M app/src/main/java/com/github/nacabaro/vbhelper/domain/card/CardFusions.kt => app/src/main/java/com/github/nacabaro/vbhelper/domain/card/CardFusions.kt +4 -23
@@ 3,6 3,7 @@ package com.github.nacabaro.vbhelper.domain.card
import androidx.room.Entity
import androidx.room.ForeignKey
import androidx.room.PrimaryKey
+import com.github.cfogrady.vbnfc.data.NfcCharacter
@Entity(
foreignKeys = [
@@ 15,25 16,7 @@ import androidx.room.PrimaryKey
ForeignKey(
entity = CardCharacter::class,
parentColumns = ["id"],
- childColumns = ["attribute1Fusion"],
- onDelete = ForeignKey.CASCADE
- ),
- ForeignKey(
- entity = CardCharacter::class,
- parentColumns = ["id"],
- childColumns = ["attribute2Fusion"],
- onDelete = ForeignKey.CASCADE
- ),
- ForeignKey(
- entity = CardCharacter::class,
- parentColumns = ["id"],
- childColumns = ["attribute3Fusion"],
- onDelete = ForeignKey.CASCADE
- ),
- ForeignKey(
- entity = CardCharacter::class,
- parentColumns = ["id"],
- childColumns = ["attribute4Fusion"],
+ childColumns = ["toCharaId"],
onDelete = ForeignKey.CASCADE
)
]
@@ 41,8 24,6 @@ import androidx.room.PrimaryKey
data class CardFusions(
@PrimaryKey(autoGenerate = true) val id: Long,
val fromCharaId: Long,
- val attribute1Fusion: Long?,
- val attribute2Fusion: Long?,
- val attribute3Fusion: Long?,
- val attribute4Fusion: Long?
+ val attribute: NfcCharacter.Attribute,
+ val toCharaId: Long
)=
\ No newline at end of file
M app/src/main/java/com/github/nacabaro/vbhelper/dtos/CharacterDtos.kt => app/src/main/java/com/github/nacabaro/vbhelper/dtos/CharacterDtos.kt +10 -0
@@ 114,4 114,14 @@ object CharacterDtos {
val changeTimerHours: Int,
val requiredAdventureLevelCompleted: Int
)
+
+ data class FusionsWithSpritesAndObtained(
+ val charaId: Long,
+ val fromCharaId: Long,
+ val spriteIdle: ByteArray,
+ val spriteWidth: Int,
+ val spriteHeight: Int,
+ val discoveredOn: Long?,
+ val fusionAttribute: NfcCharacter.Attribute
+ )
}=
\ No newline at end of file
M app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardAdventureScreen.kt => app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardAdventureScreen.kt +8 -23
@@ 6,18 6,11 @@ import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableIntStateOf
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.navigation.NavController
import com.github.nacabaro.vbhelper.components.TopBanner
-import com.github.nacabaro.vbhelper.dtos.CardDtos
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.withContext
@Composable
fun CardAdventureScreen(
@@ 25,20 18,12 @@ fun CardAdventureScreen(
cardScreenController: CardScreenControllerImpl,
cardId: Long
) {
- val cardAdventureMissions = remember { mutableStateOf(emptyList<CardDtos.CardAdventureWithSprites>()) }
- var currentCardAdventure by remember { mutableIntStateOf(0) }
-
- LaunchedEffect(cardId) {
- withContext(Dispatchers.IO) {
- cardAdventureMissions.value =
- cardScreenController
- .getCardAdventureMissions(cardId)
-
- currentCardAdventure =
- cardScreenController
- .getCardProgress(cardId)
- }
- }
+ val cardAdventureMissions by cardScreenController
+ .getCardAdventureMissions(cardId)
+ .collectAsState(emptyList())
+ val currentCardAdventure by cardScreenController
+ .getCardProgress(cardId)
+ .collectAsState(0)
Scaffold (
topBar = {
@@ 55,7 40,7 @@ fun CardAdventureScreen(
.padding(top = contentPadding.calculateTopPadding())
.verticalScroll(state = rememberScrollState())
) {
- cardAdventureMissions.value.mapIndexed { index, it ->
+ cardAdventureMissions.mapIndexed { index, it ->
CardAdventureEntry(
cardAdventureEntry = it,
obscure = index > currentCardAdventure - 1
M app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardScreenController.kt => app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardScreenController.kt +5 -2
@@ 1,10 1,13 @@
package com.github.nacabaro.vbhelper.screens.cardScreen
import com.github.nacabaro.vbhelper.dtos.CardDtos
+import com.github.nacabaro.vbhelper.dtos.CharacterDtos
+import kotlinx.coroutines.flow.Flow
interface CardScreenController {
fun renameCard(cardId: Long, newName: String, onRenamed: (String) -> Unit)
fun deleteCard(cardId: Long, onDeleted: () -> Unit)
- suspend fun getCardAdventureMissions(cardId: Long): List<CardDtos.CardAdventureWithSprites>
- suspend fun getCardProgress(cardId: Long): Int
+ fun getCardAdventureMissions(cardId: Long): Flow<List<CardDtos.CardAdventureWithSprites>>
+ fun getCardProgress(cardId: Long): Flow<Int>
+ fun getFusionsForCharacters(characterId: Long): Flow<List<CharacterDtos.FusionsWithSpritesAndObtained>>
}=
\ No newline at end of file
M app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardScreenControllerImpl.kt => app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardScreenControllerImpl.kt +11 -2
@@ 4,6 4,8 @@ import androidx.activity.ComponentActivity
import androidx.lifecycle.lifecycleScope
import com.github.nacabaro.vbhelper.di.VBHelper
import com.github.nacabaro.vbhelper.dtos.CardDtos
+import com.github.nacabaro.vbhelper.dtos.CharacterDtos
+import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.launch
class CardScreenControllerImpl(
@@ 32,15 34,22 @@ class CardScreenControllerImpl(
}
}
- override suspend fun getCardAdventureMissions(cardId: Long): List<CardDtos.CardAdventureWithSprites> {
+ override fun getCardAdventureMissions(cardId: Long): Flow<List<CardDtos.CardAdventureWithSprites>> {
return database
.cardAdventureDao()
.getAdventureForCard(cardId)
}
- override suspend fun getCardProgress(cardId: Long): Int {
+ 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)
+ }
+
}=
\ No newline at end of file
M app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardViewScreen.kt => app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardViewScreen.kt +4 -18
@@ 5,10 5,10 @@ import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.items
import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
-import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.platform.LocalContext
import androidx.navigation.NavController
import com.github.nacabaro.vbhelper.utils.BitmapData
@@ 19,32 19,19 @@ import com.github.nacabaro.vbhelper.dtos.CharacterDtos
import com.github.nacabaro.vbhelper.navigation.NavigationItems
import com.github.nacabaro.vbhelper.screens.cardScreen.dialogs.DexCharaDetailsDialog
import com.github.nacabaro.vbhelper.source.DexRepository
-import kotlinx.coroutines.launch
@Composable
fun CardViewScreen(
navController: NavController,
cardId: Long
) {
- val coroutineScope = rememberCoroutineScope()
val application = LocalContext.current.applicationContext as VBHelper
val dexRepository = DexRepository(application.container.db)
- val characterList = remember { mutableStateOf<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(cardId)
- characterList.value = newCharacterList
-
- val newCardPossibleTransformations = dexRepository.getCardPossibleTransformations(cardId)
- cardPossibleTransformations.value = newCardPossibleTransformations
- }
- }
-
Scaffold (
topBar = {
TopBanner(
@@ 70,7 57,7 @@ fun CardViewScreen(
columns = GridCells.Fixed(3),
contentPadding = contentPadding
) {
- items(characterList.value) { character ->
+ items(characterList) { character ->
CharacterEntry(
onClick = {
selectedCharacter.value = character
@@ 88,7 75,6 @@ fun CardViewScreen(
if (selectedCharacter.value != null) {
DexCharaDetailsDialog(
currentChara = selectedCharacter.value!!,
- possibleTransformations = cardPossibleTransformations.value,
obscure = selectedCharacter.value!!.discoveredOn == null,
onClickClose = {
selectedCharacter.value = null
M app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardsScreen.kt => app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardsScreen.kt +3 -13
@@ 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(
M app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/dialogs/DexCharaDetailsDialog.kt => app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/dialogs/DexCharaDetailsDialog.kt +52 -6
@@ 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
+ )
+ }
}=
\ No newline at end of file
A app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/dialogs/DexCharaFusionsDialog.kt => app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/dialogs/DexCharaFusionsDialog.kt +191 -0
@@ 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")
+ }
+ }
+ }
+ }
+}<
\ No newline at end of file
M app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/HomeScreen.kt => app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/HomeScreen.kt +0 -3
@@ 1,6 1,5 @@
package com.github.nacabaro.vbhelper.screens.homeScreens
-import android.util.Log
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
@@ 92,7 91,6 @@ fun HomeScreen(
}
) { contentPadding ->
if (activeMon.value == null || (beData.value == null && vbData.value == null) || transformationHistory.value == null) {
- Log.d("TetTet", "Something is null")
Column (
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
@@ 103,7 101,6 @@ fun HomeScreen(
Text(text = "Nothing to see here")
}
} else {
- Log.d("TetTet", "Something is not null")
if (activeMon.value!!.isBemCard) {
BEBEmHomeScreen(
activeMon = activeMon.value!!,
M app/src/main/java/com/github/nacabaro/vbhelper/screens/settingsScreen/SettingsScreenControllerImpl.kt => app/src/main/java/com/github/nacabaro/vbhelper/screens/settingsScreen/SettingsScreenControllerImpl.kt +10 -374
@@ 3,36 3,24 @@ package com.github.nacabaro.vbhelper.screens.settingsScreen
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.launch
import android.net.Uri
-import android.provider.OpenableColumns
-import android.util.Log
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
-import com.github.cfogrady.vb.dim.card.BemCard
-import com.github.cfogrady.vb.dim.card.DimCard
-import com.github.cfogrady.vb.dim.card.DimReader
-import com.github.cfogrady.vbnfc.data.NfcCharacter
import com.github.nacabaro.vbhelper.database.AppDatabase
import com.github.nacabaro.vbhelper.di.VBHelper
-import com.github.nacabaro.vbhelper.domain.characters.Sprite
-import com.github.nacabaro.vbhelper.domain.card.Card
-import com.github.nacabaro.vbhelper.domain.card.CardProgress
-import com.github.nacabaro.vbhelper.domain.card.CardCharacter
+import com.github.nacabaro.vbhelper.screens.settingsScreen.controllers.CardImportController
+import com.github.nacabaro.vbhelper.screens.settingsScreen.controllers.DatabaseManagementController
import com.github.nacabaro.vbhelper.source.ApkSecretsImporter
import com.github.nacabaro.vbhelper.source.SecretsImporter
import com.github.nacabaro.vbhelper.source.SecretsRepository
import com.github.nacabaro.vbhelper.source.proto.Secrets
import kotlinx.coroutines.Dispatchers
-import java.io.File
-import java.io.InputStream
-import java.io.OutputStream
class SettingsScreenControllerImpl(
private val context: ComponentActivity,
): SettingsScreenController {
- private val roomDbName = "internalDb"
private val filePickerLauncher: ActivityResultLauncher<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,276 101,14 @@ class SettingsScreenControllerImpl(
filePickerCard.launch(arrayOf("*/*"))
}
- private suspend fun importEvoData(
- cardId: Long,
- card: com.github.cfogrady.vb.dim.card.Card<*, *, *, *, *, *>
- ) {
- for (index in 0 until card.transformationRequirements.transformationEntries.size) {
- val evo = card.transformationRequirements.transformationEntries[index]
-
- var transformationTimerHours: Int
- var unlockAdventureLevel: Int
-
- if (card is BemCard) {
- transformationTimerHours = card
- .transformationRequirements
- .transformationEntries[index]
- .minutesUntilTransformation / 60
- unlockAdventureLevel = if (
- card
- .transformationRequirements
- .transformationEntries[index]
- .requiredCompletedAdventureLevel == 65535
- ) {
- 0
- } else {
- card
- .transformationRequirements
- .transformationEntries[index]
- .requiredCompletedAdventureLevel
- }
- } else {
- transformationTimerHours = (card as DimCard)
- .transformationRequirements
- .transformationEntries[index]
- .hoursUntilEvolution
- unlockAdventureLevel = if (
- card
- .adventureLevels
- .levels
- .last()
- .bossCharacterIndex == card.transformationRequirements.transformationEntries[index].toCharacterIndex
- ) {
- 14
- /*
- Magic number incoming!!
-
- In the case of DiMCards, stage 15 is the one that unlocks the locked character.
- We know it is a locked character if the last adventure level's boss character index
- is the current index. If it is, we add stage 15 complete as a requirement for transformation.
- */
- } else {
- 0
- /*
- Another magic number...
-
- The rest of the characters are not locked.
- */
- }
- }
-
- database
- .characterDao()
- .insertPossibleTransformation(
- cardId = cardId,
- fromChraraIndex = evo.fromCharacterIndex,
- toChraraIndex = evo.toCharacterIndex,
- requiredVitals = evo.requiredVitalValues,
- requiredTrophies = evo.requiredTrophies,
- requiredBattles = evo.requiredBattles,
- requiredWinRate = evo.requiredWinRatio,
- requiredAdventureLevelCompleted = unlockAdventureLevel,
- changeTimerHours = transformationTimerHours
- )
- }
- }
-
- private suspend fun importCharacterData(
- cardId: Long,
- card: com.github.cfogrady.vb.dim.card.Card<*, *, *, *, *, *>
- ) {
- var spriteCounter = when (card is BemCard) {
- true -> 54
- false -> 10
- }
-
- val domainCharacters = mutableListOf<CardCharacter>()
-
- val characters = card
- .characterStats
- .characterEntries
-
- for (index in 0 until characters.size) {
- var domainSprite: Sprite?
- if (index < 2 && card is DimCard) {
- domainSprite = Sprite(
- width = card.spriteData.sprites[spriteCounter + 1].spriteDimensions.width,
- height = card.spriteData.sprites[spriteCounter + 1].spriteDimensions.height,
- spriteIdle1 = card.spriteData.sprites[spriteCounter + 1].pixelData,
- spriteIdle2 = card.spriteData.sprites[spriteCounter + 2].pixelData,
- spriteWalk1 = card.spriteData.sprites[spriteCounter + 1].pixelData,
- spriteWalk2 = card.spriteData.sprites[spriteCounter + 3].pixelData,
- spriteRun1 = card.spriteData.sprites[spriteCounter + 1].pixelData,
- spriteRun2 = card.spriteData.sprites[spriteCounter + 3].pixelData,
- spriteTrain1 = card.spriteData.sprites[spriteCounter + 1].pixelData,
- spriteTrain2 = card.spriteData.sprites[spriteCounter + 3].pixelData,
- spriteHappy = card.spriteData.sprites[spriteCounter + 4].pixelData,
- spriteSleep = card.spriteData.sprites[spriteCounter + 5].pixelData,
- spriteAttack = card.spriteData.sprites[spriteCounter + 2].pixelData,
- spriteDodge = card.spriteData.sprites[spriteCounter + 3].pixelData
- )
- } else {
- domainSprite = Sprite(
- width = card.spriteData.sprites[spriteCounter + 1].spriteDimensions.width,
- height = card.spriteData.sprites[spriteCounter + 1].spriteDimensions.height,
- spriteIdle1 = card.spriteData.sprites[spriteCounter + 1].pixelData,
- spriteIdle2 = card.spriteData.sprites[spriteCounter + 2].pixelData,
- spriteWalk1 = card.spriteData.sprites[spriteCounter + 3].pixelData,
- spriteWalk2 = card.spriteData.sprites[spriteCounter + 4].pixelData,
- spriteRun1 = card.spriteData.sprites[spriteCounter + 5].pixelData,
- spriteRun2 = card.spriteData.sprites[spriteCounter + 6].pixelData,
- spriteTrain1 = card.spriteData.sprites[spriteCounter + 7].pixelData,
- spriteTrain2 = card.spriteData.sprites[spriteCounter + 8].pixelData,
- spriteHappy = card.spriteData.sprites[spriteCounter + 9].pixelData,
- spriteSleep = card.spriteData.sprites[spriteCounter + 10].pixelData,
- spriteAttack = card.spriteData.sprites[spriteCounter + 11].pixelData,
- spriteDodge = card.spriteData.sprites[spriteCounter + 12].pixelData
- )
- }
-
- val spriteId = database
- .spriteDao()
- .insertSprite(domainSprite)
-
-
- domainCharacters.add(
- CardCharacter(
- cardId = cardId,
- spriteId = spriteId,
- charaIndex = index,
- nameSprite = card.spriteData.sprites[spriteCounter].pixelData,
- stage = characters[index].stage,
- attribute = NfcCharacter.Attribute.entries[characters[index].attribute],
- baseHp = characters[index].hp,
- baseBp = characters[index].dp,
- baseAp = characters[index].ap,
- nameWidth = card.spriteData.sprites[spriteCounter].spriteDimensions.width,
- nameHeight = card.spriteData.sprites[spriteCounter].spriteDimensions.height
- )
- )
-
- spriteCounter += if (card is BemCard) {
- 14
- } else {
- when (index) {
- 0 -> 6
- 1 -> 7
- else -> 14
- }
- }
- }
-
- database
- .characterDao()
- .insertCharacter(*domainCharacters.toTypedArray())
- }
-
- private suspend fun importAdventureMissions(
- cardId: Long,
- card: com.github.cfogrady.vb.dim.card.Card<*, *, *, *, *, *>
- ) {
- Log.d("importAdventureMissions", "Importing adventure missions")
- if (card is BemCard) {
- card.adventureLevels.levels.forEach {
- database
- .cardAdventureDao()
- .insertNewAdventure(
- cardId = cardId,
- characterId = it.bossCharacterIndex,
- steps = it.steps,
- bossAp = it.bossAp,
- bossHp = it.bossHp,
- bossDp = it.bossDp,
- bossBp = it.bossBp
- )
- }
- } else if (card is DimCard) {
- card.adventureLevels.levels.map {
- database
- .cardAdventureDao()
- .insertNewAdventure(
- cardId = cardId,
- characterId = it.bossCharacterIndex,
- steps = it.steps,
- bossAp = it.bossAp,
- bossHp = it.bossHp,
- bossDp = it.bossDp,
- bossBp = null
- )
- }
- }
- }
-
- private suspend fun importCardFusions(
- cardId: Long,
- card: com.github.cfogrady.vb.dim.card.Card<*, *, *, *, *, *>
- ) {
- Log.d("importCardFusions", "Importing card fusions")
- if (card is DimCard) {
- card
- .attributeFusions
- .entries
- .forEach {
- database
- .cardFusionsDao()
- .insertNewFusion(
- cardId = cardId,
- fromCharaId = it.characterIndex,
- toCharaIdAttr1 = it.attribute1Fusion,
- toCharaIdAttr2 = it.attribute2Fusion,
- toCharaIdAttr3 = it.attribute3Fusion,
- toCharaIdAttr4 = it.attribute4Fusion
- )
- }
- }
- }
-
- private fun updateCardProgress(
- cardId: Long,
- ) {
- database
- .cardProgressDao()
- .insertCardProgress(
- CardProgress(
- cardId = cardId,
- currentStage = 1,
- unlocked = false
- )
- )
- }
-
private fun importCard(uri: Uri) {
context.lifecycleScope.launch(Dispatchers.IO) {
val contentResolver = context.contentResolver
val inputStream = contentResolver.openInputStream(uri)
inputStream.use { fileReader ->
- val dimReader = DimReader()
- val card = dimReader.readCard(fileReader, false)
-
- val cardModel = Card(
- cardId = card.header.dimId,
- logo = card.spriteData.sprites[0].pixelData,
- name = card.spriteData.text,
- stageCount = card.adventureLevels.levels.size,
- logoHeight = card.spriteData.sprites[0].height,
- logoWidth = card.spriteData.sprites[0].width,
- isBEm = card is BemCard
- )
-
- val cardId = database
- .cardDao()
- .insertNewCard(cardModel)
-
- updateCardProgress(cardId = cardId)
-
- importCharacterData(cardId, card)
-
- importEvoData(cardId, card)
-
- importAdventureMissions(cardId, card)
-
- importCardFusions(cardId, card)
+ val cardImportController = CardImportController(database)
+ cardImportController.importCard(fileReader)
}
inputStream?.close()
@@ 388,100 118,6 @@ class SettingsScreenControllerImpl(
}
}
- private fun exportDatabase(destinationUri: Uri) {
- context.lifecycleScope.launch(Dispatchers.IO) {
- try {
- val dbFile = File(context.getDatabasePath(roomDbName).absolutePath)
- if (!dbFile.exists()) {
- throw IllegalStateException("Database file does not exist!")
- }
-
- application.container.db.close()
-
- context.contentResolver.openOutputStream(destinationUri)?.use { outputStream ->
- dbFile.inputStream().use { inputStream ->
- copyFile(inputStream, outputStream)
- }
- } ?: throw IllegalArgumentException("Unable to open destination Uri for writing")
-
- context.runOnUiThread {
- Toast.makeText(context, "Database exported successfully!", Toast.LENGTH_SHORT).show()
- Toast.makeText(context, "Closing application to avoid changes.", Toast.LENGTH_LONG).show()
- context.finishAffinity()
- }
- } catch (e: Exception) {
- Log.e("ScanScreenController", "Error exporting database $e")
- context.runOnUiThread {
- Toast.makeText(context, "Error exporting database: ${e.message}", Toast.LENGTH_LONG).show()
- }
- }
- }
- }
-
- private fun importDatabase(sourceUri: Uri) {
- context.lifecycleScope.launch(Dispatchers.IO) {
- try {
- if (!getFileNameFromUri(sourceUri)!!.endsWith(".vbhelper")) {
- context.runOnUiThread {
- Toast.makeText(context, "Invalid file format", Toast.LENGTH_SHORT).show()
- }
- return@launch
- }
-
- application.container.db.close()
-
- val dbPath = context.getDatabasePath(roomDbName)
- val shmFile = File(dbPath.parent, "$roomDbName-shm")
- val walFile = File(dbPath.parent, "$roomDbName-wal")
-
- // Delete existing database files
- if (dbPath.exists()) dbPath.delete()
- if (shmFile.exists()) shmFile.delete()
- if (walFile.exists()) walFile.delete()
-
- val dbFile = File(dbPath.absolutePath)
-
- context.contentResolver.openInputStream(sourceUri)?.use { inputStream ->
- dbFile.outputStream().use { outputStream ->
- copyFile(inputStream, outputStream)
- }
- } ?: throw IllegalArgumentException("Unable to open source Uri for reading")
-
- context.runOnUiThread {
- Toast.makeText(context, "Database imported successfully!", Toast.LENGTH_SHORT).show()
- Toast.makeText(context, "Reopen the app to finish import process!", Toast.LENGTH_LONG).show()
- context.finishAffinity()
- }
- } catch (e: Exception) {
- Log.e("ScanScreenController", "Error importing database $e")
- context.runOnUiThread {
- Toast.makeText(context, "Error importing database: ${e.message}", Toast.LENGTH_LONG).show()
- }
- }
- }
- }
-
- private fun getFileNameFromUri(uri: Uri): String? {
- var fileName: String? = null
- val cursor = context.contentResolver.query(uri, null, null, null, null)
- cursor?.use {
- if (it.moveToFirst()) {
- val nameIndex = it.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME)
- fileName = it.getString(nameIndex)
- }
- }
- return fileName
- }
-
- private fun copyFile(inputStream: InputStream, outputStream: OutputStream) {
- val buffer = ByteArray(1024)
- var bytesRead: Int
- while (inputStream.read(buffer).also { bytesRead = it } != -1) {
- outputStream.write(buffer, 0, bytesRead)
- }
- outputStream.flush()
- }
-
private fun importApk(uri: Uri) {
context.lifecycleScope.launch(Dispatchers.IO) {
context.contentResolver.openInputStream(uri).use {
A app/src/main/java/com/github/nacabaro/vbhelper/screens/settingsScreen/controllers/CardImportController.kt => app/src/main/java/com/github/nacabaro/vbhelper/screens/settingsScreen/controllers/CardImportController.kt +320 -0
@@ 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
+ )
+ }
+ }
+}<
\ No newline at end of file
A app/src/main/java/com/github/nacabaro/vbhelper/screens/settingsScreen/controllers/DatabaseManagementController.kt => app/src/main/java/com/github/nacabaro/vbhelper/screens/settingsScreen/controllers/DatabaseManagementController.kt +115 -0
@@ 0,0 1,115 @@
+package com.github.nacabaro.vbhelper.screens.settingsScreen.controllers
+
+import android.net.Uri
+import android.provider.OpenableColumns
+import android.util.Log
+import android.widget.Toast
+import androidx.activity.ComponentActivity
+import androidx.lifecycle.lifecycleScope
+import com.github.nacabaro.vbhelper.di.VBHelper
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import java.io.File
+import java.io.InputStream
+import java.io.OutputStream
+
+class DatabaseManagementController(
+ val componentActivity: ComponentActivity,
+ val application: VBHelper
+) {
+ private val roomDbName = "internalDb"
+
+ fun exportDatabase( destinationUri: Uri) {
+ componentActivity.lifecycleScope.launch(Dispatchers.IO) {
+ try {
+ val dbFile = File(componentActivity.getDatabasePath(roomDbName).absolutePath)
+ if (!dbFile.exists()) {
+ throw IllegalStateException("Database file does not exist!")
+ }
+
+ application.container.db.close()
+
+ componentActivity.contentResolver.openOutputStream(destinationUri)?.use { outputStream ->
+ dbFile.inputStream().use { inputStream ->
+ copyFile(inputStream, outputStream)
+ }
+ } ?: throw IllegalArgumentException("Unable to open destination Uri for writing")
+
+ componentActivity.runOnUiThread {
+ Toast.makeText(componentActivity, "Database exported successfully!", Toast.LENGTH_SHORT).show()
+ Toast.makeText(componentActivity, "Closing application to avoid changes.", Toast.LENGTH_LONG).show()
+ componentActivity.finishAffinity()
+ }
+ } catch (e: Exception) {
+ Log.e("ScanScreenController", "Error exporting database $e")
+ componentActivity.runOnUiThread {
+ Toast.makeText(componentActivity, "Error exporting database: ${e.message}", Toast.LENGTH_LONG).show()
+ }
+ }
+ }
+ }
+
+ fun importDatabase(sourceUri: Uri) {
+ componentActivity.lifecycleScope.launch(Dispatchers.IO) {
+ try {
+ if (!getFileNameFromUri(sourceUri)!!.endsWith(".vbhelper")) {
+ componentActivity.runOnUiThread {
+ Toast.makeText(componentActivity, "Invalid file format", Toast.LENGTH_SHORT).show()
+ }
+ return@launch
+ }
+
+ application.container.db.close()
+
+ val dbPath = componentActivity.getDatabasePath(roomDbName)
+ val shmFile = File(dbPath.parent, "$roomDbName-shm")
+ val walFile = File(dbPath.parent, "$roomDbName-wal")
+
+ // Delete existing database files
+ if (dbPath.exists()) dbPath.delete()
+ if (shmFile.exists()) shmFile.delete()
+ if (walFile.exists()) walFile.delete()
+
+ val dbFile = File(dbPath.absolutePath)
+
+ componentActivity.contentResolver.openInputStream(sourceUri)?.use { inputStream ->
+ dbFile.outputStream().use { outputStream ->
+ copyFile(inputStream, outputStream)
+ }
+ } ?: throw IllegalArgumentException("Unable to open source Uri for reading")
+
+ componentActivity.runOnUiThread {
+ Toast.makeText(componentActivity, "Database imported successfully!", Toast.LENGTH_SHORT).show()
+ Toast.makeText(componentActivity, "Reopen the app to finish import process!", Toast.LENGTH_LONG).show()
+ componentActivity.finishAffinity()
+ }
+ } catch (e: Exception) {
+ Log.e("ScanScreenController", "Error importing database $e")
+ componentActivity.runOnUiThread {
+ Toast.makeText(componentActivity, "Error importing database: ${e.message}", Toast.LENGTH_LONG).show()
+ }
+ }
+ }
+ }
+
+ private fun copyFile(inputStream: InputStream, outputStream: OutputStream) {
+ val buffer = ByteArray(1024)
+ var bytesRead: Int
+ while (inputStream.read(buffer).also { bytesRead = it } != -1) {
+ outputStream.write(buffer, 0, bytesRead)
+ }
+ outputStream.flush()
+ }
+
+ private fun getFileNameFromUri(uri: Uri): String? {
+ var fileName: String? = null
+ val cursor = componentActivity.contentResolver.query(uri, null, null, null, null)
+ cursor?.use {
+ if (it.moveToFirst()) {
+ val nameIndex = it.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME)
+ fileName = it.getString(nameIndex)
+ }
+ }
+ return fileName
+ }
+}<
\ No newline at end of file
M app/src/main/java/com/github/nacabaro/vbhelper/source/DexRepository.kt => app/src/main/java/com/github/nacabaro/vbhelper/source/DexRepository.kt +9 -4
@@ 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)
}
}=
\ No newline at end of file