From 4474e6689d96977ecfef521ff2595f1d433b5580 Mon Sep 17 00:00:00 2001 From: Nacho Date: Wed, 6 Aug 2025 20:10:21 +0200 Subject: [PATCH] Few things here and there - Card management, you can now modify the name of the cards and remove cards too. - Also, support for multiple cards with the same ID works too. When scanning for the first time, if a repeat card exists, the user will be asked to choose which card the character comes from. Future scans will not ask since it is stored in the watch app reserved area. --- .../github/nacabaro/vbhelper/MainActivity.kt | 13 +- .../nacabaro/vbhelper/components/TopBanner.kt | 32 ++-- .../github/nacabaro/vbhelper/daos/CardDao.kt | 22 ++- .../vbhelper/navigation/AppNavigation.kt | 15 +- .../nacabaro/vbhelper/screens/DexScreen.kt | 83 ---------- .../vbhelper/screens/cardScreen/CardEntry.kt | 109 +++++++++++++ .../cardScreen/CardScreenController.kt | 6 + .../cardScreen/CardScreenControllerImpl.kt | 34 ++++ .../CardViewScreen.kt} | 5 +- .../screens/cardScreen/CardsScreen.kt | 145 ++++++++++++++++++ .../cardScreen/dialogs/CardDeleteDialog.kt | 51 ++++++ .../cardScreen/dialogs/CardRenameDialog.kt | 52 +++++++ .../vbhelper/screens/scanScreen/ScanScreen.kt | 50 ++++-- .../scanScreen/ScanScreenController.kt | 10 +- .../scanScreen/ScanScreenControllerImpl.kt | 31 +++- .../scanScreen/cardSelect/ChooseCard.kt | 48 ++++++ .../scanScreen/cardSelect/ScanCardEntry.kt} | 13 +- .../scanScreen/converters/FromNfcConverter.kt | 67 +++++++- .../scanScreen/converters/ToNfcConverter.kt | 19 ++- .../SettingsScreenControllerImpl.kt | 2 +- .../main/res/drawable/baseline_edit_24.xml | 9 ++ 21 files changed, 673 insertions(+), 143 deletions(-) delete mode 100644 app/src/main/java/com/github/nacabaro/vbhelper/screens/DexScreen.kt create mode 100644 app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardEntry.kt create mode 100644 app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardScreenController.kt create mode 100644 app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardScreenControllerImpl.kt rename app/src/main/java/com/github/nacabaro/vbhelper/screens/{DimScreen.kt => cardScreen/CardViewScreen.kt} (94%) create mode 100644 app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardsScreen.kt create mode 100644 app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/dialogs/CardDeleteDialog.kt create mode 100644 app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/dialogs/CardRenameDialog.kt create mode 100644 app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/cardSelect/ChooseCard.kt rename app/src/main/java/com/github/nacabaro/vbhelper/{components/DexDimEntry.kt => screens/scanScreen/cardSelect/ScanCardEntry.kt} (82%) create mode 100644 app/src/main/res/drawable/baseline_edit_24.xml diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/MainActivity.kt b/app/src/main/java/com/github/nacabaro/vbhelper/MainActivity.kt index cc8eff4..c8f358b 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/MainActivity.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/MainActivity.kt @@ -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 ) ) } diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/components/TopBanner.kt b/app/src/main/java/com/github/nacabaro/vbhelper/components/TopBanner.kt index b184cee..79c1103 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/components/TopBanner.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/components/TopBanner.kt @@ -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" ) } diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/daos/CardDao.kt b/app/src/main/java/com/github/nacabaro/vbhelper/daos/CardDao.kt index d9c1d3c..6fa7aec 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/daos/CardDao.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/daos/CardDao.kt @@ -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 + + @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 diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/navigation/AppNavigation.kt b/app/src/main/java/com/github/nacabaro/vbhelper/navigation/AppNavigation.kt index ea20f0e..76d9009 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/navigation/AppNavigation.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/navigation/AppNavigation.kt @@ -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() ) diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/DexScreen.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/DexScreen.kt deleted file mode 100644 index 6a99b4f..0000000 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/DexScreen.kt +++ /dev/null @@ -1,83 +0,0 @@ -package com.github.nacabaro.vbhelper.screens - -import androidx.compose.foundation.layout.fillMaxWidth -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.runtime.LaunchedEffect -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope -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.source.DexRepository -import kotlinx.coroutines.launch - -@Composable -fun DexScreen( - navController: NavController -) { - val coroutineScope = rememberCoroutineScope() - val application = LocalContext.current.applicationContext as VBHelper - val dexRepository = DexRepository(application.container.db) - - val cardList = remember { mutableStateOf>(emptyList()) } - - LaunchedEffect(dexRepository) { - coroutineScope.launch { - val newDimList = dexRepository.getAllDims() - cardList.value = newDimList // Replace the entire list atomically - } - } - - Scaffold ( - topBar = { - TopBanner( - text = "Discovered characters", - onGearClick = { - navController.navigate(NavigationItems.Viewer.route) - } - ) - } - ) { contentPadding -> - LazyColumn ( - modifier = Modifier - .padding(top = contentPadding.calculateTopPadding()) - ) { - items(cardList.value) { - DexDiMEntry( - name = it.cardName, - logo = BitmapData( - bitmap = it.cardLogo, - width = it.logoWidth, - height = it.logoHeight - ), - onClick = { - navController - .navigate( - NavigationItems - .CardView.route - .replace("{cardId}", "${it.cardId}") - ) - }, - obtainedCharacters = it.obtainedCharacters, - totalCharacters = it.totalCharacters, - modifier = Modifier - .fillMaxWidth() - .padding(8.dp) - ) - } - } - } -} - diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardEntry.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardEntry.kt new file mode 100644 index 0000000..9d48d5a --- /dev/null +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardEntry.kt @@ -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 diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardScreenController.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardScreenController.kt new file mode 100644 index 0000000..d47d888 --- /dev/null +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardScreenController.kt @@ -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 diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardScreenControllerImpl.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardScreenControllerImpl.kt new file mode 100644 index 0000000..0e54d15 --- /dev/null +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardScreenControllerImpl.kt @@ -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 diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/DimScreen.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardViewScreen.kt similarity index 94% rename from app/src/main/java/com/github/nacabaro/vbhelper/screens/DimScreen.kt rename to app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardViewScreen.kt index 609be95..d042eae 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/DimScreen.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardViewScreen.kt @@ -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 ) { diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardsScreen.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardsScreen.kt new file mode 100644 index 0000000..e639444 --- /dev/null +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardsScreen.kt @@ -0,0 +1,145 @@ +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 +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.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 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>(emptyList()) } + + val selectedCard = remember { mutableStateOf(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 + } + } + + Scaffold ( + topBar = { + TopBanner( + text = "My cards", + onModifyClick = { + modifyCards = !modifyCards + } + ) + } + ) { contentPadding -> + LazyColumn ( + modifier = Modifier + .padding(top = contentPadding.calculateTopPadding()) + ) { + items(cardList.value) { + CardEntry( + name = it.cardName, + logo = BitmapData( + bitmap = it.cardLogo, + width = it.logoWidth, + height = it.logoHeight + ), + onClick = { + navController + .navigate( + NavigationItems + .CardView.route + .replace("{cardId}", "${it.cardId}") + ) + }, + obtainedCharacters = it.obtainedCharacters, + totalCharacters = it.totalCharacters, + modifier = Modifier + .fillMaxWidth() + .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 + } + ) + } + ) + } +} + diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/dialogs/CardDeleteDialog.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/dialogs/CardDeleteDialog.kt new file mode 100644 index 0000000..42797da --- /dev/null +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/dialogs/CardDeleteDialog.kt @@ -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 diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/dialogs/CardRenameDialog.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/dialogs/CardRenameDialog.kt new file mode 100644 index 0000000..e3f9fe2 --- /dev/null +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/dialogs/CardRenameDialog.kt @@ -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 diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/ScanScreen.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/ScanScreen.kt index 8709ae2..a58b2b2 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/ScanScreen.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/ScanScreen.kt @@ -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(null) } + var cardsRead by remember { mutableStateOf?>(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) -> 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, NfcCharacter) -> Unit): String { return "" } override suspend fun characterToNfc(characterId: Long): NfcCharacter? { return null } }, characterId = null, diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/ScanScreenController.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/ScanScreenController.kt index 72032cf..485fab8 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/ScanScreenController.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/ScanScreenController.kt @@ -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 - fun onClickRead(secrets: Secrets, onComplete: ()->Unit) + fun onClickRead(secrets: Secrets, onComplete: ()->Unit, onMultipleCards: (List) -> 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, NfcCharacter) -> Unit + ): String suspend fun characterToNfc(characterId: Long): NfcCharacter? } \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/ScanScreenControllerImpl.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/ScanScreenControllerImpl.kt index 7721ceb..9ce42bc 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/ScanScreenControllerImpl.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/ScanScreenControllerImpl.kt @@ -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) -> 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, 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 diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/cardSelect/ChooseCard.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/cardSelect/ChooseCard.kt new file mode 100644 index 0000000..ace9911 --- /dev/null +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/cardSelect/ChooseCard.kt @@ -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, + 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 diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/components/DexDimEntry.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/cardSelect/ScanCardEntry.kt similarity index 82% rename from app/src/main/java/com/github/nacabaro/vbhelper/components/DexDimEntry.kt rename to app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/cardSelect/ScanCardEntry.kt index 36c4976..d81bb8e 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/components/DexDimEntry.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/cardSelect/ScanCardEntry.kt @@ -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 - ) } } } diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/converters/FromNfcConverter.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/converters/FromNfcConverter.kt index 459afbc..a398aae 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/converters/FromNfcConverter.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/converters/FromNfcConverter.kt @@ -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, 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( diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/converters/ToNfcConverter.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/converters/ToNfcConverter.kt index db0f88d..644e69e 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/converters/ToNfcConverter.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/converters/ToNfcConverter.kt @@ -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 { + val cardData = database + .cardDao() + .getCardByCharacterId(userCharacter.id) + + val appReserved = Array(3) { + 0u + } + + appReserved[0] = cardData.id.toUShort() + + return appReserved + } + + private suspend fun generateSpecialMissionsArray( characterId: Long diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/settingsScreen/SettingsScreenControllerImpl.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/settingsScreen/SettingsScreenControllerImpl.kt index 0e46146..632716d 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/settingsScreen/SettingsScreenControllerImpl.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/settingsScreen/SettingsScreenControllerImpl.kt @@ -129,7 +129,7 @@ class SettingsScreenControllerImpl( val dimId = database .cardDao() - .insertNewDim(cardModel) + .insertNewCard(cardModel) val cardProgress = CardProgress( cardId = dimId, diff --git a/app/src/main/res/drawable/baseline_edit_24.xml b/app/src/main/res/drawable/baseline_edit_24.xml new file mode 100644 index 0000000..9a3ef8b --- /dev/null +++ b/app/src/main/res/drawable/baseline_edit_24.xml @@ -0,0 +1,9 @@ + + +