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.Sprites
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.TemporaryCharacterData
import com.github.nacabaro.vbhelper.temporary_domain.TemporaryTransformationHistory
import com.github.nacabaro.vbhelper.ui.theme.VBHelperTheme
import kotlinx.coroutines.flow.MutableStateFlow
@ -72,6 +74,9 @@ class MainActivity : ComponentActivity() {
ActivityResultContracts.StartActivityForResult()
) {
lifecycleScope.launch {
val application = applicationContext as VBHelper
val storageRepository = application.container.db
if (it.resultCode != RESULT_OK) {
Toast.makeText(applicationContext, "Import operation cancelled.", Toast.LENGTH_SHORT).show()
}
@ -81,44 +86,27 @@ class MainActivity : ComponentActivity() {
val dimReader = DimReader()
val card = dimReader.readCard(fileReader, false)
val dimModel = Dim(
id = card.header.dimId,
dimId = card.header.dimId,
logo = card.spriteData.sprites[0].pixelData,
name = card.spriteData.text, // TODO Make user write card name
stageCount = card.adventureLevels.levels.size,
logoHeight = card.spriteData.sprites[0].height,
logoWidth = card.spriteData.sprites[0].width
)
val application = applicationContext as VBHelper
val storageRepository = application.container.db
storageRepository.dimDao().insertNewDim(dimModel)
val dimId = storageRepository
.dimDao()
.insertNewDim(dimModel)
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 domainCharacters = mutableListOf<Character>()
val domainCharacters = mutableListOf<Character>()
for (index in 0 until characters.size) {
domainCharacters.add(
Character(
id = 0,
dimId = card.header.dimId,
dimId = dimId,
monIndex = index,
name = card.spriteData.sprites[spriteCounter].pixelData,
stage = characters[index].stage,
@ -135,6 +123,7 @@ class MainActivity : ComponentActivity() {
)
)
// TODO: Improve this
if (index == 0) {
spriteCounter += 6
} else if (index == 1) {
@ -176,9 +165,12 @@ class MainActivity : ComponentActivity() {
handleTag {
val character = it.receiveCharacter()
nfcCharacter.value = character
addCharacterScannedIntoDatabase()
val importStatus = addCharacterScannedIntoDatabase()
isDoneReadingCharacter = true
"Done reading character"
importStatus
}
},
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
domain model.
*/
private fun addCharacterScannedIntoDatabase() {
val beCharacter = nfcCharacter as MutableStateFlow<BENfcCharacter?>
private fun addCharacterScannedIntoDatabase(): String {
val application = applicationContext as VBHelper
val storageRepository = application.container.db
val temporaryCharacterData = TemporaryCharacterData(
dimId = nfcCharacter.value!!.dimId.toInt(),
charIndex = nfcCharacter.value!!.charIndex.toInt(),
val dimData = storageRepository
.dimDao()
.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(),
attribute = nfcCharacter.value!!.attribute,
ageInDays = nfcCharacter.value!!.ageInDays.toInt(),
nextAdventureMissionStage = nfcCharacter.value!!.nextAdventureMissionStage.toInt(),
mood = nfcCharacter.value!!.mood.toInt(),
vitalPoints = nfcCharacter.value!!.vitalPoints.toInt(),
transformationCountdown = nfcCharacter.value!!.transformationCountdown.toInt(),
transformationCountdown = nfcCharacter.value!!.transformationCountdownInMinutes.toInt(),
injuryStatus = nfcCharacter.value!!.injuryStatus,
trophies = nfcCharacter.value!!.trophies.toInt(),
currentPhaseBattlesWon = nfcCharacter.value!!.currentPhaseBattlesWon.toInt(),
@ -293,58 +297,67 @@ class MainActivity : ComponentActivity() {
totalBattlesWon = nfcCharacter.value!!.totalBattlesWon.toInt(),
totalBattlesLost = nfcCharacter.value!!.totalBattlesLost.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
.temporaryMonsterDao()
.insertCharacterData(temporaryCharacterData)
.userCharacterDao()
.insertCharacterData(characterData)
val temporaryBECharacterData = TemporaryBECharacterData(
id = characterId,
trainingHp = beCharacter.value!!.trainingHp.toInt(),
trainingAp = beCharacter.value!!.trainingAp.toInt(),
trainingBp = beCharacter.value!!.trainingBp.toInt(),
remainingTrainingTimeInMinutes = beCharacter.value!!.remainingTrainingTimeInMinutes.toInt(),
itemEffectActivityLevelValue = beCharacter.value!!.itemEffectActivityLevelValue.toInt(),
itemEffectMentalStateValue = beCharacter.value!!.itemEffectMentalStateValue.toInt(),
itemEffectMentalStateMinutesRemaining = beCharacter.value!!.itemEffectMentalStateMinutesRemaining.toInt(),
itemEffectActivityLevelMinutesRemaining = beCharacter.value!!.itemEffectActivityLevelMinutesRemaining.toInt(),
itemEffectVitalPointsChangeValue = beCharacter.value!!.itemEffectVitalPointsChangeValue.toInt(),
itemEffectVitalPointsChangeMinutesRemaining = beCharacter.value!!.itemEffectVitalPointsChangeMinutesRemaining.toInt(),
abilityRarity = beCharacter.value!!.abilityRarity,
abilityType = beCharacter.value!!.abilityType.toInt(),
abilityBranch = beCharacter.value!!.abilityBranch.toInt(),
abilityReset = beCharacter.value!!.abilityReset.toInt(),
rank = beCharacter.value!!.abilityReset.toInt(),
itemType = beCharacter.value!!.itemType.toInt(),
itemMultiplier = beCharacter.value!!.itemMultiplier.toInt(),
itemRemainingTime = beCharacter.value!!.itemRemainingTime.toInt(),
otp0 = "", //beCharacter.value!!.otp0.toString(),
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()
if (nfcCharacter.value is BENfcCharacter) {
val beCharacter = nfcCharacter as MutableStateFlow<BENfcCharacter?>
val extraCharacterData = BECharacterData(
id = characterId,
trainingHp = beCharacter.value!!.trainingHp.toInt(),
trainingAp = beCharacter.value!!.trainingAp.toInt(),
trainingBp = beCharacter.value!!.trainingBp.toInt(),
remainingTrainingTimeInMinutes = beCharacter.value!!.remainingTrainingTimeInMinutes.toInt(),
itemEffectActivityLevelValue = beCharacter.value!!.itemEffectActivityLevelValue.toInt(),
itemEffectMentalStateValue = beCharacter.value!!.itemEffectMentalStateValue.toInt(),
itemEffectMentalStateMinutesRemaining = beCharacter.value!!.itemEffectMentalStateMinutesRemaining.toInt(),
itemEffectActivityLevelMinutesRemaining = beCharacter.value!!.itemEffectActivityLevelMinutesRemaining.toInt(),
itemEffectVitalPointsChangeValue = beCharacter.value!!.itemEffectVitalPointsChangeValue.toInt(),
itemEffectVitalPointsChangeMinutesRemaining = beCharacter.value!!.itemEffectVitalPointsChangeMinutesRemaining.toInt(),
abilityRarity = beCharacter.value!!.abilityRarity,
abilityType = beCharacter.value!!.abilityType.toInt(),
abilityBranch = beCharacter.value!!.abilityBranch.toInt(),
abilityReset = beCharacter.value!!.abilityReset.toInt(),
rank = beCharacter.value!!.abilityReset.toInt(),
itemType = beCharacter.value!!.itemType.toInt(),
itemMultiplier = beCharacter.value!!.itemMultiplier.toInt(),
itemRemainingTime = beCharacter.value!!.itemRemainingTime.toInt(),
otp0 = "", //beCharacter.value!!.otp0.toString(),
otp1 = "", //beCharacter.value!!.otp1.toString(),
minorVersion = beCharacter.value!!.characterCreationFirmwareVersion.minorVersion.toInt(),
majorVersion = beCharacter.value!!.characterCreationFirmwareVersion.majorVersion.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
.temporaryMonsterDao()
.insertTransformationHistory(*domainTransformationHistory.toTypedArray())
return "Done reading character!"
}
}

View File

@ -1,5 +1,6 @@
package com.github.nacabaro.vbhelper.components
import android.graphics.Bitmap
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.fillMaxSize
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.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
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.unit.dp
import com.github.nacabaro.vbhelper.utils.BitmapData
import java.nio.ByteBuffer
@Composable
fun CharacterEntry(
icon: ImageBitmap,
icon: BitmapData,
modifier: Modifier = Modifier,
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(
shape = MaterialTheme.shapes.medium,
onClick = onClick,
@ -26,7 +37,7 @@ fun CharacterEntry(
.size(96.dp)
) {
Image(
bitmap = icon,
bitmap = imageBitmap,
contentDescription = "Icon",
filterQuality = FilterQuality.None,
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 com.github.nacabaro.vbhelper.domain.Character
import com.github.nacabaro.vbhelper.domain.Sprites
import org.w3c.dom.CharacterData
@Dao
interface CharacterDao {
@ -18,6 +17,9 @@ interface CharacterDao {
@Query("SELECT * FROM Character WHERE dimId = :dimId")
suspend fun getCharacterByDimId(dimId: Int): List<Character>
@Query("SELECT * FROM Character WHERE monIndex = :monIndex AND dimId = :dimId LIMIT 1")
fun getCharacterByMonIndex(monIndex: Int, dimId: Long): Character
@Insert
suspend fun insertSprite(vararg sprite: Sprites)

View File

@ -9,8 +9,11 @@ import com.github.nacabaro.vbhelper.domain.Dim
@Dao
interface DiMDao {
@Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun insertNewDim(dim: Dim)
suspend fun insertNewDim(dim: Dim): Long
@Query("SELECT * FROM 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 com.github.nacabaro.vbhelper.daos.CharacterDao
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.Dim
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.TemporaryCharacterData
import com.github.nacabaro.vbhelper.temporary_domain.TemporaryTransformationHistory
@ -15,16 +18,16 @@ import com.github.nacabaro.vbhelper.temporary_domain.TemporaryTransformationHist
@Database(
version = 1,
entities = [
TemporaryCharacterData::class,
TemporaryBECharacterData::class,
TemporaryTransformationHistory::class,
Dim::class,
Character::class,
Sprites::class
Sprites::class,
UserCharacter::class,
BECharacterData::class,
TransformationHistory::class
]
)
abstract class AppDatabase : RoomDatabase() {
abstract fun temporaryMonsterDao(): TemporaryMonsterDao
abstract fun dimDao(): DiMDao
abstract fun characterDao(): CharacterDao
abstract fun userCharacterDao(): UserCharacterDao
}

View File

@ -15,8 +15,8 @@ import androidx.room.ForeignKey
]
)
data class Character (
@PrimaryKey(autoGenerate = true) val id: Long,
val dimId: Int,
@PrimaryKey(autoGenerate = true) val id: Long = 0,
val dimId: Long,
val monIndex: Int,
val name: ByteArray,
val stage: Int, // These should be replaced with enums
@ -30,4 +30,4 @@ data class Character (
val nameHeight: Int,
val spritesWidth: 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
data class Dim(
@PrimaryKey(autoGenerate = true)
val id: Int,
val id: Long = 0,
val dimId: Int,
val logo: ByteArray,
val logoWidth: 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
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.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
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.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
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.unit.dp
import androidx.navigation.NavController
import com.github.nacabaro.vbhelper.utils.BitmapData
import com.github.nacabaro.vbhelper.components.DexDiMEntry
import com.github.nacabaro.vbhelper.components.TopBanner
import com.github.nacabaro.vbhelper.di.VBHelper
import com.github.nacabaro.vbhelper.domain.Dim
import com.github.nacabaro.vbhelper.navigation.BottomNavItem
import com.github.nacabaro.vbhelper.source.DexRepository
import kotlinx.coroutines.launch
import java.nio.ByteBuffer
@Composable
fun DexScreen(
@ -66,16 +55,13 @@ fun DexScreen(
.padding(top = contentPadding.calculateTopPadding())
) {
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(
name = it.name,
logo = imageBitmap,
logo = BitmapData(
bitmap = it.logo,
width = it.logoWidth,
height = it.logoHeight
),
onClick = {
navController
.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
import android.graphics.Bitmap
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
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.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.platform.LocalContext
import androidx.navigation.NavController
import com.github.nacabaro.vbhelper.utils.BitmapData
import com.github.nacabaro.vbhelper.components.CharacterEntry
import com.github.nacabaro.vbhelper.components.TopBanner
import com.github.nacabaro.vbhelper.domain.Character
import com.github.nacabaro.vbhelper.di.VBHelper
import com.github.nacabaro.vbhelper.source.DexRepository
import kotlinx.coroutines.launch
import java.nio.ByteBuffer
@Composable
fun DiMScreen(
@ -54,15 +52,13 @@ fun DiMScreen(
contentPadding = contentPadding
) {
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(
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.material3.Button
import androidx.compose.material3.Card
import androidx.compose.material3.CardElevation
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
@ -32,18 +30,18 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import com.github.nacabaro.vbhelper.R
import com.github.nacabaro.vbhelper.components.StorageEntry
import com.github.nacabaro.vbhelper.components.CharacterEntry
import com.github.nacabaro.vbhelper.components.TopBanner
import com.github.nacabaro.vbhelper.di.VBHelper
import com.github.nacabaro.vbhelper.domain.device_data.UserCharacter
import com.github.nacabaro.vbhelper.dtos.CharacterDtos
import com.github.nacabaro.vbhelper.source.StorageRepository
import com.github.nacabaro.vbhelper.temporary_domain.TemporaryCharacterData
import kotlinx.coroutines.coroutineScope
import com.github.nacabaro.vbhelper.utils.BitmapData
import kotlinx.coroutines.launch
@ -52,15 +50,14 @@ fun StorageScreen() {
val coroutineScope = rememberCoroutineScope()
val application = LocalContext.current.applicationContext as VBHelper
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) }
LaunchedEffect(storageRepository) {
coroutineScope.launch {
monList.clear()
monList.addAll(storageRepository.getAllCharacters())
Log.d("StorageScreen", "Updated data: $monList")
val characterList = storageRepository.getAllCharacters()
monList.value = characterList
}
}
@ -69,7 +66,7 @@ fun StorageScreen() {
Scaffold (
topBar = { TopBanner(text = "My Digimon") }
) { contentPadding ->
if (monList.isEmpty()) {
if (monList.value.isEmpty()) {
Column (
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
@ -91,15 +88,17 @@ fun StorageScreen() {
.scrollable(state = rememberScrollState(), orientation = Orientation.Vertical)
.padding(top = contentPadding.calculateTopPadding())
) {
items(monList) { index ->
var showDialog by rememberSaveable { mutableStateOf(false) }
StorageEntry(
name = index.dimId.toString() + " - " + index.charIndex.toString(),
icon = R.drawable.ic_launcher_foreground,
onClick = { selectedCharacter = index.id },
items(monList.value) { index ->
CharacterEntry(
icon = BitmapData(
bitmap = index.spriteIdle,
width = index.spriteWidth,
height = index.spriteHeight
),
modifier = Modifier
.padding(8.dp)
.size(96.dp)
)
if (selectedCharacter != null) {
@ -121,7 +120,7 @@ fun StorageDialog(
val coroutineScope = rememberCoroutineScope()
val application = LocalContext.current.applicationContext as VBHelper
val storageRepository = StorageRepository(application.container.db)
val character = remember { mutableStateOf<TemporaryCharacterData?>(null) }
val character = remember { mutableStateOf<UserCharacter?>(null) }
LaunchedEffect(storageRepository) {
coroutineScope.launch {

View File

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