More things:

- Improved upload function to support VB (still to do to add VB)
- Adapted to the latest version of the vb-nfc-reader library
- Added foreign keys with uploaded characters and existing dim database
- Removed the tempoorary objects
- Handled errors uploading (dim not existing for now and VB not supported yet)
- Improved bitmap conversion by moving it to it's own composable
  - Also added object to move bitmap data around
- Added DTO to read sprite data and character data simultaneously

Good night!
This commit is contained in:
Nacho 2025-01-08 02:31:36 +01:00
parent 5edd753da1
commit a028611d8a
23 changed files with 384 additions and 268 deletions

View File

@ -29,8 +29,10 @@ import com.github.nacabaro.vbhelper.di.VBHelper
import com.github.nacabaro.vbhelper.domain.Dim import com.github.nacabaro.vbhelper.domain.Dim
import com.github.nacabaro.vbhelper.domain.Sprites import com.github.nacabaro.vbhelper.domain.Sprites
import com.github.nacabaro.vbhelper.domain.Character import com.github.nacabaro.vbhelper.domain.Character
import com.github.nacabaro.vbhelper.domain.device_data.BECharacterData
import com.github.nacabaro.vbhelper.domain.device_data.TransformationHistory
import com.github.nacabaro.vbhelper.domain.device_data.UserCharacter
import com.github.nacabaro.vbhelper.temporary_domain.TemporaryBECharacterData import com.github.nacabaro.vbhelper.temporary_domain.TemporaryBECharacterData
import com.github.nacabaro.vbhelper.temporary_domain.TemporaryCharacterData
import com.github.nacabaro.vbhelper.temporary_domain.TemporaryTransformationHistory import com.github.nacabaro.vbhelper.temporary_domain.TemporaryTransformationHistory
import com.github.nacabaro.vbhelper.ui.theme.VBHelperTheme import com.github.nacabaro.vbhelper.ui.theme.VBHelperTheme
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
@ -72,6 +74,9 @@ class MainActivity : ComponentActivity() {
ActivityResultContracts.StartActivityForResult() ActivityResultContracts.StartActivityForResult()
) { ) {
lifecycleScope.launch { lifecycleScope.launch {
val application = applicationContext as VBHelper
val storageRepository = application.container.db
if (it.resultCode != RESULT_OK) { if (it.resultCode != RESULT_OK) {
Toast.makeText(applicationContext, "Import operation cancelled.", Toast.LENGTH_SHORT).show() Toast.makeText(applicationContext, "Import operation cancelled.", Toast.LENGTH_SHORT).show()
} }
@ -81,44 +86,27 @@ class MainActivity : ComponentActivity() {
val dimReader = DimReader() val dimReader = DimReader()
val card = dimReader.readCard(fileReader, false) val card = dimReader.readCard(fileReader, false)
val dimModel = Dim( val dimModel = Dim(
id = card.header.dimId, dimId = card.header.dimId,
logo = card.spriteData.sprites[0].pixelData, logo = card.spriteData.sprites[0].pixelData,
name = card.spriteData.text, // TODO Make user write card name name = card.spriteData.text, // TODO Make user write card name
stageCount = card.adventureLevels.levels.size, stageCount = card.adventureLevels.levels.size,
logoHeight = card.spriteData.sprites[0].height, logoHeight = card.spriteData.sprites[0].height,
logoWidth = card.spriteData.sprites[0].width logoWidth = card.spriteData.sprites[0].width
) )
val application = applicationContext as VBHelper
val storageRepository = application.container.db val dimId = storageRepository
storageRepository.dimDao().insertNewDim(dimModel) .dimDao()
.insertNewDim(dimModel)
val characters = card.characterStats.characterEntries val characters = card.characterStats.characterEntries
/*
Confusing math for me ahead
sprite[0] logo
sprite[10] name
sprite[10 + 1] character_1
sprite[10 + 2] character_2
sprite[16] name 1
sprite[17] character_1
sprite[18] character_2
sprite[23] name 2
sprite[24] character_1
sprite[25] character_2
sprite[23 + 12 + 1] name 3
sprite[23 + 12 + 2] character_1
sprite[23 + 12 + 3] character_2
*/
var spriteCounter = 10 var spriteCounter = 10
var domainCharacters = mutableListOf<Character>() val domainCharacters = mutableListOf<Character>()
for (index in 0 until characters.size) { for (index in 0 until characters.size) {
domainCharacters.add( domainCharacters.add(
Character( Character(
id = 0, dimId = dimId,
dimId = card.header.dimId,
monIndex = index, monIndex = index,
name = card.spriteData.sprites[spriteCounter].pixelData, name = card.spriteData.sprites[spriteCounter].pixelData,
stage = characters[index].stage, stage = characters[index].stage,
@ -135,6 +123,7 @@ class MainActivity : ComponentActivity() {
) )
) )
// TODO: Improve this
if (index == 0) { if (index == 0) {
spriteCounter += 6 spriteCounter += 6
} else if (index == 1) { } else if (index == 1) {
@ -176,9 +165,12 @@ class MainActivity : ComponentActivity() {
handleTag { handleTag {
val character = it.receiveCharacter() val character = it.receiveCharacter()
nfcCharacter.value = character nfcCharacter.value = character
addCharacterScannedIntoDatabase()
val importStatus = addCharacterScannedIntoDatabase()
isDoneReadingCharacter = true isDoneReadingCharacter = true
"Done reading character"
importStatus
} }
}, },
onClickScan = { onClickScan = {
@ -273,19 +265,31 @@ class MainActivity : ComponentActivity() {
now, it's a matter of implementing the functionality to parse dim/bem cards and use my now, it's a matter of implementing the functionality to parse dim/bem cards and use my
domain model. domain model.
*/ */
private fun addCharacterScannedIntoDatabase() { private fun addCharacterScannedIntoDatabase(): String {
val beCharacter = nfcCharacter as MutableStateFlow<BENfcCharacter?> val application = applicationContext as VBHelper
val storageRepository = application.container.db
val temporaryCharacterData = TemporaryCharacterData( val dimData = storageRepository
dimId = nfcCharacter.value!!.dimId.toInt(), .dimDao()
charIndex = nfcCharacter.value!!.charIndex.toInt(), .getDimById(nfcCharacter.value!!.dimId.toInt())
if (dimData == null) {
return "Card not found"
}
val cardCharData = storageRepository
.characterDao()
.getCharacterByMonIndex(nfcCharacter.value!!.charIndex.toInt(), dimData.id)
val characterData = UserCharacter(
charId = cardCharData.id,
stage = nfcCharacter.value!!.stage.toInt(), stage = nfcCharacter.value!!.stage.toInt(),
attribute = nfcCharacter.value!!.attribute, attribute = nfcCharacter.value!!.attribute,
ageInDays = nfcCharacter.value!!.ageInDays.toInt(), ageInDays = nfcCharacter.value!!.ageInDays.toInt(),
nextAdventureMissionStage = nfcCharacter.value!!.nextAdventureMissionStage.toInt(), nextAdventureMissionStage = nfcCharacter.value!!.nextAdventureMissionStage.toInt(),
mood = nfcCharacter.value!!.mood.toInt(), mood = nfcCharacter.value!!.mood.toInt(),
vitalPoints = nfcCharacter.value!!.vitalPoints.toInt(), vitalPoints = nfcCharacter.value!!.vitalPoints.toInt(),
transformationCountdown = nfcCharacter.value!!.transformationCountdown.toInt(), transformationCountdown = nfcCharacter.value!!.transformationCountdownInMinutes.toInt(),
injuryStatus = nfcCharacter.value!!.injuryStatus, injuryStatus = nfcCharacter.value!!.injuryStatus,
trophies = nfcCharacter.value!!.trophies.toInt(), trophies = nfcCharacter.value!!.trophies.toInt(),
currentPhaseBattlesWon = nfcCharacter.value!!.currentPhaseBattlesWon.toInt(), currentPhaseBattlesWon = nfcCharacter.value!!.currentPhaseBattlesWon.toInt(),
@ -293,58 +297,67 @@ class MainActivity : ComponentActivity() {
totalBattlesWon = nfcCharacter.value!!.totalBattlesWon.toInt(), totalBattlesWon = nfcCharacter.value!!.totalBattlesWon.toInt(),
totalBattlesLost = nfcCharacter.value!!.totalBattlesLost.toInt(), totalBattlesLost = nfcCharacter.value!!.totalBattlesLost.toInt(),
activityLevel = nfcCharacter.value!!.activityLevel.toInt(), activityLevel = nfcCharacter.value!!.activityLevel.toInt(),
heartRateCurrent = nfcCharacter.value!!.heartRateCurrent.toInt() heartRateCurrent = nfcCharacter.value!!.heartRateCurrent.toInt(),
characterType = when (nfcCharacter.value) {
is BENfcCharacter -> com.github.nacabaro.vbhelper.domain.DeviceType.BEDevice
else -> com.github.nacabaro.vbhelper.domain.DeviceType.VBDevice
}
) )
val application = applicationContext as VBHelper
val storageRepository = application.container.db
val characterId: Long = storageRepository val characterId: Long = storageRepository
.temporaryMonsterDao() .userCharacterDao()
.insertCharacterData(temporaryCharacterData) .insertCharacterData(characterData)
val temporaryBECharacterData = TemporaryBECharacterData( if (nfcCharacter.value is BENfcCharacter) {
id = characterId, val beCharacter = nfcCharacter as MutableStateFlow<BENfcCharacter?>
trainingHp = beCharacter.value!!.trainingHp.toInt(), val extraCharacterData = BECharacterData(
trainingAp = beCharacter.value!!.trainingAp.toInt(), id = characterId,
trainingBp = beCharacter.value!!.trainingBp.toInt(), trainingHp = beCharacter.value!!.trainingHp.toInt(),
remainingTrainingTimeInMinutes = beCharacter.value!!.remainingTrainingTimeInMinutes.toInt(), trainingAp = beCharacter.value!!.trainingAp.toInt(),
itemEffectActivityLevelValue = beCharacter.value!!.itemEffectActivityLevelValue.toInt(), trainingBp = beCharacter.value!!.trainingBp.toInt(),
itemEffectMentalStateValue = beCharacter.value!!.itemEffectMentalStateValue.toInt(), remainingTrainingTimeInMinutes = beCharacter.value!!.remainingTrainingTimeInMinutes.toInt(),
itemEffectMentalStateMinutesRemaining = beCharacter.value!!.itemEffectMentalStateMinutesRemaining.toInt(), itemEffectActivityLevelValue = beCharacter.value!!.itemEffectActivityLevelValue.toInt(),
itemEffectActivityLevelMinutesRemaining = beCharacter.value!!.itemEffectActivityLevelMinutesRemaining.toInt(), itemEffectMentalStateValue = beCharacter.value!!.itemEffectMentalStateValue.toInt(),
itemEffectVitalPointsChangeValue = beCharacter.value!!.itemEffectVitalPointsChangeValue.toInt(), itemEffectMentalStateMinutesRemaining = beCharacter.value!!.itemEffectMentalStateMinutesRemaining.toInt(),
itemEffectVitalPointsChangeMinutesRemaining = beCharacter.value!!.itemEffectVitalPointsChangeMinutesRemaining.toInt(), itemEffectActivityLevelMinutesRemaining = beCharacter.value!!.itemEffectActivityLevelMinutesRemaining.toInt(),
abilityRarity = beCharacter.value!!.abilityRarity, itemEffectVitalPointsChangeValue = beCharacter.value!!.itemEffectVitalPointsChangeValue.toInt(),
abilityType = beCharacter.value!!.abilityType.toInt(), itemEffectVitalPointsChangeMinutesRemaining = beCharacter.value!!.itemEffectVitalPointsChangeMinutesRemaining.toInt(),
abilityBranch = beCharacter.value!!.abilityBranch.toInt(), abilityRarity = beCharacter.value!!.abilityRarity,
abilityReset = beCharacter.value!!.abilityReset.toInt(), abilityType = beCharacter.value!!.abilityType.toInt(),
rank = beCharacter.value!!.abilityReset.toInt(), abilityBranch = beCharacter.value!!.abilityBranch.toInt(),
itemType = beCharacter.value!!.itemType.toInt(), abilityReset = beCharacter.value!!.abilityReset.toInt(),
itemMultiplier = beCharacter.value!!.itemMultiplier.toInt(), rank = beCharacter.value!!.abilityReset.toInt(),
itemRemainingTime = beCharacter.value!!.itemRemainingTime.toInt(), itemType = beCharacter.value!!.itemType.toInt(),
otp0 = "", //beCharacter.value!!.otp0.toString(), itemMultiplier = beCharacter.value!!.itemMultiplier.toInt(),
otp1 = "", //beCharacter.value!!.otp1.toString(), itemRemainingTime = beCharacter.value!!.itemRemainingTime.toInt(),
minorVersion = beCharacter.value!!.characterCreationFirmwareVersion.minorVersion.toInt(), otp0 = "", //beCharacter.value!!.otp0.toString(),
majorVersion = beCharacter.value!!.characterCreationFirmwareVersion.majorVersion.toInt(), otp1 = "", //beCharacter.value!!.otp1.toString(),
) minorVersion = beCharacter.value!!.characterCreationFirmwareVersion.minorVersion.toInt(),
majorVersion = beCharacter.value!!.characterCreationFirmwareVersion.majorVersion.toInt(),
storageRepository
.temporaryMonsterDao()
.insertBECharacterData(temporaryBECharacterData)
val transformationHistoryWatch = beCharacter.value!!.transformationHistory
val domainTransformationHistory = transformationHistoryWatch.map { item ->
TemporaryTransformationHistory(
monId = characterId,
toCharIndex = item.toCharIndex.toInt(),
yearsSince1988 = item.yearsSince1988.toInt(),
month = item.month.toInt(),
day = item.day.toInt()
) )
storageRepository
.userCharacterDao()
.insertBECharacterData(extraCharacterData)
val transformationHistoryWatch = beCharacter.value!!.transformationHistory
val domainTransformationHistory = transformationHistoryWatch.map { item ->
TransformationHistory(
monId = characterId,
toCharIndex = item.toCharIndex.toInt(),
year = item.year.toInt(),
month = item.month.toInt(),
day = item.day.toInt()
)
}
storageRepository
.userCharacterDao()
.insertTransformationHistory(*domainTransformationHistory.toTypedArray())
} else {
return "Not implemented yet"
} }
storageRepository return "Done reading character!"
.temporaryMonsterDao()
.insertTransformationHistory(*domainTransformationHistory.toTypedArray())
} }
} }

View File

@ -1,5 +1,6 @@
package com.github.nacabaro.vbhelper.components package com.github.nacabaro.vbhelper.components
import android.graphics.Bitmap
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
@ -7,17 +8,27 @@ import androidx.compose.foundation.layout.size
import androidx.compose.material3.Card import androidx.compose.material3.Card
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.FilterQuality import androidx.compose.ui.graphics.FilterQuality
import androidx.compose.ui.graphics.ImageBitmap import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.github.nacabaro.vbhelper.utils.BitmapData
import java.nio.ByteBuffer
@Composable @Composable
fun CharacterEntry( fun CharacterEntry(
icon: ImageBitmap, icon: BitmapData,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
onClick: () -> Unit = { } onClick: () -> Unit = { }
) { ) {
val bitmap = remember (icon.bitmap) {
Bitmap.createBitmap(icon.width, icon.height, Bitmap.Config.RGB_565).apply {
copyPixelsFromBuffer(ByteBuffer.wrap(icon.bitmap))
}
}
val imageBitmap = remember(bitmap) { bitmap.asImageBitmap() }
Card( Card(
shape = MaterialTheme.shapes.medium, shape = MaterialTheme.shapes.medium,
onClick = onClick, onClick = onClick,
@ -26,7 +37,7 @@ fun CharacterEntry(
.size(96.dp) .size(96.dp)
) { ) {
Image( Image(
bitmap = icon, bitmap = imageBitmap,
contentDescription = "Icon", contentDescription = "Icon",
filterQuality = FilterQuality.None, filterQuality = FilterQuality.None,
modifier = Modifier modifier = Modifier

View File

@ -0,0 +1,62 @@
package com.github.nacabaro.vbhelper.components
import android.graphics.Bitmap
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.Card
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.FilterQuality
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.unit.dp
import com.github.nacabaro.vbhelper.utils.BitmapData
import java.nio.ByteBuffer
@Composable
fun DexDiMEntry(
name: String,
logo: BitmapData,
onClick: () -> Unit,
modifier: Modifier = Modifier
) {
val bitmap = remember (logo.bitmap) {
Bitmap.createBitmap(logo.width, logo.height, Bitmap.Config.RGB_565).apply {
copyPixelsFromBuffer(ByteBuffer.wrap(logo.bitmap))
}
}
val imageBitmap = remember(bitmap) { bitmap.asImageBitmap() }
Card (
shape = MaterialTheme.shapes.medium,
modifier = modifier,
onClick = onClick
) {
Row (
horizontalArrangement = Arrangement.Start,
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.padding(8.dp)
) {
Image (
bitmap = imageBitmap,
contentDescription = name,
filterQuality = FilterQuality.None,
modifier = Modifier
.padding(8.dp)
.size(64.dp)
)
Text(
text = name,
modifier = Modifier
.padding(8.dp)
)
}
}
}

View File

@ -1,64 +0,0 @@
package com.github.nacabaro.vbhelper.components
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Column
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.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.graphics.ImageBitmap
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
@Composable
fun StorageEntry(
name: String,
nameBitmap: ImageBitmap? = null,
modifier: Modifier = Modifier,
icon: Int? = null,
bitmap: ImageBitmap? = null,
onClick: () -> Unit = { }
) {
Card(
shape = MaterialTheme.shapes.medium,
onClick = onClick,
modifier = modifier
.padding(8.dp)
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
.fillMaxSize()
) {
if (bitmap != null) {
Image(
bitmap = bitmap,
contentDescription = name,
modifier = Modifier
.padding(8.dp)
.size(64.dp)
)
} else if (icon != null) {
Image(
painter = painterResource(id = icon),
contentDescription = name,
modifier = Modifier
.padding(8.dp)
.size(64.dp)
)
}
Text(
text = name,
textAlign = TextAlign.Center,
modifier = Modifier
.padding(8.dp)
)
}
}
}

View File

@ -5,7 +5,6 @@ import androidx.room.Insert
import androidx.room.Query import androidx.room.Query
import com.github.nacabaro.vbhelper.domain.Character import com.github.nacabaro.vbhelper.domain.Character
import com.github.nacabaro.vbhelper.domain.Sprites import com.github.nacabaro.vbhelper.domain.Sprites
import org.w3c.dom.CharacterData
@Dao @Dao
interface CharacterDao { interface CharacterDao {
@ -18,6 +17,9 @@ interface CharacterDao {
@Query("SELECT * FROM Character WHERE dimId = :dimId") @Query("SELECT * FROM Character WHERE dimId = :dimId")
suspend fun getCharacterByDimId(dimId: Int): List<Character> suspend fun getCharacterByDimId(dimId: Int): List<Character>
@Query("SELECT * FROM Character WHERE monIndex = :monIndex AND dimId = :dimId LIMIT 1")
fun getCharacterByMonIndex(monIndex: Int, dimId: Long): Character
@Insert @Insert
suspend fun insertSprite(vararg sprite: Sprites) suspend fun insertSprite(vararg sprite: Sprites)

View File

@ -9,8 +9,11 @@ import com.github.nacabaro.vbhelper.domain.Dim
@Dao @Dao
interface DiMDao { interface DiMDao {
@Insert(onConflict = OnConflictStrategy.IGNORE) @Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun insertNewDim(dim: Dim) suspend fun insertNewDim(dim: Dim): Long
@Query("SELECT * FROM Dim") @Query("SELECT * FROM Dim")
suspend fun getAllDims(): List<Dim> suspend fun getAllDims(): List<Dim>
@Query("SELECT * FROM Dim WHERE dimId = :id")
fun getDimById(id: Int): Dim?
} }

View File

@ -0,0 +1,39 @@
package com.github.nacabaro.vbhelper.daos
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import com.github.nacabaro.vbhelper.domain.device_data.UserCharacter
import com.github.nacabaro.vbhelper.domain.device_data.BECharacterData
import com.github.nacabaro.vbhelper.domain.device_data.TransformationHistory
import com.github.nacabaro.vbhelper.dtos.CharacterDtos
@Dao
interface UserCharacterDao {
@Insert
fun insertCharacterData(characterData: UserCharacter): Long
@Insert
fun insertBECharacterData(characterData: BECharacterData)
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertTransformationHistory(vararg transformationHistory: TransformationHistory)
@Query("SELECT * FROM TransformationHistory WHERE monId = :monId")
fun getTransformationHistory(monId: Int): List<TransformationHistory>
@Query("""
SELECT
uc.*,
c.sprite1 AS spriteIdle,
c.spritesWidth AS spriteWidth,
c.spritesHeight AS spriteHeight
FROM UserCharacter uc
JOIN Character c ON uc.charId = c.id
""")
suspend fun getAllCharacters(): List<CharacterDtos.CharacterWithSprites>
@Query("SELECT * FROM UserCharacter WHERE id = :id")
suspend fun getCharacter(id: Long): UserCharacter
}

View File

@ -1,15 +0,0 @@
package com.github.nacabaro.vbhelper.daos
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.Query
import com.github.nacabaro.vbhelper.domain.UserMonsters
@Dao
interface UserMonstersDao {
@Insert
fun insertUserMonsters(userMonsters: UserMonsters)
@Query("SELECT * FROM UserMonsters WHERE userId = :userId")
fun getUserMonsters(userId: Int): List<UserMonsters>
}

View File

@ -4,10 +4,13 @@ import androidx.room.Database
import androidx.room.RoomDatabase import androidx.room.RoomDatabase
import com.github.nacabaro.vbhelper.daos.CharacterDao import com.github.nacabaro.vbhelper.daos.CharacterDao
import com.github.nacabaro.vbhelper.daos.DiMDao import com.github.nacabaro.vbhelper.daos.DiMDao
import com.github.nacabaro.vbhelper.daos.UserCharacterDao
import com.github.nacabaro.vbhelper.domain.Character import com.github.nacabaro.vbhelper.domain.Character
import com.github.nacabaro.vbhelper.domain.Dim import com.github.nacabaro.vbhelper.domain.Dim
import com.github.nacabaro.vbhelper.domain.Sprites import com.github.nacabaro.vbhelper.domain.Sprites
import com.github.nacabaro.vbhelper.temporary_daos.TemporaryMonsterDao import com.github.nacabaro.vbhelper.domain.device_data.BECharacterData
import com.github.nacabaro.vbhelper.domain.device_data.TransformationHistory
import com.github.nacabaro.vbhelper.domain.device_data.UserCharacter
import com.github.nacabaro.vbhelper.temporary_domain.TemporaryBECharacterData import com.github.nacabaro.vbhelper.temporary_domain.TemporaryBECharacterData
import com.github.nacabaro.vbhelper.temporary_domain.TemporaryCharacterData import com.github.nacabaro.vbhelper.temporary_domain.TemporaryCharacterData
import com.github.nacabaro.vbhelper.temporary_domain.TemporaryTransformationHistory import com.github.nacabaro.vbhelper.temporary_domain.TemporaryTransformationHistory
@ -15,16 +18,16 @@ import com.github.nacabaro.vbhelper.temporary_domain.TemporaryTransformationHist
@Database( @Database(
version = 1, version = 1,
entities = [ entities = [
TemporaryCharacterData::class,
TemporaryBECharacterData::class,
TemporaryTransformationHistory::class,
Dim::class, Dim::class,
Character::class, Character::class,
Sprites::class Sprites::class,
UserCharacter::class,
BECharacterData::class,
TransformationHistory::class
] ]
) )
abstract class AppDatabase : RoomDatabase() { abstract class AppDatabase : RoomDatabase() {
abstract fun temporaryMonsterDao(): TemporaryMonsterDao
abstract fun dimDao(): DiMDao abstract fun dimDao(): DiMDao
abstract fun characterDao(): CharacterDao abstract fun characterDao(): CharacterDao
abstract fun userCharacterDao(): UserCharacterDao
} }

View File

@ -15,8 +15,8 @@ import androidx.room.ForeignKey
] ]
) )
data class Character ( data class Character (
@PrimaryKey(autoGenerate = true) val id: Long, @PrimaryKey(autoGenerate = true) val id: Long = 0,
val dimId: Int, val dimId: Long,
val monIndex: Int, val monIndex: Int,
val name: ByteArray, val name: ByteArray,
val stage: Int, // These should be replaced with enums val stage: Int, // These should be replaced with enums
@ -30,4 +30,4 @@ data class Character (
val nameHeight: Int, val nameHeight: Int,
val spritesWidth: Int, val spritesWidth: Int,
val spritesHeight: Int val spritesHeight: Int
) )

View File

@ -0,0 +1,6 @@
package com.github.nacabaro.vbhelper.domain
enum class DeviceType {
VBDevice,
BEDevice
}

View File

@ -7,7 +7,8 @@ import androidx.room.PrimaryKey
@Entity @Entity
data class Dim( data class Dim(
@PrimaryKey(autoGenerate = true) @PrimaryKey(autoGenerate = true)
val id: Int, val id: Long = 0,
val dimId: Int,
val logo: ByteArray, val logo: ByteArray,
val logoWidth: Int, val logoWidth: Int,
val logoHeight: Int, val logoHeight: Int,

View File

@ -0,0 +1,43 @@
package com.github.nacabaro.vbhelper.domain.device_data
import androidx.room.Entity
import androidx.room.ForeignKey
import androidx.room.PrimaryKey
import com.github.cfogrady.vbnfc.data.NfcCharacter
import com.github.nacabaro.vbhelper.temporary_domain.TemporaryCharacterData
@Entity(
foreignKeys = [
ForeignKey(
entity = UserCharacter::class,
parentColumns = ["id"],
childColumns = ["id"],
onDelete = ForeignKey.CASCADE
)
]
)
data class BECharacterData (
@PrimaryKey(autoGenerate = true) val id: Long,
val trainingHp: Int,
val trainingAp: Int,
val trainingBp: Int,
val remainingTrainingTimeInMinutes: Int,
val itemEffectMentalStateValue: Int,
val itemEffectMentalStateMinutesRemaining: Int,
val itemEffectActivityLevelValue: Int,
val itemEffectActivityLevelMinutesRemaining: Int,
val itemEffectVitalPointsChangeValue: Int,
val itemEffectVitalPointsChangeMinutesRemaining: Int,
val abilityRarity: NfcCharacter.AbilityRarity,
val abilityType: Int,
val abilityBranch: Int,
val abilityReset: Int,
val rank: Int,
val itemType: Int,
val itemMultiplier: Int,
val itemRemainingTime: Int,
val otp0: String,
val otp1: String,
val minorVersion: Int,
val majorVersion: Int,
)

View File

@ -0,0 +1,24 @@
package com.github.nacabaro.vbhelper.domain.device_data
import androidx.room.Entity
import androidx.room.ForeignKey
import androidx.room.PrimaryKey
@Entity(
foreignKeys = [
ForeignKey(
entity = UserCharacter::class,
parentColumns = ["id"],
childColumns = ["monId"],
onDelete = ForeignKey.CASCADE
)
]
)
data class TransformationHistory (
@PrimaryKey(autoGenerate = true) val id: Long = 0,
val monId: Long,
val toCharIndex: Int,
val year: Int,
val month: Int,
val day: Int
)

View File

@ -0,0 +1,4 @@
package com.github.nacabaro.vbhelper.domain.device_data
class VBCharacterData {
}

View File

@ -0,0 +1,32 @@
package com.github.nacabaro.vbhelper.dtos
import androidx.room.PrimaryKey
import com.github.cfogrady.vbnfc.data.NfcCharacter
import com.github.nacabaro.vbhelper.domain.DeviceType
object CharacterDtos {
data class CharacterWithSprites(
var id: Long = 0,
var charId: Long,
var stage: Int,
var attribute: NfcCharacter.Attribute,
var ageInDays: Int,
var nextAdventureMissionStage: Int, // next adventure mission stage on the character's dim
var mood: Int,
var vitalPoints: Int,
var transformationCountdown: Int,
var injuryStatus: NfcCharacter.InjuryStatus,
var trophies: Int,
var currentPhaseBattlesWon: Int,
var currentPhaseBattlesLost: Int,
var totalBattlesWon: Int,
var totalBattlesLost: Int,
var activityLevel: Int,
var heartRateCurrent: Int,
var characterType: DeviceType,
val spriteIdle: ByteArray,
val spriteWidth: Int,
val spriteHeight: Int
)
}

View File

@ -1,4 +0,0 @@
package com.github.nacabaro.vbhelper.dtos
class MonsterDataCombined {
}

View File

@ -1,38 +1,27 @@
package com.github.nacabaro.vbhelper.screens package com.github.nacabaro.vbhelper.screens
import android.graphics.Bitmap
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.material3.Card
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.FilterQuality
import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.navigation.NavController import androidx.navigation.NavController
import com.github.nacabaro.vbhelper.utils.BitmapData
import com.github.nacabaro.vbhelper.components.DexDiMEntry
import com.github.nacabaro.vbhelper.components.TopBanner import com.github.nacabaro.vbhelper.components.TopBanner
import com.github.nacabaro.vbhelper.di.VBHelper import com.github.nacabaro.vbhelper.di.VBHelper
import com.github.nacabaro.vbhelper.domain.Dim import com.github.nacabaro.vbhelper.domain.Dim
import com.github.nacabaro.vbhelper.navigation.BottomNavItem import com.github.nacabaro.vbhelper.navigation.BottomNavItem
import com.github.nacabaro.vbhelper.source.DexRepository import com.github.nacabaro.vbhelper.source.DexRepository
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.nio.ByteBuffer
@Composable @Composable
fun DexScreen( fun DexScreen(
@ -66,16 +55,13 @@ fun DexScreen(
.padding(top = contentPadding.calculateTopPadding()) .padding(top = contentPadding.calculateTopPadding())
) { ) {
items(dimList.value) { items(dimList.value) {
val bitmap = remember (it.logo) {
Bitmap.createBitmap(it.logoWidth, it.logoHeight, Bitmap.Config.RGB_565).apply {
copyPixelsFromBuffer(ByteBuffer.wrap(it.logo))
}
}
val imageBitmap = remember(bitmap) { bitmap.asImageBitmap() }
DexDiMEntry( DexDiMEntry(
name = it.name, name = it.name,
logo = imageBitmap, logo = BitmapData(
bitmap = it.logo,
width = it.logoWidth,
height = it.logoHeight
),
onClick = { onClick = {
navController navController
.navigate( .navigate(
@ -93,37 +79,3 @@ fun DexScreen(
} }
} }
@Composable
fun DexDiMEntry(
name: String,
logo: ImageBitmap,
onClick: () -> Unit,
modifier: Modifier = Modifier
) {
Card (
shape = MaterialTheme.shapes.medium,
modifier = modifier,
onClick = onClick
) {
Row (
horizontalArrangement = Arrangement.Start,
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.padding(8.dp)
) {
Image (
bitmap = logo,
contentDescription = name,
filterQuality = FilterQuality.None,
modifier = Modifier
.padding(8.dp)
.size(64.dp)
)
Text(
text = name,
modifier = Modifier
.padding(8.dp)
)
}
}
}

View File

@ -1,6 +1,5 @@
package com.github.nacabaro.vbhelper.screens package com.github.nacabaro.vbhelper.screens
import android.graphics.Bitmap
import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.items import androidx.compose.foundation.lazy.grid.items
@ -10,16 +9,15 @@ import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.navigation.NavController import androidx.navigation.NavController
import com.github.nacabaro.vbhelper.utils.BitmapData
import com.github.nacabaro.vbhelper.components.CharacterEntry import com.github.nacabaro.vbhelper.components.CharacterEntry
import com.github.nacabaro.vbhelper.components.TopBanner import com.github.nacabaro.vbhelper.components.TopBanner
import com.github.nacabaro.vbhelper.domain.Character import com.github.nacabaro.vbhelper.domain.Character
import com.github.nacabaro.vbhelper.di.VBHelper import com.github.nacabaro.vbhelper.di.VBHelper
import com.github.nacabaro.vbhelper.source.DexRepository import com.github.nacabaro.vbhelper.source.DexRepository
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.nio.ByteBuffer
@Composable @Composable
fun DiMScreen( fun DiMScreen(
@ -54,15 +52,13 @@ fun DiMScreen(
contentPadding = contentPadding contentPadding = contentPadding
) { ) {
items(characterList.value) { character -> items(characterList.value) { character ->
val bitmapCharacter = remember (character.sprite1) {
Bitmap.createBitmap(character.spritesWidth, character.spritesHeight, Bitmap.Config.RGB_565).apply {
copyPixelsFromBuffer(ByteBuffer.wrap(character.sprite1))
}
}
val imageBitmapCharacter = remember(bitmapCharacter) { bitmapCharacter.asImageBitmap() }
CharacterEntry( CharacterEntry(
icon = imageBitmapCharacter, onClick = { },
onClick = { } icon = BitmapData(
bitmap = character.sprite1,
width = character.spritesWidth,
height = character.spritesHeight
),
) )
} }
} }

View File

@ -16,8 +16,6 @@ import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.Card import androidx.compose.material3.Card
import androidx.compose.material3.CardElevation
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@ -32,18 +30,18 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties import androidx.compose.ui.window.DialogProperties
import com.github.nacabaro.vbhelper.R import com.github.nacabaro.vbhelper.components.CharacterEntry
import com.github.nacabaro.vbhelper.components.StorageEntry
import com.github.nacabaro.vbhelper.components.TopBanner import com.github.nacabaro.vbhelper.components.TopBanner
import com.github.nacabaro.vbhelper.di.VBHelper import com.github.nacabaro.vbhelper.di.VBHelper
import com.github.nacabaro.vbhelper.domain.device_data.UserCharacter
import com.github.nacabaro.vbhelper.dtos.CharacterDtos
import com.github.nacabaro.vbhelper.source.StorageRepository import com.github.nacabaro.vbhelper.source.StorageRepository
import com.github.nacabaro.vbhelper.temporary_domain.TemporaryCharacterData import com.github.nacabaro.vbhelper.temporary_domain.TemporaryCharacterData
import kotlinx.coroutines.coroutineScope import com.github.nacabaro.vbhelper.utils.BitmapData
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -52,15 +50,14 @@ fun StorageScreen() {
val coroutineScope = rememberCoroutineScope() val coroutineScope = rememberCoroutineScope()
val application = LocalContext.current.applicationContext as VBHelper val application = LocalContext.current.applicationContext as VBHelper
val storageRepository = StorageRepository(application.container.db) val storageRepository = StorageRepository(application.container.db)
val monList = remember { mutableStateListOf<TemporaryCharacterData>() } val monList = remember { mutableStateOf<List<CharacterDtos.CharacterWithSprites>>(emptyList()) }
var selectedCharacter by remember { mutableStateOf<Long?>(null) } var selectedCharacter by remember { mutableStateOf<Long?>(null) }
LaunchedEffect(storageRepository) { LaunchedEffect(storageRepository) {
coroutineScope.launch { coroutineScope.launch {
monList.clear() val characterList = storageRepository.getAllCharacters()
monList.addAll(storageRepository.getAllCharacters()) monList.value = characterList
Log.d("StorageScreen", "Updated data: $monList")
} }
} }
@ -69,7 +66,7 @@ fun StorageScreen() {
Scaffold ( Scaffold (
topBar = { TopBanner(text = "My Digimon") } topBar = { TopBanner(text = "My Digimon") }
) { contentPadding -> ) { contentPadding ->
if (monList.isEmpty()) { if (monList.value.isEmpty()) {
Column ( Column (
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center, verticalArrangement = Arrangement.Center,
@ -91,15 +88,17 @@ fun StorageScreen() {
.scrollable(state = rememberScrollState(), orientation = Orientation.Vertical) .scrollable(state = rememberScrollState(), orientation = Orientation.Vertical)
.padding(top = contentPadding.calculateTopPadding()) .padding(top = contentPadding.calculateTopPadding())
) { ) {
items(monList) { index -> items(monList.value) { index ->
var showDialog by rememberSaveable { mutableStateOf(false) } CharacterEntry(
icon = BitmapData(
StorageEntry( bitmap = index.spriteIdle,
name = index.dimId.toString() + " - " + index.charIndex.toString(), width = index.spriteWidth,
icon = R.drawable.ic_launcher_foreground, height = index.spriteHeight
onClick = { selectedCharacter = index.id }, ),
modifier = Modifier modifier = Modifier
.padding(8.dp) .padding(8.dp)
.size(96.dp)
) )
if (selectedCharacter != null) { if (selectedCharacter != null) {
@ -121,7 +120,7 @@ fun StorageDialog(
val coroutineScope = rememberCoroutineScope() val coroutineScope = rememberCoroutineScope()
val application = LocalContext.current.applicationContext as VBHelper val application = LocalContext.current.applicationContext as VBHelper
val storageRepository = StorageRepository(application.container.db) val storageRepository = StorageRepository(application.container.db)
val character = remember { mutableStateOf<TemporaryCharacterData?>(null) } val character = remember { mutableStateOf<UserCharacter?>(null) }
LaunchedEffect(storageRepository) { LaunchedEffect(storageRepository) {
coroutineScope.launch { coroutineScope.launch {

View File

@ -1,16 +1,19 @@
package com.github.nacabaro.vbhelper.source package com.github.nacabaro.vbhelper.source
import com.github.nacabaro.vbhelper.daos.CharacterDao
import com.github.nacabaro.vbhelper.database.AppDatabase import com.github.nacabaro.vbhelper.database.AppDatabase
import com.github.nacabaro.vbhelper.domain.device_data.UserCharacter
import com.github.nacabaro.vbhelper.dtos.CharacterDtos
import com.github.nacabaro.vbhelper.temporary_domain.TemporaryCharacterData import com.github.nacabaro.vbhelper.temporary_domain.TemporaryCharacterData
class StorageRepository ( class StorageRepository (
private val db: AppDatabase private val db: AppDatabase
) { ) {
suspend fun getAllCharacters(): List<TemporaryCharacterData> { suspend fun getAllCharacters(): List<CharacterDtos.CharacterWithSprites> {
return db.temporaryMonsterDao().getAllCharacters() return db.userCharacterDao().getAllCharacters()
} }
suspend fun getSingleCharacter(id: Long): TemporaryCharacterData { suspend fun getSingleCharacter(id: Long): UserCharacter {
return db.temporaryMonsterDao().getCharacter(id) return db.userCharacterDao().getCharacter(id)
} }
} }

View File

@ -0,0 +1,8 @@
package com.github.nacabaro.vbhelper.utils
// simple, but smooth
data class BitmapData (
val bitmap: ByteArray,
val width: Int,
val height: Int
)

View File

@ -1,2 +0,0 @@
package com.github.nacabaro.vbhelper.vm