~cytrogen/vbhelper

019f07d82717a421f3ec0692930fed9f096c9cac — nacabaro 8 months ago fb09004 + 4474e66
Merge pull request #36 from nacabaro/card/card_management

Few things here and there
20 files changed, 601 insertions(+), 71 deletions(-)

M app/src/main/java/com/github/nacabaro/vbhelper/MainActivity.kt
M app/src/main/java/com/github/nacabaro/vbhelper/components/TopBanner.kt
M app/src/main/java/com/github/nacabaro/vbhelper/daos/CardDao.kt
M app/src/main/java/com/github/nacabaro/vbhelper/navigation/AppNavigation.kt
A app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardEntry.kt
A app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardScreenController.kt
A app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardScreenControllerImpl.kt
R app/src/main/java/com/github/nacabaro/vbhelper/screens/{DimScreen => cardScreen/CardViewScreen}.kt
R app/src/main/java/com/github/nacabaro/vbhelper/screens/{DexScreen => cardScreen/CardsScreen}.kt
A app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/dialogs/CardDeleteDialog.kt
A app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/dialogs/CardRenameDialog.kt
M app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/ScanScreen.kt
M app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/ScanScreenController.kt
M app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/ScanScreenControllerImpl.kt
A app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/cardSelect/ChooseCard.kt
R app/src/main/java/com/github/nacabaro/vbhelper/{components/DexDimEntry => screens/scanScreen/cardSelect/ScanCardEntry}.kt
M app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/converters/FromNfcConverter.kt
M app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/converters/ToNfcConverter.kt
M app/src/main/java/com/github/nacabaro/vbhelper/screens/settingsScreen/SettingsScreenControllerImpl.kt
A app/src/main/res/drawable/baseline_edit_24.xml
M app/src/main/java/com/github/nacabaro/vbhelper/MainActivity.kt => app/src/main/java/com/github/nacabaro/vbhelper/MainActivity.kt +9 -4
@@ 14,6 14,7 @@ import com.github.nacabaro.vbhelper.screens.itemsScreen.ItemsScreenControllerImp
import com.github.nacabaro.vbhelper.screens.scanScreen.ScanScreenControllerImpl
import com.github.nacabaro.vbhelper.screens.settingsScreen.SettingsScreenControllerImpl
import com.github.nacabaro.vbhelper.screens.adventureScreen.AdventureScreenControllerImpl
import com.github.nacabaro.vbhelper.screens.cardScreen.CardScreenControllerImpl
import com.github.nacabaro.vbhelper.screens.spriteViewer.SpriteViewerControllerImpl
import com.github.nacabaro.vbhelper.screens.storageScreen.StorageScreenControllerImpl
import com.github.nacabaro.vbhelper.ui.theme.VBHelperTheme


@@ 47,6 48,7 @@ class MainActivity : ComponentActivity() {
        val storageScreenController = StorageScreenControllerImpl(this)
        val homeScreenController = HomeScreenControllerImpl(this)
        val spriteViewerController = SpriteViewerControllerImpl(this)
        val cardScreenController = CardScreenControllerImpl(this)

        super.onCreate(savedInstanceState)



@@ 61,7 63,8 @@ class MainActivity : ComponentActivity() {
                    adventureScreenController = adventureScreenController,
                    homeScreenController = homeScreenController,
                    storageScreenController = storageScreenController,
                    spriteViewerController = spriteViewerController
                    spriteViewerController = spriteViewerController,
                    cardScreenController = cardScreenController
                )
            }
        }


@@ 93,8 96,9 @@ class MainActivity : ComponentActivity() {
        adventureScreenController: AdventureScreenControllerImpl,
        storageScreenController: StorageScreenControllerImpl,
        homeScreenController: HomeScreenControllerImpl,
        spriteViewerController: SpriteViewerControllerImpl
        ) {
        spriteViewerController: SpriteViewerControllerImpl,
        cardScreenController: CardScreenControllerImpl
    ) {
        AppNavigation(
            applicationNavigationHandlers = AppNavigationHandlers(
                settingsScreenController,


@@ 103,7 107,8 @@ class MainActivity : ComponentActivity() {
                adventureScreenController,
                storageScreenController,
                homeScreenController,
                spriteViewerController
                spriteViewerController,
                cardScreenController
            )
        )
    }

M app/src/main/java/com/github/nacabaro/vbhelper/components/TopBanner.kt => app/src/main/java/com/github/nacabaro/vbhelper/components/TopBanner.kt +22 -10
@@ 24,7 24,8 @@ fun TopBanner(
    onGearClick: (() -> Unit)? = null,
    onBackClick: (() -> Unit)? = null,
    onScanClick: (() -> Unit)? = null,
    onAdventureClick: (() -> Unit)? = null
    onAdventureClick: (() -> Unit)? = null,
    onModifyClick: (() -> Unit)? = null
) {
    Box( // Use Box to overlay elements
        modifier = modifier


@@ 37,16 38,16 @@ fun TopBanner(
            textAlign = TextAlign.Center,
            fontSize = 24.sp,
            modifier = Modifier
                .align(Alignment.Center) // Center the text
                .align(Alignment.Center)
        )
         if (onGearClick != null) {
            IconButton(
                onClick = onGearClick,
                modifier = Modifier
                    .align(Alignment.CenterEnd) // Place gear icon at the end
                    .align(Alignment.CenterEnd)
            ) {
                Icon(
                    painter = painterResource(R.drawable.baseline_settings_24), // Use a gear icon
                    painter = painterResource(R.drawable.baseline_settings_24),
                    contentDescription = "Settings"
                )
            }


@@ 54,23 55,34 @@ fun TopBanner(
            IconButton(
                onClick = onAdventureClick,
                modifier = Modifier
                    .align(Alignment.CenterEnd) // Place gear icon at the end
                    .align(Alignment.CenterEnd)
            ) {
                Icon(
                    painter = painterResource(R.drawable.baseline_fort_24), // Use a gear icon
                    painter = painterResource(R.drawable.baseline_fort_24),
                    contentDescription = "Adventure"
                )
            }
        } else if (onModifyClick != null) {
             IconButton(
                 onClick = onModifyClick,
                 modifier = Modifier
                     .align(Alignment.CenterEnd)
             ) {
                 Icon(
                     painter = painterResource(R.drawable.baseline_edit_24),
                     contentDescription = "Adventure"
                 )
             }
         }

        if (onScanClick != null) {
            IconButton(
                onClick = onScanClick,
                modifier = Modifier
                    .align(Alignment.CenterStart) // Place gear icon at the end
                    .align(Alignment.CenterStart)
            ) {
                Icon(
                    painter = painterResource(R.drawable.baseline_nfc_24), // Use a gear icon
                    painter = painterResource(R.drawable.baseline_nfc_24),
                    contentDescription = "Scan"
                )
            }


@@ 78,10 90,10 @@ fun TopBanner(
            IconButton(
                onClick = onBackClick,
                modifier = Modifier
                    .align(Alignment.CenterStart) // Place gear icon at the end
                    .align(Alignment.CenterStart)
            ) {
                Icon(
                    painter = painterResource(R.drawable.baseline_arrow_back_24), // Use a gear icon
                    painter = painterResource(R.drawable.baseline_arrow_back_24),
                    contentDescription = "Settings"
                )
            }

M app/src/main/java/com/github/nacabaro/vbhelper/daos/CardDao.kt => app/src/main/java/com/github/nacabaro/vbhelper/daos/CardDao.kt +20 -2
@@ 9,8 9,26 @@ import com.github.nacabaro.vbhelper.domain.card.Card
@Dao
interface CardDao {
    @Insert(onConflict = OnConflictStrategy.IGNORE)
    suspend fun insertNewDim(card: Card): Long
    suspend fun insertNewCard(card: Card): Long

    @Query("SELECT * FROM Card WHERE cardId = :id")
    fun getDimById(id: Int): Card?
    fun getCardByCardId(id: Int): List<Card>

    @Query("SELECT * FROM Card WHERE id = :id")
    fun getCardById(id: Long): Card?

    @Query("""
        SELECT ca.* 
        FROM Card ca
        JOIN Character ch ON ca.id = ch.dimId
        JOIN UserCharacter uc ON ch.id = uc.charId
        WHERE uc.id = :id
    """)
    suspend fun getCardByCharacterId(id: Long): Card

    @Query("UPDATE Card SET name = :newName WHERE id = :id")
    suspend fun renameCard(id: Int, newName: String)

    @Query("DELETE FROM Card WHERE id = :id")
    suspend fun deleteCard(id: Long)
}
\ No newline at end of file

M app/src/main/java/com/github/nacabaro/vbhelper/navigation/AppNavigation.kt => app/src/main/java/com/github/nacabaro/vbhelper/navigation/AppNavigation.kt +9 -6
@@ 19,8 19,8 @@ import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import com.github.nacabaro.vbhelper.di.VBHelper
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.cardScreen.CardsScreen
import com.github.nacabaro.vbhelper.screens.cardScreen.CardViewScreen
import com.github.nacabaro.vbhelper.screens.homeScreens.HomeScreen
import com.github.nacabaro.vbhelper.screens.itemsScreen.ItemsScreen
import com.github.nacabaro.vbhelper.screens.scanScreen.ScanScreen


@@ 34,6 34,7 @@ import com.github.nacabaro.vbhelper.screens.itemsScreen.ItemsScreenControllerImp
import com.github.nacabaro.vbhelper.screens.settingsScreen.SettingsScreenControllerImpl
import com.github.nacabaro.vbhelper.screens.adventureScreen.AdventureScreen
import com.github.nacabaro.vbhelper.screens.adventureScreen.AdventureScreenControllerImpl
import com.github.nacabaro.vbhelper.screens.cardScreen.CardScreenControllerImpl
import com.github.nacabaro.vbhelper.screens.settingsScreen.CreditsScreen
import com.github.nacabaro.vbhelper.screens.spriteViewer.SpriteViewerControllerImpl
import com.github.nacabaro.vbhelper.screens.storageScreen.StorageScreenControllerImpl


@@ 46,7 47,8 @@ data class AppNavigationHandlers(
    val adventureScreenController: AdventureScreenControllerImpl,
    val storageScreenController: StorageScreenControllerImpl,
    val homeScreenController: HomeScreenControllerImpl,
    val spriteViewerController: SpriteViewerControllerImpl
    val spriteViewerController: SpriteViewerControllerImpl,
    val cardScreenController: CardScreenControllerImpl
)

@Composable


@@ 121,8 123,9 @@ fun AppNavigation(
                )
            }
            composable(NavigationItems.Dex.route) {
                DexScreen(
                    navController = navController
                CardsScreen(
                    navController = navController,
                    cardScreenController = applicationNavigationHandlers.cardScreenController
                )
            }
            composable(NavigationItems.Settings.route) {


@@ 140,7 143,7 @@ fun AppNavigation(
            composable(NavigationItems.CardView.route) {
                val cardId = it.arguments?.getString("cardId")
                if (cardId != null) {
                    DiMScreen(
                    CardViewScreen(
                        navController = navController,
                        dimId = cardId.toLong()
                    )

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

import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material.icons.filled.Edit
import androidx.compose.material3.Card
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
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.platform.LocalContext
import androidx.compose.ui.unit.dp
import com.github.nacabaro.vbhelper.utils.BitmapData
import com.github.nacabaro.vbhelper.utils.getBitmap

@Composable
fun CardEntry(
    name: String,
    logo: BitmapData,
    obtainedCharacters: Int,
    totalCharacters: Int,
    onClick: () -> Unit,
    displayModify: Boolean,
    onClickModify: () -> Unit,
    onClickDelete: () -> Unit,
    modifier: Modifier = Modifier
) {
    val bitmap = remember (logo.bitmap) { logo.getBitmap() }
    val imageBitmap = remember(bitmap) { bitmap.asImageBitmap() }
    val density: Float = LocalContext.current.resources.displayMetrics.density
    val dpSize = (logo.width * 4 / density).dp

    Card (
        shape = MaterialTheme.shapes.medium,
        modifier = modifier,
        onClick = if (!displayModify) {
            onClick
        } else {
            {  }
        }
    ) {
        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(dpSize)
            )
            Column(
                modifier = Modifier
                    .padding(8.dp)
                    .weight(1f)
            ) {
                Text(
                    text = name,
                    modifier = Modifier
                )
                Text(
                    text = "$obtainedCharacters of $totalCharacters characters obtained",
                    fontFamily = MaterialTheme.typography.labelSmall.fontFamily,
                    fontSize = MaterialTheme.typography.labelSmall.fontSize,
                    modifier = Modifier
                )
            }
            if (displayModify) {
                Row (
                    modifier = Modifier,
                    horizontalArrangement = Arrangement.End,
                ) {
                    IconButton(
                        onClick = onClickModify
                    ) {
                        Icon(
                            imageVector = Icons.Default.Edit,
                            contentDescription = "Edit"
                        )
                    }
                    IconButton(
                        onClick = onClickDelete
                    ) {
                        Icon(
                            imageVector = Icons.Default.Delete,
                            contentDescription = "Delete"
                        )
                    }
                }
            }
        }
    }
}
\ No newline at end of file

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

interface CardScreenController {
    fun renameCard(cardId: Long, newName: String, onRenamed: (String) -> Unit)
    fun deleteCard(cardId: Long, onDeleted: () -> Unit)
}
\ No newline at end of file

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

import androidx.activity.ComponentActivity
import androidx.lifecycle.lifecycleScope
import com.github.nacabaro.vbhelper.di.VBHelper
import kotlinx.coroutines.launch

class CardScreenControllerImpl(
    private val componentActivity: ComponentActivity,
) : CardScreenController {
    private val application = componentActivity.applicationContext as VBHelper
    private val database = application.container.db


    override fun renameCard(cardId: Long, newName: String, onRenamed: (String) -> Unit) {
        componentActivity.lifecycleScope.launch {
            database
                .cardDao()
                .renameCard(cardId.toInt(), newName)

            onRenamed(newName)
        }
    }

    override fun deleteCard(cardId: Long, onDeleted: () -> Unit) {
        componentActivity.lifecycleScope.launch {
            database
                .cardDao()
                .deleteCard(cardId)

            onDeleted()
        }
    }
}
\ No newline at end of file

R app/src/main/java/com/github/nacabaro/vbhelper/screens/DimScreen.kt => app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardViewScreen.kt +2 -3
@@ 1,4 1,4 @@
package com.github.nacabaro.vbhelper.screens
package com.github.nacabaro.vbhelper.screens.cardScreen

import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid


@@ 14,14 14,13 @@ 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.characters.Character
import com.github.nacabaro.vbhelper.di.VBHelper
import com.github.nacabaro.vbhelper.dtos.CharacterDtos
import com.github.nacabaro.vbhelper.source.DexRepository
import kotlinx.coroutines.launch

@Composable
fun DiMScreen(
fun CardViewScreen(
    navController: NavController,
    dimId: Long
) {

R app/src/main/java/com/github/nacabaro/vbhelper/screens/DexScreen.kt => app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardsScreen.kt +73 -11
@@ 1,5 1,6 @@
package com.github.nacabaro.vbhelper.screens
package com.github.nacabaro.vbhelper.screens.cardScreen

import android.util.Log
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn


@@ 7,45 8,54 @@ import androidx.compose.foundation.lazy.items
import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
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.dtos.CardDtos
import com.github.nacabaro.vbhelper.navigation.NavigationItems
import com.github.nacabaro.vbhelper.screens.cardScreen.dialogs.CardDeleteDialog
import com.github.nacabaro.vbhelper.screens.cardScreen.dialogs.CardRenameDialog
import com.github.nacabaro.vbhelper.source.DexRepository
import kotlinx.coroutines.launch

@Composable
fun DexScreen(
    navController: NavController
fun CardsScreen(
    navController: NavController,
    cardScreenController: CardScreenControllerImpl
) {
    val coroutineScope = rememberCoroutineScope()
    val application = LocalContext.current.applicationContext as VBHelper
    val dexRepository = DexRepository(application.container.db)

    val cardList = remember { mutableStateOf<List<CardDtos.CardProgress>>(emptyList()) }

    val selectedCard = remember { mutableStateOf<CardDtos.CardProgress?>(null) }
    var clickedDelete by remember { mutableStateOf(false) }
    var clickedRename by remember { mutableStateOf(false) }

    var modifyCards by remember { mutableStateOf(false) }

    LaunchedEffect(dexRepository) {
        coroutineScope.launch {
            val newDimList = dexRepository.getAllDims()
            cardList.value = newDimList // Replace the entire list atomically
            cardList.value = newDimList
        }
    }

    Scaffold (
        topBar = {
            TopBanner(
                text = "Discovered characters",
                onGearClick = {
                    navController.navigate(NavigationItems.Viewer.route)
                text = "My cards",
                onModifyClick = {
                    modifyCards = !modifyCards
                }
            )
        }


@@ 55,7 65,7 @@ fun DexScreen(
                .padding(top = contentPadding.calculateTopPadding())
        ) {
            items(cardList.value) {
                DexDiMEntry(
                CardEntry(
                    name = it.cardName,
                    logo = BitmapData(
                        bitmap = it.cardLogo,


@@ 74,10 84,62 @@ fun DexScreen(
                    totalCharacters = it.totalCharacters,
                    modifier = Modifier
                        .fillMaxWidth()
                        .padding(8.dp)
                        .padding(8.dp),
                    displayModify = modifyCards,
                    onClickModify = {
                        selectedCard.value = it
                        clickedRename = true
                    },
                    onClickDelete = {
                        selectedCard.value = it
                        clickedDelete = true
                    }
                )
            }
        }
    }

    if (clickedRename) {
        CardRenameDialog(
            onDismiss = {
                clickedRename = false
                selectedCard.value = null
            },
            onRename = { newName ->
                Log.d("CardsScreen", "New name: $newName")
                Log.d("CardsScreen", "Card: ${selectedCard.value.toString()}")
                cardScreenController
                    .renameCard(
                        cardId = selectedCard.value!!.cardId,
                        newName = newName,
                        onRenamed = {
                            clickedRename = false
                            selectedCard.value = null
                        }
                    )
            },
            currentName = selectedCard.value!!.cardName
        )
    }

    if (clickedDelete) {
        CardDeleteDialog(
            cardName = selectedCard.value!!.cardName,
            onDismiss = {
                clickedDelete = false
                selectedCard.value = null
            },
            onConfirm = {
                cardScreenController
                    .deleteCard(
                        cardId = selectedCard.value!!.cardId,
                        onDeleted = {
                            clickedDelete = false
                            selectedCard.value = null
                        }
                    )
            }
        )
    }
}


A app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/dialogs/CardDeleteDialog.kt => app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/dialogs/CardDeleteDialog.kt +51 -0
@@ 0,0 1,51 @@
package com.github.nacabaro.vbhelper.screens.cardScreen.dialogs

import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.Card
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog

@Composable
fun CardDeleteDialog(
    cardName: String,
    onDismiss: () -> Unit,
    onConfirm: () -> Unit
) {
    Dialog(
        onDismissRequest = onDismiss

    ) {
        Card ( ) {
            Column (
                modifier = Modifier
                    .padding(16.dp)
            ) {
                Text(text = "Are you sure you want to delete $cardName. This action will also delete all the characters raised from this card.")
                Spacer(modifier = Modifier.padding(8.dp))
                Row {
                    Button(
                        onClick = {
                            onDismiss()
                        }
                    ) {
                        Text(text = "Confirm")
                    }
                    Button(
                        onClick = {
                            onConfirm()
                        }
                    ) {
                        Text(text = "Delete")
                    }
                }
            }
        }
    }
}
\ No newline at end of file

A app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/dialogs/CardRenameDialog.kt => app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/dialogs/CardRenameDialog.kt +52 -0
@@ 0,0 1,52 @@
package com.github.nacabaro.vbhelper.screens.cardScreen.dialogs

import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.Card
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
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.Modifier
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog

@Composable
fun CardRenameDialog(
    onDismiss: () -> Unit,
    onRename: (String) -> Unit,
    currentName: String
) {
    var cardName by remember { mutableStateOf(currentName) }

    Dialog(
        onDismissRequest = onDismiss

    ) {
        Card ( ) {
            Column (
                modifier = Modifier
                    .padding(16.dp)
            ) {
                TextField(
                    value = cardName,
                    onValueChange = { cardName = it }
                )
                Spacer(modifier = Modifier.padding(8.dp))
                Button(
                    onClick = {
                        onRename(cardName)
                        onDismiss()
                    }
                ) {
                    Text(text = "Rename")
                }
            }
        }
    }
}
\ No newline at end of file

M app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/ScanScreen.kt => app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/ScanScreen.kt +41 -9
@@ 30,7 30,9 @@ import com.github.cfogrady.vbnfc.data.NfcCharacter
import com.github.nacabaro.vbhelper.ActivityLifecycleListener
import com.github.nacabaro.vbhelper.components.TopBanner
import com.github.nacabaro.vbhelper.di.VBHelper
import com.github.nacabaro.vbhelper.domain.card.Card
import com.github.nacabaro.vbhelper.navigation.NavigationItems
import com.github.nacabaro.vbhelper.screens.cardScreen.ChooseCard
import com.github.nacabaro.vbhelper.source.StorageRepository
import com.github.nacabaro.vbhelper.source.isMissingSecrets
import com.github.nacabaro.vbhelper.source.proto.Secrets


@@ 53,6 55,8 @@ fun ScanScreen(
    val storageRepository = StorageRepository(application.container.db)
    var nfcCharacter by remember { mutableStateOf<NfcCharacter?>(null) }

    var cardsRead by remember { mutableStateOf<List<Card>?>(null) }

    val context = LocalContext.current

    LaunchedEffect(storageRepository) {


@@ 71,6 75,7 @@ fun ScanScreen(

    var readingScreen by remember { mutableStateOf(false) }
    var writingScreen by remember { mutableStateOf(false) }
    var cardSelectScreen by remember { mutableStateOf(false) }
    var isDoneReadingCharacter by remember { mutableStateOf(false) }
    var isDoneSendingCard by remember { mutableStateOf(false) }
    var isDoneWritingCharacter by remember { mutableStateOf(false) }


@@ 85,15 90,33 @@ fun ScanScreen(
                    }

                    override fun onResume() {
                        scanScreenController.onClickRead(secrets!!) {
                            isDoneReadingCharacter = true
                        }
                        scanScreenController.onClickRead(
                            secrets = secrets!!,
                            onComplete = {
                                isDoneReadingCharacter = true
                            },
                            onMultipleCards = { cards ->
                                cardsRead = cards
                                readingScreen = false
                                cardSelectScreen = true
                                isDoneReadingCharacter = true
                            }
                        )
                    }
                }
            )
            scanScreenController.onClickRead(secrets!!) {
                isDoneReadingCharacter = true
            }
            scanScreenController.onClickRead(
                secrets = secrets!!,
                onComplete = {
                    isDoneReadingCharacter = true
                },
                onMultipleCards = { cards ->
                    cardsRead = cards
                    readingScreen = false
                    cardSelectScreen = true
                    isDoneReadingCharacter = true
                }
            )
        }
        onDispose {
            if(readingScreen) {


@@ 149,7 172,7 @@ fun ScanScreen(
        }
    }

    if (isDoneReadingCharacter) {
    if (isDoneReadingCharacter && !cardSelectScreen) {
        readingScreen = false
        navController.navigate(NavigationItems.Home.route)
    } else if (isDoneSendingCard && isDoneWritingCharacter) {


@@ 181,6 204,14 @@ fun ScanScreen(
                scanScreenController.cancelRead()
            }
        }
    } else if (cardSelectScreen) {
        ChooseCard(
            cards = cardsRead!!,
            onCardSelected = { card ->
                cardSelectScreen = false
                scanScreenController.flushCharacter(card.id)
            }
        )
    } else {
        ChooseConnectOption(
            onClickRead = when {


@@ 290,11 321,12 @@ fun ScanScreenPreview() {
            ) {

            }
            override fun onClickRead(secrets: Secrets, onComplete: ()->Unit) {}
            override fun flushCharacter(cardId: Long) {}
            override fun onClickRead(secrets: Secrets, onComplete: ()->Unit, onMultipleCards: (List<Card>) -> Unit) {}
            override fun onClickCheckCard(secrets: Secrets, nfcCharacter: NfcCharacter, onComplete: () -> Unit) {}
            override fun onClickWrite(secrets: Secrets, nfcCharacter: NfcCharacter, onComplete: () -> Unit) {}
            override fun cancelRead() {}
            override fun characterFromNfc(nfcCharacter: NfcCharacter): String { return "" }
            override fun characterFromNfc(nfcCharacter: NfcCharacter, onMultipleCards: (List<Card>, NfcCharacter) -> Unit): String { return "" }
            override suspend fun characterToNfc(characterId: Long): NfcCharacter? { return null }
        },
        characterId = null,

M app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/ScanScreenController.kt => app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/ScanScreenController.kt +8 -2
@@ 2,12 2,13 @@ package com.github.nacabaro.vbhelper.screens.scanScreen

import com.github.cfogrady.vbnfc.data.NfcCharacter
import com.github.nacabaro.vbhelper.ActivityLifecycleListener
import com.github.nacabaro.vbhelper.domain.card.Card
import com.github.nacabaro.vbhelper.source.proto.Secrets
import kotlinx.coroutines.flow.Flow

interface ScanScreenController {
    val secretsFlow: Flow<Secrets>
    fun onClickRead(secrets: Secrets, onComplete: ()->Unit)
    fun onClickRead(secrets: Secrets, onComplete: ()->Unit, onMultipleCards: (List<Card>) -> Unit)
    fun onClickCheckCard(secrets: Secrets, nfcCharacter: NfcCharacter, onComplete: () -> Unit)
    fun onClickWrite(secrets: Secrets, nfcCharacter: NfcCharacter, onComplete: () -> Unit)



@@ 16,6 17,11 @@ interface ScanScreenController {
    fun registerActivityLifecycleListener(key: String, activityLifecycleListener: ActivityLifecycleListener)
    fun unregisterActivityLifecycleListener(key: String)

    fun characterFromNfc(nfcCharacter: NfcCharacter): String
    fun flushCharacter(cardId: Long)

    fun characterFromNfc(
        nfcCharacter: NfcCharacter,
        onMultipleCards: (List<Card>, NfcCharacter) -> Unit
    ): String
    suspend fun characterToNfc(characterId: Long): NfcCharacter?
}
\ No newline at end of file

M app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/ScanScreenControllerImpl.kt => app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/ScanScreenControllerImpl.kt +26 -5
@@ 15,6 15,7 @@ import com.github.cfogrady.vbnfc.be.BENfcCharacter
import com.github.cfogrady.vbnfc.data.NfcCharacter
import com.github.cfogrady.vbnfc.vb.VBNfcCharacter
import com.github.nacabaro.vbhelper.ActivityLifecycleListener
import com.github.nacabaro.vbhelper.domain.card.Card
import com.github.nacabaro.vbhelper.screens.scanScreen.converters.FromNfcConverter
import com.github.nacabaro.vbhelper.screens.scanScreen.converters.ToNfcConverter
import com.github.nacabaro.vbhelper.source.getCryptographicTransformerMap


@@ 31,7 32,7 @@ class ScanScreenControllerImpl(
    private val registerActivityLifecycleListener: (String, ActivityLifecycleListener)->Unit,
    private val unregisterActivityLifecycleListener: (String)->Unit,
): ScanScreenController {

    private var lastScannedCharacter: NfcCharacter? = null
    private val nfcAdapter: NfcAdapter

    init {


@@ 43,10 44,14 @@ class ScanScreenControllerImpl(
        checkSecrets()
    }

    override fun onClickRead(secrets: Secrets, onComplete: ()->Unit) {
    override fun onClickRead(secrets: Secrets, onComplete: ()->Unit, onMultipleCards: (List<Card>) -> Unit) {
        handleTag(secrets) { tagCommunicator ->
            val character = tagCommunicator.receiveCharacter()
            val resultMessage = characterFromNfc(character)
            val resultMessage = characterFromNfc(character) { cards, nfcCharacter ->
                lastScannedCharacter = nfcCharacter
                onMultipleCards(cards)

            }
            onComplete.invoke()
            resultMessage
        }


@@ 156,11 161,14 @@ class ScanScreenControllerImpl(
        componentActivity.startActivity(Intent(Settings.ACTION_WIRELESS_SETTINGS))
    }

    override fun characterFromNfc(nfcCharacter: NfcCharacter): String {
    override fun characterFromNfc(
        nfcCharacter: NfcCharacter,
        onMultipleCards: (List<Card>, NfcCharacter) -> Unit
    ): String {
        val nfcConverter = FromNfcConverter(
            componentActivity = componentActivity
        )
        return nfcConverter.addCharacter(nfcCharacter)
        return nfcConverter.addCharacter(nfcCharacter, onMultipleCards)
    }

    override suspend fun characterToNfc(characterId: Long): NfcCharacter {


@@ 172,4 180,17 @@ class ScanScreenControllerImpl(
        Log.d("CharacterType", character.toString())
        return character
    }

    override fun flushCharacter(cardId: Long) {
        val nfcConverter = FromNfcConverter(
            componentActivity = componentActivity
        )

        componentActivity.lifecycleScope.launch(Dispatchers.IO) {
            if (lastScannedCharacter != null) {
                nfcConverter.addCharacterUsingCard(lastScannedCharacter!!, cardId)
                lastScannedCharacter = null
            }
        }
    }
}
\ No newline at end of file

A app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/cardSelect/ChooseCard.kt => app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/cardSelect/ChooseCard.kt +48 -0
@@ 0,0 1,48 @@
package com.github.nacabaro.vbhelper.screens.cardScreen

import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.github.nacabaro.vbhelper.components.TopBanner
import com.github.nacabaro.vbhelper.domain.card.Card
import com.github.nacabaro.vbhelper.screens.scanScreen.cardSelect.ScanCardEntry
import com.github.nacabaro.vbhelper.utils.BitmapData

@Composable
fun ChooseCard(
    cards: List<Card>,
    onCardSelected: (Card) -> Unit
) {
    Scaffold (
        topBar = {
            TopBanner(
                text = "Choose card",
            )
        }
    ) { contentPadding ->
        LazyColumn (
            modifier = Modifier
                .padding(top = contentPadding.calculateTopPadding())
        ) {
            items(cards) {
                ScanCardEntry(
                    name = it.name,
                    logo = BitmapData(
                        it.logo,
                        it.logoWidth,
                        it.logoHeight
                    ),
                    onClick = {
                        onCardSelected(it)
                    },
                    modifier = Modifier
                        .padding(8.dp)
                )
            }
        }
    }
}
\ No newline at end of file

R app/src/main/java/com/github/nacabaro/vbhelper/components/DexDimEntry.kt => app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/cardSelect/ScanCardEntry.kt +3 -10
@@ 1,4 1,4 @@
package com.github.nacabaro.vbhelper.components
package com.github.nacabaro.vbhelper.screens.scanScreen.cardSelect

import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Arrangement


@@ 21,11 21,9 @@ import com.github.nacabaro.vbhelper.utils.BitmapData
import com.github.nacabaro.vbhelper.utils.getBitmap

@Composable
fun DexDiMEntry(
fun ScanCardEntry(
    name: String,
    logo: BitmapData,
    obtainedCharacters: Int,
    totalCharacters: Int,
    onClick: () -> Unit,
    modifier: Modifier = Modifier
) {


@@ 56,17 54,12 @@ fun DexDiMEntry(
            Column(
                modifier = Modifier
                    .padding(8.dp)
                    .weight(1f)
            ) {
                Text(
                    text = name,
                    modifier = Modifier
                )
                Text(
                    text = "$obtainedCharacters of $totalCharacters characters obtained",
                    fontFamily = MaterialTheme.typography.labelSmall.fontFamily,
                    fontSize = MaterialTheme.typography.labelSmall.fontSize,
                    modifier = Modifier
                )
            }
        }
    }

M app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/converters/FromNfcConverter.kt => app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/converters/FromNfcConverter.kt +60 -7
@@ 21,17 21,70 @@ class FromNfcConverter (
) {
    private val application = componentActivity.applicationContext as VBHelper
    private val database = application.container.db



    fun addCharacter(nfcCharacter: NfcCharacter): String {
    
    
    fun addCharacterUsingCard(
        nfcCharacter: NfcCharacter,
        cardId: Long
    ): String {
        val cardData = database
            .cardDao()
            .getDimById(nfcCharacter.dimId.toInt())
            .getCardById(cardId)

        if (cardData == null)
        if (cardData == null) {
            return "Card not found"
        }

        return insertCharacter(nfcCharacter, cardData)
    }
    

    fun addCharacter(
        nfcCharacter: NfcCharacter,
        onMultipleCards: (List<Card>, NfcCharacter) -> Unit
    ): String {
        val appReservedCardId = nfcCharacter
            .appReserved2[0].toLong()

        var cardData: Card? =  null

        if (appReservedCardId != 0L) {
            val fetchedCard = database
                .cardDao()
                .getCardById(appReservedCardId)

            if (fetchedCard == null) {
                return "Card not found"
            } else if (fetchedCard.cardId == nfcCharacter.dimId.toInt()) {
                cardData = fetchedCard
            }
        }

        if (cardData == null) {
            val allCards = database
                .cardDao()
                .getCardByCardId(nfcCharacter.dimId.toInt())

            if (allCards.isEmpty())
                return "Card not found"

            if (allCards.size > 1) {
                onMultipleCards(allCards, nfcCharacter)
                return "Multiple cards found"
            }

            cardData = allCards[0]
        }

        return insertCharacter(nfcCharacter, cardData)
    }



    private fun insertCharacter(
        nfcCharacter: NfcCharacter,
        cardData: Card
    ): String {
        val cardCharData = database
            .characterDao()
            .getCharacterByMonIndex(nfcCharacter.charIndex.toInt(), cardData.id)


@@ 92,7 145,7 @@ class FromNfcConverter (

        return "Done reading character!"
    }

    


    private fun updateCardProgress(

M app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/converters/ToNfcConverter.kt => app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/converters/ToNfcConverter.kt +18 -1
@@ 84,7 84,7 @@ class ToNfcConverter(
            transformationHistory = paddedTransformationArray,
            vitalHistory = generateVitalsHistoryArray(characterId),
            appReserved1 = ByteArray(12) {0},
            appReserved2 = Array(3) {0u},
            appReserved2 = generateUShortAppReserved(userCharacter),
            generation = vbData.generation.toUShort(),
            totalTrophies = vbData.totalTrophies.toUShort(),
            specialMissions = watchSpecialMissions.toTypedArray()


@@ 94,6 94,23 @@ class ToNfcConverter(
    }


    private suspend fun generateUShortAppReserved(
        userCharacter: UserCharacter
    ): Array<UShort> {
        val cardData = database
            .cardDao()
            .getCardByCharacterId(userCharacter.id)

        val appReserved = Array<UShort>(3) {
            0u
        }

        appReserved[0] = cardData.id.toUShort()

        return appReserved
    }



    private suspend fun generateSpecialMissionsArray(
        characterId: Long

M app/src/main/java/com/github/nacabaro/vbhelper/screens/settingsScreen/SettingsScreenControllerImpl.kt => app/src/main/java/com/github/nacabaro/vbhelper/screens/settingsScreen/SettingsScreenControllerImpl.kt +1 -1
@@ 129,7 129,7 @@ class SettingsScreenControllerImpl(

                val dimId = database
                    .cardDao()
                    .insertNewDim(cardModel)
                    .insertNewCard(cardModel)

                val cardProgress = CardProgress(
                    cardId = dimId,

A app/src/main/res/drawable/baseline_edit_24.xml => app/src/main/res/drawable/baseline_edit_24.xml +9 -0
@@ 0,0 1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="960"
    android:viewportHeight="960">
  <path
      android:pathData="M200,760h57l391,-391 -57,-57 -391,391v57ZM120,840v-170l528,-527q12,-11 26.5,-17t30.5,-6q16,0 31,6t26,18l55,56q12,11 17.5,26t5.5,30q0,16 -5.5,30.5T817,313L290,840L120,840ZM760,256 L704,200 760,256ZM619,341 L591,312 648,369 619,341Z"
      android:fillColor="#000000"/>
</vector>