Merge pull request #7 from nacabaro/dim/read_cards

Read cards
This commit is contained in:
nacabaro 2025-01-05 22:24:31 +01:00 committed by GitHub
commit 5edd753da1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 579 additions and 91 deletions

View File

@ -10,25 +10,31 @@ import android.widget.Toast
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge import androidx.activity.enableEdgeToEdge
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.platform.LocalContext import androidx.lifecycle.lifecycleScope
import com.github.cfogrady.vb.dim.card.DimReader
import com.github.nacabaro.vbhelper.navigation.AppNavigation import com.github.nacabaro.vbhelper.navigation.AppNavigation
import com.github.cfogrady.vbnfc.CryptographicTransformer import com.github.cfogrady.vbnfc.CryptographicTransformer
import com.github.cfogrady.vbnfc.R
import com.github.cfogrady.vbnfc.TagCommunicator import com.github.cfogrady.vbnfc.TagCommunicator
import com.github.cfogrady.vbnfc.be.BENfcCharacter import com.github.cfogrady.vbnfc.be.BENfcCharacter
import com.github.cfogrady.vbnfc.data.DeviceType import com.github.cfogrady.vbnfc.data.DeviceType
import com.github.cfogrady.vbnfc.data.NfcCharacter import com.github.cfogrady.vbnfc.data.NfcCharacter
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.Sprites
import com.github.nacabaro.vbhelper.domain.Character
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
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
import kotlinx.coroutines.launch
class MainActivity : ComponentActivity() { class MainActivity : ComponentActivity() {
private lateinit var nfcAdapter: NfcAdapter private lateinit var nfcAdapter: NfcAdapter
@ -36,10 +42,14 @@ class MainActivity : ComponentActivity() {
private var nfcCharacter = MutableStateFlow<NfcCharacter?>(null) private var nfcCharacter = MutableStateFlow<NfcCharacter?>(null)
private lateinit var activityResultLauncher: ActivityResultLauncher<Intent>
// EXTRACTED DIRECTLY FROM EXAMPLE APP // EXTRACTED DIRECTLY FROM EXAMPLE APP
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
deviceToCryptographicTransformers = getMapOfCryptographicTransformers() deviceToCryptographicTransformers = getMapOfCryptographicTransformers()
registerFileActivityResult()
val maybeNfcAdapter = NfcAdapter.getDefaultAdapter(this) val maybeNfcAdapter = NfcAdapter.getDefaultAdapter(this)
if (maybeNfcAdapter == null) { if (maybeNfcAdapter == null) {
Toast.makeText(this, "No NFC on device!", Toast.LENGTH_SHORT).show() Toast.makeText(this, "No NFC on device!", Toast.LENGTH_SHORT).show()
@ -57,11 +67,109 @@ class MainActivity : ComponentActivity() {
} }
} }
private fun registerFileActivityResult() {
activityResultLauncher = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) {
lifecycleScope.launch {
if (it.resultCode != RESULT_OK) {
Toast.makeText(applicationContext, "Import operation cancelled.", Toast.LENGTH_SHORT).show()
}
val contentResolver = applicationContext.contentResolver
val inputStream = contentResolver.openInputStream(it.data!!.data!!)
inputStream.use { fileReader ->
val dimReader = DimReader()
val card = dimReader.readCard(fileReader, false)
val dimModel = Dim(
id = 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 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>()
for (index in 0 until characters.size) {
domainCharacters.add(
Character(
id = 0,
dimId = card.header.dimId,
monIndex = index,
name = card.spriteData.sprites[spriteCounter].pixelData,
stage = characters[index].stage,
attribute = characters[index].attribute,
baseHp = characters[index].hp,
baseBp = characters[index].dp,
baseAp = characters[index].ap,
sprite1 = card.spriteData.sprites[spriteCounter + 1].pixelData,
sprite2 = card.spriteData.sprites[spriteCounter + 2].pixelData,
nameWidth = card.spriteData.sprites[spriteCounter].width,
nameHeight = card.spriteData.sprites[spriteCounter].height,
spritesWidth = card.spriteData.sprites[spriteCounter + 1].width,
spritesHeight = card.spriteData.sprites[spriteCounter + 1].height
)
)
if (index == 0) {
spriteCounter += 6
} else if (index == 1) {
spriteCounter += 7
} else {
spriteCounter += 14
}
}
storageRepository
.characterDao()
.insertCharacter(*domainCharacters.toTypedArray())
val sprites = card.spriteData.sprites.map { sprite ->
Sprites(
id = 0,
sprite = sprite.pixelData,
width = sprite.width,
height = sprite.height
)
}
storageRepository
.characterDao()
.insertSprite(*sprites.toTypedArray())
}
inputStream?.close()
Toast.makeText(applicationContext, "Import successful!", Toast.LENGTH_SHORT).show()
}
}
}
@Composable @Composable
private fun MainApplication() { private fun MainApplication() {
var isDoneReadingCharacter by remember { mutableStateOf(false) } var isDoneReadingCharacter by remember { mutableStateOf(false) }
val application = LocalContext.current.applicationContext as VBHelper
val storageRepository = application.container.db
AppNavigation( AppNavigation(
isDoneReadingCharacter = isDoneReadingCharacter, isDoneReadingCharacter = isDoneReadingCharacter,
onClickRead = { onClickRead = {
@ -75,6 +183,13 @@ class MainActivity : ComponentActivity() {
}, },
onClickScan = { onClickScan = {
isDoneReadingCharacter = false isDoneReadingCharacter = false
},
onClickImportCard = {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = "*/*"
}
activityResultLauncher.launch(intent)
} }
) )
} }
@ -160,6 +275,7 @@ class MainActivity : ComponentActivity() {
*/ */
private fun addCharacterScannedIntoDatabase() { private fun addCharacterScannedIntoDatabase() {
val beCharacter = nfcCharacter as MutableStateFlow<BENfcCharacter?> val beCharacter = nfcCharacter as MutableStateFlow<BENfcCharacter?>
val temporaryCharacterData = TemporaryCharacterData( val temporaryCharacterData = TemporaryCharacterData(
dimId = nfcCharacter.value!!.dimId.toInt(), dimId = nfcCharacter.value!!.dimId.toInt(),
charIndex = nfcCharacter.value!!.charIndex.toInt(), charIndex = nfcCharacter.value!!.charIndex.toInt(),

View File

@ -0,0 +1,37 @@
package com.github.nacabaro.vbhelper.components
import androidx.compose.foundation.Image
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.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.FilterQuality
import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.unit.dp
@Composable
fun CharacterEntry(
icon: ImageBitmap,
modifier: Modifier = Modifier,
onClick: () -> Unit = { }
) {
Card(
shape = MaterialTheme.shapes.medium,
onClick = onClick,
modifier = modifier
.padding(8.dp)
.size(96.dp)
) {
Image(
bitmap = icon,
contentDescription = "Icon",
filterQuality = FilterQuality.None,
modifier = Modifier
.padding(8.dp)
.fillMaxSize()
)
}
}

View File

@ -0,0 +1,64 @@
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

@ -1,10 +1,12 @@
package com.github.nacabaro.vbhelper.components package com.github.nacabaro.vbhelper.components
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
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.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
@ -20,10 +22,11 @@ fun TopBanner(
text: String, text: String,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
onGearClick: (() -> Unit)? = null, onGearClick: (() -> Unit)? = null,
onBackClick: (() -> Unit)? = null onBackClick: (() -> Unit)? = null,
) { ) {
Box( // Use Box to overlay elements Box( // Use Box to overlay elements
modifier = modifier modifier = modifier
.background(MaterialTheme.colorScheme.background)
.fillMaxWidth() .fillMaxWidth()
.padding(16.dp) .padding(16.dp)
) { ) {

View File

@ -0,0 +1,26 @@
package com.github.nacabaro.vbhelper.daos
import androidx.room.Dao
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 {
@Insert
suspend fun insertCharacter(vararg characterData: Character)
@Query("SELECT * FROM Character")
suspend fun getAllCharacters(): List<Character>
@Query("SELECT * FROM Character WHERE dimId = :dimId")
suspend fun getCharacterByDimId(dimId: Int): List<Character>
@Insert
suspend fun insertSprite(vararg sprite: Sprites)
@Query("SELECT * FROM Sprites")
suspend fun getAllSprites(): List<Sprites>
}

View File

@ -2,15 +2,15 @@ package com.github.nacabaro.vbhelper.daos
import androidx.room.Dao import androidx.room.Dao
import androidx.room.Insert import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query import androidx.room.Query
import com.github.nacabaro.vbhelper.domain.Dim import com.github.nacabaro.vbhelper.domain.Dim
import kotlinx.coroutines.flow.Flow
@Dao @Dao
interface DiMDao { interface DiMDao {
@Insert @Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun insertNewDim(dim: Dim) suspend fun insertNewDim(dim: Dim)
@Query("SELECT * FROM Dim") @Query("SELECT * FROM Dim")
fun getAllDims(): Flow<List<Dim>> suspend fun getAllDims(): List<Dim>
} }

View File

@ -2,6 +2,11 @@ package com.github.nacabaro.vbhelper.database
import androidx.room.Database import androidx.room.Database
import androidx.room.RoomDatabase import androidx.room.RoomDatabase
import com.github.nacabaro.vbhelper.daos.CharacterDao
import com.github.nacabaro.vbhelper.daos.DiMDao
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.temporary_daos.TemporaryMonsterDao
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
@ -12,10 +17,14 @@ import com.github.nacabaro.vbhelper.temporary_domain.TemporaryTransformationHist
entities = [ entities = [
TemporaryCharacterData::class, TemporaryCharacterData::class,
TemporaryBECharacterData::class, TemporaryBECharacterData::class,
TemporaryTransformationHistory::class TemporaryTransformationHistory::class,
Dim::class,
Character::class,
Sprites::class
] ]
) )
abstract class AppDatabase : RoomDatabase() { abstract class AppDatabase : RoomDatabase() {
abstract fun temporaryMonsterDao(): TemporaryMonsterDao abstract fun temporaryMonsterDao(): TemporaryMonsterDao
abstract fun dimDao(): DiMDao
abstract fun characterDao(): CharacterDao
} }

View File

@ -14,15 +14,20 @@ import androidx.room.ForeignKey
) )
] ]
) )
data class Mon ( data class Character (
@PrimaryKey val id: Int, @PrimaryKey(autoGenerate = true) val id: Long,
val dimId: Int, val dimId: Int,
val monIndex: Int, val monIndex: Int,
val name: String, val name: ByteArray,
val stage: Int, // These should be replaced with enums val stage: Int, // These should be replaced with enums
val attribute: Int, // This one too val attribute: Int, // This one too
val baseHp: Int, val baseHp: Int,
val baseBp: Int, val baseBp: Int,
val baseAp: Int, val baseAp: Int,
val evoTime: Int, // In minutes val sprite1: ByteArray,
val sprite2: ByteArray,
val nameWidth: Int,
val nameHeight: Int,
val spritesWidth: Int,
val spritesHeight: Int
) )

View File

@ -1,5 +1,6 @@
package com.github.nacabaro.vbhelper.domain package com.github.nacabaro.vbhelper.domain
import android.icu.text.ListFormatter.Width
import androidx.room.Entity import androidx.room.Entity
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
@ -7,6 +8,9 @@ import androidx.room.PrimaryKey
data class Dim( data class Dim(
@PrimaryKey(autoGenerate = true) @PrimaryKey(autoGenerate = true)
val id: Int, val id: Int,
val logo: ByteArray,
val logoWidth: Int,
val logoHeight: Int,
val name: String, val name: String,
val stageCount: Int val stageCount: Int
) )

View File

@ -7,13 +7,13 @@ import androidx.room.PrimaryKey
@Entity( @Entity(
foreignKeys = [ foreignKeys = [
ForeignKey( ForeignKey(
entity = Mon::class, entity = Character::class,
parentColumns = ["id"], parentColumns = ["id"],
childColumns = ["monId"], childColumns = ["monId"],
onDelete = ForeignKey.CASCADE onDelete = ForeignKey.CASCADE
), ),
ForeignKey( ForeignKey(
entity = Mon::class, entity = Character::class,
parentColumns = ["id"], parentColumns = ["id"],
childColumns = ["nextMon"], childColumns = ["nextMon"],
onDelete = ForeignKey.CASCADE onDelete = ForeignKey.CASCADE

View File

@ -0,0 +1,12 @@
package com.github.nacabaro.vbhelper.domain
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity
data class Sprites(
@PrimaryKey(autoGenerate = true) val id : Int,
val sprite: ByteArray,
val width: Int,
val height: Int
)

View File

@ -13,7 +13,7 @@ import androidx.room.PrimaryKey
onDelete = ForeignKey.CASCADE onDelete = ForeignKey.CASCADE
), ),
ForeignKey( ForeignKey(
entity = Mon::class, entity = Character::class,
parentColumns = ["id"], parentColumns = ["id"],
childColumns = ["monId"], childColumns = ["monId"],
onDelete = ForeignKey.CASCADE onDelete = ForeignKey.CASCADE

View File

@ -1,5 +1,6 @@
package com.github.nacabaro.vbhelper.navigation package com.github.nacabaro.vbhelper.navigation
import android.util.Log
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@ -9,15 +10,18 @@ import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController import androidx.navigation.compose.rememberNavController
import com.github.nacabaro.vbhelper.screens.BattlesScreen import com.github.nacabaro.vbhelper.screens.BattlesScreen
import com.github.nacabaro.vbhelper.screens.DexScreen import com.github.nacabaro.vbhelper.screens.DexScreen
import com.github.nacabaro.vbhelper.screens.DiMScreen
import com.github.nacabaro.vbhelper.screens.HomeScreen import com.github.nacabaro.vbhelper.screens.HomeScreen
import com.github.nacabaro.vbhelper.screens.ScanScreen import com.github.nacabaro.vbhelper.screens.ScanScreen
import com.github.nacabaro.vbhelper.screens.SettingsScreen import com.github.nacabaro.vbhelper.screens.SettingsScreen
import com.github.nacabaro.vbhelper.screens.SpriteViewer
import com.github.nacabaro.vbhelper.screens.StorageScreen import com.github.nacabaro.vbhelper.screens.StorageScreen
@Composable @Composable
fun AppNavigation( fun AppNavigation(
onClickRead: () -> Unit, onClickRead: () -> Unit,
onClickScan: () -> Unit, onClickScan: () -> Unit,
onClickImportCard: () -> Unit,
isDoneReadingCharacter: Boolean isDoneReadingCharacter: Boolean
) { ) {
val navController = rememberNavController() val navController = rememberNavController()
@ -53,13 +57,31 @@ fun AppNavigation(
) )
} }
composable(BottomNavItem.Dex.route) { composable(BottomNavItem.Dex.route) {
DexScreen() DexScreen(
navController = navController
)
} }
composable(BottomNavItem.Settings.route) { composable(BottomNavItem.Settings.route) {
SettingsScreen( SettingsScreen(
navController = navController,
onClickImportCard = onClickImportCard
)
}
composable(BottomNavItem.Viewer.route) {
SpriteViewer(
navController = navController navController = navController
) )
} }
composable(BottomNavItem.CardView.route) {
val dimId = it.arguments?.getString("dimId")
Log.d("dimId", dimId.toString())
if (dimId != null) {
DiMScreen(
navController = navController,
dimId = dimId.toInt()
)
}
}
} }
} }
} }

View File

@ -13,4 +13,6 @@ sealed class BottomNavItem (
object Dex : BottomNavItem("Dex", R.drawable.baseline_menu_book_24, "Dex") object Dex : BottomNavItem("Dex", R.drawable.baseline_menu_book_24, "Dex")
object Storage : BottomNavItem("Storage", R.drawable.baseline_catching_pokemon_24, "Storage") object Storage : BottomNavItem("Storage", R.drawable.baseline_catching_pokemon_24, "Storage")
object Settings : BottomNavItem("Settings", R.drawable.baseline_settings_24, "Settings") object Settings : BottomNavItem("Settings", R.drawable.baseline_settings_24, "Settings")
object Viewer : BottomNavItem("Viewer", R.drawable.baseline_image_24, "Viewer")
object CardView : BottomNavItem("Card/{dimId}", R.drawable.baseline_image_24, "Card")
} }

View File

@ -1,5 +1,6 @@
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.Image
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
@ -7,38 +8,85 @@ 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.layout.size
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.Card import androidx.compose.material3.Card
import androidx.compose.material3.MaterialTheme 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
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.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource 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.compose.ui.unit.dp
import com.github.nacabaro.vbhelper.R import androidx.navigation.NavController
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.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 @Composable
fun DexScreen() { fun DexScreen(
navController: NavController
) {
val coroutineScope = rememberCoroutineScope()
val application = LocalContext.current.applicationContext as VBHelper
val dexRepository = DexRepository(application.container.db)
val dimList = remember { mutableStateOf<List<Dim>>(emptyList()) }
LaunchedEffect(dexRepository) {
coroutineScope.launch {
val newDimList = dexRepository.getAllDims()
dimList.value = newDimList // Replace the entire list atomically
}
}
Scaffold ( Scaffold (
topBar = { TopBanner("Discovered Digimon") } topBar = {
TopBanner(
text = "Discovered Digimon",
onGearClick = {
navController.navigate(BottomNavItem.Viewer.route)
}
)
}
) { contentPadding -> ) { contentPadding ->
LazyColumn ( LazyColumn (
modifier = Modifier modifier = Modifier
.padding(top = contentPadding.calculateTopPadding()) .padding(top = contentPadding.calculateTopPadding())
) { ) {
items(100) { i -> 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 = "Digimon $i", name = it.name,
icon = R.drawable.baseline_egg_24, logo = imageBitmap,
onClick = {}, onClick = {
navController
.navigate(
BottomNavItem
.CardView.route
.replace("{dimId}", "${it.id}")
)
},
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding( .padding(8.dp)
vertical = 8.dp,
horizontal = 16.dp
)
) )
} }
} }
@ -48,7 +96,7 @@ fun DexScreen() {
@Composable @Composable
fun DexDiMEntry( fun DexDiMEntry(
name: String, name: String,
icon: Int, logo: ImageBitmap,
onClick: () -> Unit, onClick: () -> Unit,
modifier: Modifier = Modifier modifier: Modifier = Modifier
) { ) {
@ -64,8 +112,9 @@ fun DexDiMEntry(
.padding(8.dp) .padding(8.dp)
) { ) {
Image ( Image (
painter = painterResource(id = icon), bitmap = logo,
contentDescription = name, contentDescription = name,
filterQuality = FilterQuality.None,
modifier = Modifier modifier = Modifier
.padding(8.dp) .padding(8.dp)
.size(64.dp) .size(64.dp)

View File

@ -0,0 +1,70 @@
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
import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.platform.LocalContext
import androidx.navigation.NavController
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(
navController: NavController,
dimId: Int
) {
val coroutineScope = rememberCoroutineScope()
val application = LocalContext.current.applicationContext as VBHelper
val dexRepository = DexRepository(application.container.db)
val characterList = remember { mutableStateOf<List<Character>>(emptyList()) }
LaunchedEffect(dexRepository) {
coroutineScope.launch {
val newCharacterList = dexRepository.getCharactersByDimId(dimId)
characterList.value = newCharacterList
}
}
Scaffold (
topBar = {
TopBanner(
text = "Discovered Digimon",
onBackClick = {
navController.popBackStack()
}
)
}
) { contentPadding ->
LazyVerticalGrid(
columns = GridCells.Fixed(3),
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 = { }
)
}
}
}
}

View File

@ -17,19 +17,12 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import androidx.navigation.NavController import androidx.navigation.NavController
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.vb.dim.sprite.SpriteData
import com.github.cfogrady.vb.dim.sprite.SpriteData.Sprite
import com.github.nacabaro.vbhelper.components.TopBanner import com.github.nacabaro.vbhelper.components.TopBanner
import java.io.File
import java.io.FileInputStream
@Composable @Composable
fun SettingsScreen( fun SettingsScreen(
navController: NavController, navController: NavController,
dimReader: DimReader = DimReader() onClickImportCard: () -> Unit
) { ) {
Scaffold ( Scaffold (
topBar = { topBar = {
@ -56,22 +49,8 @@ fun SettingsScreen(
SettingsEntry(title = "Import transform functions", description = "Import standard vital bracelet keys") { } SettingsEntry(title = "Import transform functions", description = "Import standard vital bracelet keys") { }
SettingsEntry(title = "Import decryption key", description = "Import standard vital bracelet keys") { } SettingsEntry(title = "Import decryption key", description = "Import standard vital bracelet keys") { }
SettingsSection("DiM/BEm management") SettingsSection("DiM/BEm management")
SettingsEntry(title = "Import DiM card", description = "Import DiM card file") { SettingsEntry(title = "Import DiM card", description = "Import DiM/BEm card file", onClick = onClickImportCard)
// placeholder SettingsEntry(title = "Rename DiM/BEm", description = "Set card name") { }
// val file = File("dummy_file.bin") //filePicker()
// val fileInputStream = FileInputStream(file)
// fileInputStream.use {
// val card = dimReader.readCard(fileInputStream, false)
// if (card is DimCard) {
// val logo = card.spriteData.sprites[0]
// }
// val beMemory = card as BemCard
// val logo = beMemory.spriteData.sprites[0]
// }
}
SettingsEntry(title = "Import BEm card", description = "Import BEm card file") {
// placeholder
}
SettingsSection("About and credits") SettingsSection("About and credits")
SettingsEntry(title = "Credits", description = "Credits") { } SettingsEntry(title = "Credits", description = "Credits") { }
SettingsEntry(title = "About", description = "About") { } SettingsEntry(title = "About", description = "About") { }

View File

@ -0,0 +1,89 @@
package com.github.nacabaro.vbhelper.screens
import android.graphics.Bitmap
import android.util.Log
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
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.mutableStateListOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Modifier
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.components.TopBanner
import com.github.nacabaro.vbhelper.di.VBHelper
import com.github.nacabaro.vbhelper.domain.Sprites
import com.github.nacabaro.vbhelper.source.SpriteRepo
import kotlinx.coroutines.launch
import java.nio.ByteBuffer
@Composable
fun SpriteViewer(
navController: NavController
) {
val coroutineScope = rememberCoroutineScope()
val application = LocalContext.current.applicationContext as VBHelper
val db = application.container.db
val spriteRepo = SpriteRepo(db)
val spriteList = remember { mutableStateListOf<Sprites>() }
Log.d("SpriteViewer", "spriteList: $spriteList")
LaunchedEffect(spriteRepo) {
coroutineScope.launch {
spriteList.clear()
spriteList.addAll(spriteRepo.getAllSprites())
}
}
Scaffold (
topBar = {
TopBanner(
text = "Sprite viewer",
onBackClick = {
navController.popBackStack()
}
)
},
modifier = Modifier
.fillMaxSize()
) { contentPadding ->
LazyColumn (
modifier = Modifier
.padding(top = contentPadding.calculateTopPadding())
) {
items(spriteList) { sprite ->
val bitmap = remember (sprite.sprite) {
Log.d("SpriteViewer", "sprite: $sprite")
Bitmap.createBitmap(sprite.width, sprite.height, Bitmap.Config.RGB_565).apply {
copyPixelsFromBuffer(ByteBuffer.wrap(sprite.sprite))
}
}
val imageBitmap = remember(bitmap) { bitmap.asImageBitmap() }
Image(
bitmap = imageBitmap,
contentDescription = "Sprite",
modifier = Modifier
.size(256.dp)
)
}
}
}
}

View File

@ -38,6 +38,7 @@ 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.R
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.source.StorageRepository import com.github.nacabaro.vbhelper.source.StorageRepository
@ -112,41 +113,6 @@ fun StorageScreen() {
} }
} }
@Composable
fun StorageEntry(
name: String,
icon: Int,
onClick: () -> Unit = {},
modifier: Modifier = Modifier
) {
Card(
shape = MaterialTheme.shapes.medium,
onClick = onClick,
modifier = modifier
.padding(8.dp)
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
.fillMaxSize()
) {
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)
)
}
}
}
@Composable @Composable
fun StorageDialog( fun StorageDialog(
characterId: Long, characterId: Long,

View File

@ -0,0 +1,18 @@
package com.github.nacabaro.vbhelper.source
import com.github.nacabaro.vbhelper.database.AppDatabase
import com.github.nacabaro.vbhelper.domain.Character
import com.github.nacabaro.vbhelper.domain.Dim
import kotlinx.coroutines.flow.Flow
class DexRepository (
private val db: AppDatabase
) {
suspend fun getAllDims(): List<Dim> {
return db.dimDao().getAllDims()
}
suspend fun getCharactersByDimId(dimId: Int): List<Character> {
return db.characterDao().getCharacterByDimId(dimId)
}
}

View File

@ -0,0 +1,12 @@
package com.github.nacabaro.vbhelper.source
import com.github.nacabaro.vbhelper.database.AppDatabase
import com.github.nacabaro.vbhelper.domain.Sprites
class SpriteRepo (
private val db: AppDatabase
) {
suspend fun getAllSprites(): List<Sprites> {
return db.characterDao().getAllSprites()
}
}

View File

@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M21,19V5c0,-1.1 -0.9,-2 -2,-2H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2zM8.5,13.5l2.5,3.01L14.5,12l4.5,6H5l3.5,-4.5z"/>
</vector>