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 @@ + + +