~cytrogen/vbhelper

19fbed0ef244699eb288ac62087a83dc6a4c48ab — Nacho 1 year, 3 months ago 8229282
More things!
- Started slowly implementing the original model.
- Names are bitmaps, so we use that.
- Sprites are stored in RoomDB as bitmap.
- Can now import DiMs, need to implement BEms.
- Can also view all the sprites for all the DiMs and now you can view DiMs individually (these two are temporary).

Things to do next
- Use the new model when uploading characters from the watch.
- Figure out why my implementation of ABGR to BGRA isn't working (or something like that)
- Improve data retrieval from the database, maybe using flows and properly implemented view models. As of now the interface is quite flickery.
- Improve code that handles DiMs and BEms.

As for bugs
- Interface sometimes likes flickering a lot, need to figure out why
- I cannot for the life of me create transparent bitmaps... help!
- Something else I might be forgetting...

Going to continue working in this branch.
22 files changed, 605 insertions(+), 70 deletions(-)

M app/src/main/java/com/github/nacabaro/vbhelper/MainActivity.kt
A app/src/main/java/com/github/nacabaro/vbhelper/components/CharacterEntry.kt
A app/src/main/java/com/github/nacabaro/vbhelper/components/StorageEntry.kt
M app/src/main/java/com/github/nacabaro/vbhelper/components/TopBanner.kt
A app/src/main/java/com/github/nacabaro/vbhelper/daos/CharacterDao.kt
M app/src/main/java/com/github/nacabaro/vbhelper/daos/DiMDao.kt
M app/src/main/java/com/github/nacabaro/vbhelper/database/AppDatabase.kt
R app/src/main/java/com/github/nacabaro/vbhelper/domain/{Mon => Character}.kt
M app/src/main/java/com/github/nacabaro/vbhelper/domain/Dim.kt
M app/src/main/java/com/github/nacabaro/vbhelper/domain/Evolutions.kt
A app/src/main/java/com/github/nacabaro/vbhelper/domain/Sprites.kt
M app/src/main/java/com/github/nacabaro/vbhelper/domain/UserMonsters.kt
M app/src/main/java/com/github/nacabaro/vbhelper/navigation/AppNavigation.kt
M app/src/main/java/com/github/nacabaro/vbhelper/navigation/BottomNavItem.kt
M app/src/main/java/com/github/nacabaro/vbhelper/screens/DexScreen.kt
A app/src/main/java/com/github/nacabaro/vbhelper/screens/DimScreen.kt
M app/src/main/java/com/github/nacabaro/vbhelper/screens/SettingsScreen.kt
A app/src/main/java/com/github/nacabaro/vbhelper/screens/SpriteViewer.kt
M app/src/main/java/com/github/nacabaro/vbhelper/screens/StorageScreen.kt
A app/src/main/java/com/github/nacabaro/vbhelper/source/DexRepository.kt
A app/src/main/java/com/github/nacabaro/vbhelper/source/SpriteRepo.kt
A app/src/main/res/drawable/baseline_image_24.xml
M app/src/main/java/com/github/nacabaro/vbhelper/MainActivity.kt => app/src/main/java/com/github/nacabaro/vbhelper/MainActivity.kt +120 -4
@@ 10,25 10,31 @@ import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.platform.LocalContext
import androidx.lifecycle.lifecycleScope
import com.github.cfogrady.vb.dim.card.DimReader
import com.github.nacabaro.vbhelper.navigation.AppNavigation
import com.github.cfogrady.vbnfc.CryptographicTransformer
import com.github.cfogrady.vbnfc.R
import com.github.cfogrady.vbnfc.TagCommunicator
import com.github.cfogrady.vbnfc.be.BENfcCharacter
import com.github.cfogrady.vbnfc.data.DeviceType
import com.github.cfogrady.vbnfc.data.NfcCharacter
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.TemporaryCharacterData
import com.github.nacabaro.vbhelper.temporary_domain.TemporaryTransformationHistory
import com.github.nacabaro.vbhelper.ui.theme.VBHelperTheme
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch

class MainActivity : ComponentActivity() {
    private lateinit var nfcAdapter: NfcAdapter


@@ 36,10 42,14 @@ class MainActivity : ComponentActivity() {

    private var nfcCharacter = MutableStateFlow<NfcCharacter?>(null)

    private lateinit var activityResultLauncher: ActivityResultLauncher<Intent>

    // EXTRACTED DIRECTLY FROM EXAMPLE APP
    override fun onCreate(savedInstanceState: Bundle?) {
        deviceToCryptographicTransformers = getMapOfCryptographicTransformers()

        registerFileActivityResult()

        val maybeNfcAdapter = NfcAdapter.getDefaultAdapter(this)
        if (maybeNfcAdapter == null) {
            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
    private fun MainApplication() {
        var isDoneReadingCharacter by remember { mutableStateOf(false) }
        val application = LocalContext.current.applicationContext as VBHelper
        val storageRepository = application.container.db

        AppNavigation(
            isDoneReadingCharacter = isDoneReadingCharacter,
            onClickRead = {


@@ 75,6 183,13 @@ class MainActivity : ComponentActivity() {
            },
            onClickScan = {
                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() {
        val beCharacter = nfcCharacter as MutableStateFlow<BENfcCharacter?>

        val temporaryCharacterData = TemporaryCharacterData(
            dimId = nfcCharacter.value!!.dimId.toInt(),
            charIndex = nfcCharacter.value!!.charIndex.toInt(),

A app/src/main/java/com/github/nacabaro/vbhelper/components/CharacterEntry.kt => app/src/main/java/com/github/nacabaro/vbhelper/components/CharacterEntry.kt +52 -0
@@ 0,0 1,52 @@
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 CharacterEntry(
    name: ImageBitmap,
    icon: ImageBitmap,
    modifier: Modifier = Modifier,
    onClick: () -> Unit = {  }
) {
    Card(
        shape = MaterialTheme.shapes.medium,
        onClick = onClick,
        modifier = modifier
            .padding(8.dp)
    ) {
        Column(
            horizontalAlignment = Alignment.CenterHorizontally,
            modifier = Modifier
                .fillMaxSize()
        ) {
            Image(
                bitmap = icon,
                contentDescription = "Icon",
                modifier = Modifier
                    .padding(8.dp)
                    .size(64.dp)
            )
            Image(
                bitmap = name,
                contentDescription = "Name",
                modifier = Modifier
                    .padding(8.dp)
            )
        }
    }
}
\ No newline at end of file

A app/src/main/java/com/github/nacabaro/vbhelper/components/StorageEntry.kt => app/src/main/java/com/github/nacabaro/vbhelper/components/StorageEntry.kt +64 -0
@@ 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)
            )
        }
    }
}

M app/src/main/java/com/github/nacabaro/vbhelper/components/TopBanner.kt => app/src/main/java/com/github/nacabaro/vbhelper/components/TopBanner.kt +1 -1
@@ 20,7 20,7 @@ fun TopBanner(
    text: String,
    modifier: Modifier = Modifier,
    onGearClick: (() -> Unit)? = null,
    onBackClick: (() -> Unit)? = null
    onBackClick: (() -> Unit)? = null,
) {
    Box( // Use Box to overlay elements
        modifier = modifier

A app/src/main/java/com/github/nacabaro/vbhelper/daos/CharacterDao.kt => app/src/main/java/com/github/nacabaro/vbhelper/daos/CharacterDao.kt +26 -0
@@ 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>
}
\ No newline at end of file

M app/src/main/java/com/github/nacabaro/vbhelper/daos/DiMDao.kt => app/src/main/java/com/github/nacabaro/vbhelper/daos/DiMDao.kt +3 -3
@@ 2,15 2,15 @@ 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.Dim
import kotlinx.coroutines.flow.Flow

@Dao
interface DiMDao {
    @Insert
    @Insert(onConflict = OnConflictStrategy.IGNORE)
    suspend fun insertNewDim(dim: Dim)

    @Query("SELECT * FROM Dim")
    fun getAllDims(): Flow<List<Dim>>
    suspend fun getAllDims(): List<Dim>
}
\ No newline at end of file

M app/src/main/java/com/github/nacabaro/vbhelper/database/AppDatabase.kt => app/src/main/java/com/github/nacabaro/vbhelper/database/AppDatabase.kt +11 -2
@@ 2,6 2,11 @@ package com.github.nacabaro.vbhelper.database

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.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_domain.TemporaryBECharacterData
import com.github.nacabaro.vbhelper.temporary_domain.TemporaryCharacterData


@@ 12,10 17,14 @@ import com.github.nacabaro.vbhelper.temporary_domain.TemporaryTransformationHist
    entities = [
        TemporaryCharacterData::class,
        TemporaryBECharacterData::class,
        TemporaryTransformationHistory::class
        TemporaryTransformationHistory::class,
        Dim::class,
        Character::class,
        Sprites::class
    ]
)
abstract class AppDatabase : RoomDatabase() {
    abstract fun temporaryMonsterDao(): TemporaryMonsterDao

    abstract fun dimDao(): DiMDao
    abstract fun characterDao(): CharacterDao
}
\ No newline at end of file

R app/src/main/java/com/github/nacabaro/vbhelper/domain/Mon.kt => app/src/main/java/com/github/nacabaro/vbhelper/domain/Character.kt +9 -4
@@ 14,15 14,20 @@ import androidx.room.ForeignKey
        )
    ]
)
data class Mon (
    @PrimaryKey val id: Int,
data class Character (
    @PrimaryKey(autoGenerate = true) val id: Long,
    val dimId: Int,
    val monIndex: Int,
    val name: String,
    val name: ByteArray,
    val stage: Int, // These should be replaced with enums
    val attribute: Int, // This one too
    val baseHp: Int,
    val baseBp: 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
)
\ No newline at end of file

M app/src/main/java/com/github/nacabaro/vbhelper/domain/Dim.kt => app/src/main/java/com/github/nacabaro/vbhelper/domain/Dim.kt +4 -0
@@ 1,5 1,6 @@
package com.github.nacabaro.vbhelper.domain

import android.icu.text.ListFormatter.Width
import androidx.room.Entity
import androidx.room.PrimaryKey



@@ 7,6 8,9 @@ import androidx.room.PrimaryKey
data class Dim(
    @PrimaryKey(autoGenerate = true)
    val id: Int,
    val logo: ByteArray,
    val logoWidth: Int,
    val logoHeight: Int,
    val name: String,
    val stageCount: Int
)

M app/src/main/java/com/github/nacabaro/vbhelper/domain/Evolutions.kt => app/src/main/java/com/github/nacabaro/vbhelper/domain/Evolutions.kt +2 -2
@@ 7,13 7,13 @@ import androidx.room.PrimaryKey
@Entity(
    foreignKeys = [
        ForeignKey(
            entity = Mon::class,
            entity = Character::class,
            parentColumns = ["id"],
            childColumns = ["monId"],
            onDelete = ForeignKey.CASCADE
        ),
        ForeignKey(
            entity = Mon::class,
            entity = Character::class,
            parentColumns = ["id"],
            childColumns = ["nextMon"],
            onDelete = ForeignKey.CASCADE

A app/src/main/java/com/github/nacabaro/vbhelper/domain/Sprites.kt => app/src/main/java/com/github/nacabaro/vbhelper/domain/Sprites.kt +12 -0
@@ 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
)

M app/src/main/java/com/github/nacabaro/vbhelper/domain/UserMonsters.kt => app/src/main/java/com/github/nacabaro/vbhelper/domain/UserMonsters.kt +1 -1
@@ 13,7 13,7 @@ import androidx.room.PrimaryKey
            onDelete = ForeignKey.CASCADE
        ),
        ForeignKey(
            entity = Mon::class,
            entity = Character::class,
            parentColumns = ["id"],
            childColumns = ["monId"],
            onDelete = ForeignKey.CASCADE

M app/src/main/java/com/github/nacabaro/vbhelper/navigation/AppNavigation.kt => app/src/main/java/com/github/nacabaro/vbhelper/navigation/AppNavigation.kt +23 -1
@@ 1,5 1,6 @@
package com.github.nacabaro.vbhelper.navigation

import android.util.Log
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable


@@ 9,15 10,18 @@ import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import com.github.nacabaro.vbhelper.screens.BattlesScreen
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.ScanScreen
import com.github.nacabaro.vbhelper.screens.SettingsScreen
import com.github.nacabaro.vbhelper.screens.SpriteViewer
import com.github.nacabaro.vbhelper.screens.StorageScreen

@Composable
fun AppNavigation(
    onClickRead: () -> Unit,
    onClickScan: () -> Unit,
    onClickImportCard: () -> Unit,
    isDoneReadingCharacter: Boolean
) {
    val navController = rememberNavController()


@@ 53,13 57,31 @@ fun AppNavigation(
                )
            }
            composable(BottomNavItem.Dex.route) {
                DexScreen()
                DexScreen(
                    navController = navController
                )
            }
            composable(BottomNavItem.Settings.route) {
                SettingsScreen(
                    navController = navController,
                    onClickImportCard = onClickImportCard
                )
            }
            composable(BottomNavItem.Viewer.route) {
                SpriteViewer(
                    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()
                    )
                }
            }
        }
    }
}

M app/src/main/java/com/github/nacabaro/vbhelper/navigation/BottomNavItem.kt => app/src/main/java/com/github/nacabaro/vbhelper/navigation/BottomNavItem.kt +2 -0
@@ 13,4 13,6 @@ sealed class BottomNavItem (
    object Dex : BottomNavItem("Dex", R.drawable.baseline_menu_book_24, "Dex")
    object Storage : BottomNavItem("Storage", R.drawable.baseline_catching_pokemon_24, "Storage")
    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")
}
\ No newline at end of file

M app/src/main/java/com/github/nacabaro/vbhelper/screens/DexScreen.kt => app/src/main/java/com/github/nacabaro/vbhelper/screens/DexScreen.kt +64 -12
@@ 1,5 1,7 @@
package com.github.nacabaro.vbhelper.screens

import android.graphics.Bitmap
import android.util.Log
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row


@@ 7,38 9,88 @@ 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.mutableStateListOf
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.ImageBitmap
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
import com.github.nacabaro.vbhelper.R
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() {
fun DexScreen(
    navController: NavController
) {
    val coroutineScope = rememberCoroutineScope()
    val application = LocalContext.current.applicationContext as VBHelper
    val dexRepository = DexRepository(application.container.db)

    val dimList = remember { mutableStateListOf<Dim>() }

    LaunchedEffect(dexRepository) {
        coroutineScope.launch {
            dimList.clear()
            dimList.addAll(dexRepository.getAllDims())
        }
    }

    Scaffold (
        topBar = { TopBanner("Discovered Digimon") }
        topBar = {
            TopBanner(
                text = "Discovered Digimon",
                onGearClick = {
                    navController.navigate(BottomNavItem.Viewer.route)
                }
            )
        }
    ) { contentPadding ->
        LazyColumn (
            modifier = Modifier
                .padding(top = contentPadding.calculateTopPadding())
        ) {
            items(100) { i ->
            items(dimList) {
                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() }

                Log.d("DexScreen", "dimList: ${it.id}")

                DexDiMEntry(
                    name = "Digimon $i",
                    icon = R.drawable.baseline_egg_24,
                    onClick = {},
                    name = it.name,
                    logo = imageBitmap,
                    onClick = {
                        navController
                            .navigate(
                                BottomNavItem
                                    .CardView.route
                                    .replace("{dimId}", "${it.id}")
                            )
                    },
                    modifier = Modifier
                        .fillMaxWidth()
                        .padding(
                            vertical = 8.dp,
                            horizontal = 16.dp
                        )
                        .padding(8.dp)
                )
            }
        }


@@ 48,7 100,7 @@ fun DexScreen() {
@Composable
fun DexDiMEntry(
    name: String,
    icon: Int,
    logo: ImageBitmap,
    onClick: () -> Unit,
    modifier: Modifier = Modifier
) {


@@ 64,7 116,7 @@ fun DexDiMEntry(
                .padding(8.dp)
        ) {
            Image (
                painter = painterResource(id = icon),
                bitmap = logo,
                contentDescription = name,
                modifier = Modifier
                    .padding(8.dp)

A app/src/main/java/com/github/nacabaro/vbhelper/screens/DimScreen.kt => app/src/main/java/com/github/nacabaro/vbhelper/screens/DimScreen.kt +82 -0
@@ 0,0 1,82 @@
package com.github.nacabaro.vbhelper.screens

import android.graphics.Bitmap
import android.util.Log
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.mutableStateListOf
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.StorageEntry
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.navigation.BottomNavItem
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 { mutableStateListOf<Character>() }

    Log.d("dimId", dimId.toString())

    LaunchedEffect(dexRepository) {
        coroutineScope.launch {
            characterList.clear()
            characterList.addAll(dexRepository.getCharactersByDimId(dimId))
        }
    }

    Scaffold (
        topBar = {
            TopBanner(
                text = "Discovered Digimon",
                onBackClick = {
                    navController.popBackStack()
                }
            )
        }
    ) { contentPadding ->
        LazyVerticalGrid(
            columns = GridCells.Fixed(3),
            contentPadding = contentPadding
        ) {
            items(characterList) { character ->
                val bitmapName = remember (character.name) {
                    Bitmap.createBitmap(character.nameWidth, character.nameHeight, Bitmap.Config.RGB_565).apply {
                        copyPixelsFromBuffer(ByteBuffer.wrap(character.name))
                    }
                }
                val bitmapCharacter = remember (character.sprite1) {
                    Bitmap.createBitmap(character.spritesWidth, character.spritesHeight, Bitmap.Config.RGB_565).apply {
                        copyPixelsFromBuffer(ByteBuffer.wrap(character.sprite1))
                    }
                }
                val imageBitmapName = remember(bitmapName) { bitmapName.asImageBitmap() }
                val imageBitmapCharacter = remember(bitmapCharacter) { bitmapCharacter.asImageBitmap() }
                CharacterEntry(
                    name = imageBitmapName,
                    icon = imageBitmapCharacter,
                    onClick = {  }
                )
            }
        }
    }
}
\ No newline at end of file

M app/src/main/java/com/github/nacabaro/vbhelper/screens/SettingsScreen.kt => app/src/main/java/com/github/nacabaro/vbhelper/screens/SettingsScreen.kt +4 -5
@@ 29,7 29,8 @@ import java.io.FileInputStream
@Composable
fun SettingsScreen(
    navController: NavController,
    dimReader: DimReader = DimReader()
    //dimReader: DimReader = DimReader(),
    onClickImportCard: () -> Unit
) {
    Scaffold (
        topBar = {


@@ 56,7 57,8 @@ fun SettingsScreen(
            SettingsEntry(title = "Import transform functions", description = "Import standard vital bracelet keys") { }
            SettingsEntry(title = "Import decryption key", description = "Import standard vital bracelet keys") { }
            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") {
                onClickImportCard()
                // placeholder
//                val file = File("dummy_file.bin") //filePicker()
//                val fileInputStream = FileInputStream(file)


@@ 69,9 71,6 @@ fun SettingsScreen(
//                    val logo = beMemory.spriteData.sprites[0]
//                }
            }
            SettingsEntry(title = "Import BEm card", description = "Import BEm card file") {
                // placeholder
            }
            SettingsSection("About and credits")
            SettingsEntry(title = "Credits", description = "Credits") { }
            SettingsEntry(title = "About", description = "About") { }

A app/src/main/java/com/github/nacabaro/vbhelper/screens/SpriteViewer.kt => app/src/main/java/com/github/nacabaro/vbhelper/screens/SpriteViewer.kt +89 -0
@@ 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)
                )
            }
        }
    }
}


M app/src/main/java/com/github/nacabaro/vbhelper/screens/StorageScreen.kt => app/src/main/java/com/github/nacabaro/vbhelper/screens/StorageScreen.kt +1 -35
@@ 38,6 38,7 @@ 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.TopBanner
import com.github.nacabaro.vbhelper.di.VBHelper
import com.github.nacabaro.vbhelper.source.StorageRepository


@@ 113,41 114,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
fun StorageDialog(
    characterId: Long,
    onDismissRequest: () -> Unit

A app/src/main/java/com/github/nacabaro/vbhelper/source/DexRepository.kt => app/src/main/java/com/github/nacabaro/vbhelper/source/DexRepository.kt +18 -0
@@ 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)
    }
}
\ No newline at end of file

A app/src/main/java/com/github/nacabaro/vbhelper/source/SpriteRepo.kt => app/src/main/java/com/github/nacabaro/vbhelper/source/SpriteRepo.kt +12 -0
@@ 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()
    }
}

A app/src/main/res/drawable/baseline_image_24.xml => app/src/main/res/drawable/baseline_image_24.xml +5 -0
@@ 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>