From dce186737dcdcfc6ca8d3ae5a9d22d938e5af0b1 Mon Sep 17 00:00:00 2001 From: Nacho Date: Sun, 16 Nov 2025 01:34:31 +0100 Subject: [PATCH] Other few things - I changed more things to flows from the database - Cleaned up the logic coming from the scan screen - Added a delete button to a character. CAREFUL, IT HAS NO CONFIRMATION YET! - Fixed a few things, now scanning is more stable and will fix the second whoops thing. - Quick patch, should improve stability when writing to the watch --- .../github/nacabaro/vbhelper/daos/CardDao.kt | 3 +- .../vbhelper/daos/UserCharacterDao.kt | 3 +- .../itemsScreen/ChooseCharacterScreen.kt | 5 +- .../scanScreen/ChooseConnectionScreen.kt | 78 ++++++ .../vbhelper/screens/scanScreen/ScanScreen.kt | 232 ++---------------- .../scanScreen/converters/FromNfcConverter.kt | 7 +- .../scanScreen/converters/ToNfcConverter.kt | 2 + .../ActionScreen.kt} | 9 +- .../scanScreen/screens/ReadCharacterScreen.kt | 59 +++++ .../scanScreen/screens/ReadingScreen.kt | 109 ++++++++ .../scanScreen/screens/WriteCardScreen.kt | 132 ++++++++++ .../screens/WriteCharacterScreen.kt | 135 ++++++++++ .../scanScreen/screens/WritingScreen.kt | 140 +++++++++++ .../screens/storageScreen/StorageDialog.kt | 8 + .../screens/storageScreen/StorageScreen.kt | 31 +-- .../storageScreen/StorageScreenController.kt | 1 + .../StorageScreenControllerImpl.kt | 17 ++ .../vbhelper/source/ScanRepository.kt | 13 + .../vbhelper/source/StorageRepository.kt | 3 +- 19 files changed, 752 insertions(+), 235 deletions(-) create mode 100644 app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/ChooseConnectionScreen.kt rename app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/{ReadingCharacter.kt => screens/ActionScreen.kt} (85%) create mode 100644 app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/screens/ReadCharacterScreen.kt create mode 100644 app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/screens/ReadingScreen.kt create mode 100644 app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/screens/WriteCardScreen.kt create mode 100644 app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/screens/WriteCharacterScreen.kt create mode 100644 app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/screens/WritingScreen.kt create mode 100644 app/src/main/java/com/github/nacabaro/vbhelper/source/ScanRepository.kt 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 0401f9e..e9511f2 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 @@ -5,6 +5,7 @@ import androidx.room.Insert import androidx.room.OnConflictStrategy import androidx.room.Query import com.github.nacabaro.vbhelper.domain.card.Card +import kotlinx.coroutines.flow.Flow @Dao interface CardDao { @@ -26,7 +27,7 @@ interface CardDao { WHERE uc.id = :id """ ) - suspend fun getCardByCharacterId(id: Long): Card + fun getCardByCharacterId(id: Long): Flow @Query("UPDATE Card SET name = :newName WHERE id = :id") suspend fun renameCard(id: Int, newName: String) diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/daos/UserCharacterDao.kt b/app/src/main/java/com/github/nacabaro/vbhelper/daos/UserCharacterDao.kt index 42fb503..02f9293 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/daos/UserCharacterDao.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/daos/UserCharacterDao.kt @@ -13,6 +13,7 @@ import com.github.nacabaro.vbhelper.domain.device_data.TransformationHistory import com.github.nacabaro.vbhelper.domain.device_data.VBCharacterData import com.github.nacabaro.vbhelper.domain.device_data.VitalsHistory import com.github.nacabaro.vbhelper.dtos.CharacterDtos +import kotlinx.coroutines.flow.Flow @Dao interface UserCharacterDao { @@ -76,7 +77,7 @@ interface UserCharacterDao { LEFT JOIN Adventure a ON a.characterId = uc.id """ ) - suspend fun getAllCharacters(): List + fun getAllCharacters(): Flow> @Query( """ diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/itemsScreen/ChooseCharacterScreen.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/itemsScreen/ChooseCharacterScreen.kt index 26be571..5b2b884 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/itemsScreen/ChooseCharacterScreen.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/itemsScreen/ChooseCharacterScreen.kt @@ -24,6 +24,7 @@ import com.github.nacabaro.vbhelper.dtos.CharacterDtos import com.github.nacabaro.vbhelper.dtos.ItemDtos import com.github.nacabaro.vbhelper.source.StorageRepository import com.github.nacabaro.vbhelper.utils.BitmapData +import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch @@ -35,7 +36,7 @@ fun ChooseCharacterScreen( ) { val coroutineScope = rememberCoroutineScope() val application = LocalContext.current.applicationContext as VBHelper - val storageRepository = StorageRepository(application.container.db) + val storageRepository = StorageRepository(application.container.db, ) val characterList = remember { mutableStateOf>(emptyList()) } @@ -57,7 +58,7 @@ fun ChooseCharacterScreen( characterList.value = storageRepository.getVBCharacters() } else -> { - characterList.value = storageRepository.getAllCharacters() + characterList.value = storageRepository.getAllCharacters().first() } } } diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/ChooseConnectionScreen.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/ChooseConnectionScreen.kt new file mode 100644 index 0000000..34180ba --- /dev/null +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/ChooseConnectionScreen.kt @@ -0,0 +1,78 @@ +package com.github.nacabaro.vbhelper.screens.scanScreen + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Button +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.navigation.NavController +import com.github.nacabaro.vbhelper.components.TopBanner + +@Composable +fun ChooseConnectOption( + onClickRead: (() -> Unit)? = null, + onClickWrite: (() -> Unit)? = null, + navController: NavController +) { + Scaffold( + topBar = { + TopBanner( + text = "Scan a Vital Bracelet", + onBackClick = { + navController.popBackStack() + } + ) + } + ) { contentPadding -> + Column( + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier + .fillMaxSize() + .padding(contentPadding) + ) { + ScanButton( + text = "Vital Bracelet to App", + disabled = onClickRead == null, + onClick = onClickRead?: { }, + ) + Spacer(modifier = Modifier.height(16.dp)) + ScanButton( + text = "App to Vital Bracelet", + disabled = onClickWrite == null, + onClick = onClickWrite?: { }, + ) + } + } +} + + +@Composable +fun ScanButton( + text: String, + onClick: () -> Unit, + modifier: Modifier = Modifier, + disabled: Boolean = false, +) { + Button( + onClick = onClick, + modifier = modifier, + enabled = !disabled, + ) { + Text( + text = text, + fontSize = 16.sp, + modifier = Modifier + .padding(4.dp) + ) + } +} \ 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 a58b2b2..8dec3b6 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 @@ -1,38 +1,24 @@ package com.github.nacabaro.vbhelper.screens.scanScreen import android.widget.Toast -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.material3.Button -import androidx.compose.material3.Scaffold -import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp import androidx.navigation.NavController import androidx.navigation.compose.rememberNavController 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.screens.scanScreen.screens.ReadingScreen +import com.github.nacabaro.vbhelper.screens.scanScreen.screens.WritingScreen import com.github.nacabaro.vbhelper.source.StorageRepository import com.github.nacabaro.vbhelper.source.isMissingSecrets import com.github.nacabaro.vbhelper.source.proto.Secrets @@ -55,8 +41,6 @@ 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) { @@ -73,143 +57,33 @@ 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) } + var readingScreen by remember { mutableStateOf(false) } - DisposableEffect(readingScreen) { - if(readingScreen) { - scanScreenController.registerActivityLifecycleListener( - SCAN_SCREEN_ACTIVITY_LIFECYCLE_LISTENER, - object: ActivityLifecycleListener { - override fun onPause() { - scanScreenController.cancelRead() - } - - override fun onResume() { - scanScreenController.onClickRead( - secrets = secrets!!, - onComplete = { - isDoneReadingCharacter = true - }, - onMultipleCards = { cards -> - cardsRead = cards - readingScreen = false - cardSelectScreen = true - isDoneReadingCharacter = true - } - ) - } - } - ) - scanScreenController.onClickRead( - secrets = secrets!!, - onComplete = { - isDoneReadingCharacter = true - }, - onMultipleCards = { cards -> - cardsRead = cards - readingScreen = false - cardSelectScreen = true - isDoneReadingCharacter = true - } - ) - } - onDispose { - if(readingScreen) { - scanScreenController.unregisterActivityLifecycleListener( - SCAN_SCREEN_ACTIVITY_LIFECYCLE_LISTENER - ) - scanScreenController.cancelRead() - } - } - } - - DisposableEffect(writingScreen, isDoneSendingCard) { - if (writingScreen) { - scanScreenController.registerActivityLifecycleListener( - SCAN_SCREEN_ACTIVITY_LIFECYCLE_LISTENER, - object : ActivityLifecycleListener { - override fun onPause() { - scanScreenController.cancelRead() - } - - override fun onResume() { - if (!isDoneSendingCard) { - scanScreenController.onClickCheckCard(secrets!!, nfcCharacter!!) { - isDoneSendingCard = true - } - } else if (!isDoneWritingCharacter) { - scanScreenController.onClickWrite(secrets!!, nfcCharacter!!) { - isDoneWritingCharacter = true - } - } - } - } - ) - } - - if (secrets != null && nfcCharacter != null) { - if (!isDoneSendingCard) { - scanScreenController.onClickCheckCard(secrets!!, nfcCharacter!!) { - isDoneSendingCard = true - } - } else if (!isDoneWritingCharacter) { - scanScreenController.onClickWrite(secrets!!, nfcCharacter!!) { - isDoneWritingCharacter = true - } - } - } - - onDispose { - if(writingScreen) { - scanScreenController.unregisterActivityLifecycleListener(SCAN_SCREEN_ACTIVITY_LIFECYCLE_LISTENER) - scanScreenController.cancelRead() - } - } - } - - if (isDoneReadingCharacter && !cardSelectScreen) { - readingScreen = false - navController.navigate(NavigationItems.Home.route) - } else if (isDoneSendingCard && isDoneWritingCharacter) { - writingScreen = false - navController.navigate(NavigationItems.Home.route) - LaunchedEffect(storageRepository) { - withContext(Dispatchers.IO) { - storageRepository - .deleteCharacter(characterId!!) - } - } - } - - if (readingScreen) { - ReadingCharacterScreen("Reading character") { - readingScreen = false - scanScreenController.cancelRead() - } - } else if (writingScreen) { - if (!isDoneSendingCard) { - ReadingCharacterScreen("Sending card") { + if (writingScreen && nfcCharacter != null && characterId != null) { + WritingScreen( + scanScreenController = scanScreenController, + nfcCharacter = nfcCharacter!!, + characterId = characterId, + onComplete = { writingScreen = false - scanScreenController.cancelRead() - } - } else if (!isDoneWritingCharacter) { - ReadingCharacterScreen("Writing character") { - isDoneSendingCard = false + navController.navigate(NavigationItems.Home.route) + }, + onCancel = { writingScreen = false - scanScreenController.cancelRead() + navController.navigate(NavigationItems.Home.route) } - } - } else if (cardSelectScreen) { - ChooseCard( - cards = cardsRead!!, - onCardSelected = { card -> - cardSelectScreen = false - scanScreenController.flushCharacter(card.id) + ) + } else if (readingScreen) { + ReadingScreen( + scanScreenController = scanScreenController, + onCancel = { + readingScreen = false + navController.navigate(NavigationItems.Home.route) + }, + onComplete = { + readingScreen = false + navController.navigate(NavigationItems.Home.route) } ) } else { @@ -247,66 +121,8 @@ fun ScanScreen( } } -@Composable -fun ChooseConnectOption( - onClickRead: (() -> Unit)? = null, - onClickWrite: (() -> Unit)? = null, - navController: NavController -) { - Scaffold( - topBar = { - TopBanner( - text = "Scan a Vital Bracelet", - onBackClick = { - navController.popBackStack() - } - ) - } - ) { contentPadding -> - Column( - verticalArrangement = Arrangement.Center, - horizontalAlignment = Alignment.CenterHorizontally, - modifier = Modifier - .fillMaxSize() - .padding(contentPadding) - ) { - ScanButton( - text = "Vital Bracelet to App", - disabled = onClickRead == null, - onClick = onClickRead?: { }, - ) - Spacer(modifier = Modifier.height(16.dp)) - ScanButton( - text = "App to Vital Bracelet", - disabled = onClickWrite == null, - onClick = onClickWrite?: { }, - ) - } - } -} -@Composable -fun ScanButton( - text: String, - onClick: () -> Unit, - modifier: Modifier = Modifier, - disabled: Boolean = false, -) { - Button( - onClick = onClick, - modifier = modifier, - enabled = !disabled, - ) { - Text( - text = text, - fontSize = 16.sp, - modifier = Modifier - .padding(4.dp) - ) - } -} - @Preview(showBackground = true) @Composable fun ScanScreenPreview() { 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 7acb137..dcc85a3 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 @@ -1,13 +1,11 @@ package com.github.nacabaro.vbhelper.screens.scanScreen.converters -import android.util.Log import androidx.activity.ComponentActivity 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.di.VBHelper import com.github.nacabaro.vbhelper.domain.card.Card -import com.github.nacabaro.vbhelper.domain.card.CardProgress import com.github.nacabaro.vbhelper.domain.device_data.BECharacterData import com.github.nacabaro.vbhelper.domain.device_data.SpecialMissions import com.github.nacabaro.vbhelper.domain.device_data.UserCharacter @@ -250,10 +248,11 @@ class FromNfcConverter ( ) { val vitalsHistoryWatch = nfcCharacter.vitalHistory val vitalsHistory = vitalsHistoryWatch.map { historyElement -> - Log.d("VitalsHistory", "${historyElement.year.toInt()} ${historyElement.month.toInt()} ${historyElement.day.toInt()}") + val year = if (historyElement.year.toInt() !in 2021 .. 2035) 0 else historyElement.year.toInt() + VitalsHistory( charId = characterId, - year = historyElement.year.toInt(), + year = year, month = historyElement.month.toInt(), day = historyElement.day.toInt(), vitalPoints = historyElement.vitalsGained.toInt() 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 dbc58fc..2078238 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 @@ -14,6 +14,7 @@ import com.github.nacabaro.vbhelper.di.VBHelper import com.github.nacabaro.vbhelper.domain.device_data.UserCharacter import com.github.nacabaro.vbhelper.dtos.CharacterDtos import com.github.nacabaro.vbhelper.utils.DeviceType +import kotlinx.coroutines.flow.first import java.util.Date class ToNfcConverter( @@ -96,6 +97,7 @@ class ToNfcConverter( val cardData = database .cardDao() .getCardByCharacterId(userCharacter.id) + .first() val appReserved = Array(3) { 0u diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/ReadingCharacter.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/screens/ActionScreen.kt similarity index 85% rename from app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/ReadingCharacter.kt rename to app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/screens/ActionScreen.kt index 89ce2ed..8d216f2 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/ReadingCharacter.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/screens/ActionScreen.kt @@ -1,4 +1,4 @@ -package com.github.nacabaro.vbhelper.screens.scanScreen +package com.github.nacabaro.vbhelper.screens.scanScreen.screens import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -14,13 +14,16 @@ import androidx.compose.ui.unit.dp import com.github.nacabaro.vbhelper.components.TopBanner @Composable -fun ReadingCharacterScreen( +fun ActionScreen( topBannerText: String, onClickCancel: () -> Unit, ) { Scaffold ( topBar = { - TopBanner(topBannerText) + TopBanner( + text = topBannerText, + onBackClick = onClickCancel + ) } ) { innerPadding -> Column ( diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/screens/ReadCharacterScreen.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/screens/ReadCharacterScreen.kt new file mode 100644 index 0000000..757cc49 --- /dev/null +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/screens/ReadCharacterScreen.kt @@ -0,0 +1,59 @@ +package com.github.nacabaro.vbhelper.screens.scanScreen.screens + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Button +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import com.github.nacabaro.vbhelper.components.TopBanner + +@Composable +fun ReadCharacterScreen( + onClickCancel: () -> Unit, + onClickConfirm: () -> Unit +) { + Scaffold( + topBar = { + TopBanner( + text = "Read character", + onBackClick = onClickCancel + ) + } + ) { innerPadding -> + Column( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center, + modifier = Modifier + .padding(innerPadding) + .fillMaxSize() + ) { + Text( + text = "Prepare your device!", + textAlign = TextAlign.Center + ) + + Text( + text = "Go to connect and when ready press confirm!", + textAlign = TextAlign.Center + ) + + Spacer( + modifier = Modifier.padding(8.dp) + ) + + Button( + onClick = onClickConfirm, + ) { + Text("Confirm") + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/screens/ReadingScreen.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/screens/ReadingScreen.kt new file mode 100644 index 0000000..58432ce --- /dev/null +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/screens/ReadingScreen.kt @@ -0,0 +1,109 @@ +package com.github.nacabaro.vbhelper.screens.scanScreen.screens + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import com.github.nacabaro.vbhelper.ActivityLifecycleListener +import com.github.nacabaro.vbhelper.domain.card.Card +import com.github.nacabaro.vbhelper.screens.cardScreen.ChooseCard +import com.github.nacabaro.vbhelper.screens.scanScreen.SCAN_SCREEN_ACTIVITY_LIFECYCLE_LISTENER +import com.github.nacabaro.vbhelper.screens.scanScreen.ScanScreenController + +@Composable +fun ReadingScreen( + scanScreenController: ScanScreenController, + onCancel: () -> Unit, + onComplete: () -> Unit +) { + val secrets by scanScreenController.secretsFlow.collectAsState(null) + + var cardsRead by remember { mutableStateOf?>(null) } + + var readingScreen by remember { mutableStateOf(false) } + var isDoneReadingCharacter by remember { mutableStateOf(false) } + var cardSelectScreen by remember { mutableStateOf(false) } + + DisposableEffect(readingScreen) { + if(readingScreen) { + scanScreenController.registerActivityLifecycleListener( + SCAN_SCREEN_ACTIVITY_LIFECYCLE_LISTENER, + object: ActivityLifecycleListener { + override fun onPause() { + scanScreenController.cancelRead() + } + + override fun onResume() { + scanScreenController.onClickRead( + secrets = secrets!!, + onComplete = { + isDoneReadingCharacter = true + }, + onMultipleCards = { cards -> + cardsRead = cards + readingScreen = false + cardSelectScreen = true + isDoneReadingCharacter = true + } + ) + } + } + ) + scanScreenController.onClickRead( + secrets = secrets!!, + onComplete = { + isDoneReadingCharacter = true + }, + onMultipleCards = { cards -> + cardsRead = cards + readingScreen = false + cardSelectScreen = true + isDoneReadingCharacter = true + } + ) + } + onDispose { + if(readingScreen) { + scanScreenController.unregisterActivityLifecycleListener( + SCAN_SCREEN_ACTIVITY_LIFECYCLE_LISTENER + ) + scanScreenController.cancelRead() + } + } + } + + if (isDoneReadingCharacter && !cardSelectScreen) { + readingScreen = false + onComplete() + } + + if (!readingScreen) { + ReadCharacterScreen( + onClickConfirm = { + readingScreen = true + }, + onClickCancel = { + onCancel() + } + ) + } + + if (readingScreen) { + ActionScreen("Reading character") { + readingScreen = false + scanScreenController.cancelRead() + onCancel() + } + } else if (cardSelectScreen) { + ChooseCard( + cards = cardsRead!!, + onCardSelected = { card -> + cardSelectScreen = false + scanScreenController.flushCharacter(card.id) + } + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/screens/WriteCardScreen.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/screens/WriteCardScreen.kt new file mode 100644 index 0000000..db47745 --- /dev/null +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/screens/WriteCardScreen.kt @@ -0,0 +1,132 @@ +package com.github.nacabaro.vbhelper.screens.scanScreen.screens + +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.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.material3.Button +import androidx.compose.material3.Card +import androidx.compose.material3.CardColors +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.material3.contentColorFor +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.FilterQuality +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.unit.dp +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.source.ScanRepository +import com.github.nacabaro.vbhelper.utils.BitmapData +import com.github.nacabaro.vbhelper.utils.getImageBitmap + +@Composable +fun WriteCardScreen( + characterId: Long, + onClickCancel: () -> Unit, + onClickConfirm: () -> Unit +) { + val application = LocalContext.current.applicationContext as VBHelper + val database = application.container.db + val scanRepository = ScanRepository(database) + val cardDetails by scanRepository.getCardDetails(characterId).collectAsState(Card( + id = 0, + cardId = 0, + name = "", + logo = byteArrayOf(), + logoHeight = 0, + logoWidth = 0, + stageCount = 0, + isBEm = false + )) + + Scaffold( + topBar = { + TopBanner( + text = "Writing card details", + onBackClick = onClickCancel + ) + } + ) { innerPadding -> + Column( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center, + modifier = Modifier + .padding(innerPadding) + .fillMaxSize() + ) { + Card ( + modifier = Modifier + .fillMaxWidth() + .padding(8.dp) + ) { + Row ( + modifier = Modifier.padding(16.dp), + ){ + if (cardDetails.logoHeight > 0 && cardDetails.logoWidth > 0) { + val charaBitmapData = BitmapData( + bitmap = cardDetails.logo, + width = cardDetails.logoWidth, + height = cardDetails.logoHeight + ) + val charaImageBitmapData = charaBitmapData.getImageBitmap( + context = LocalContext.current, + multiplier = 4, + obscure = false + ) + + Card ( + colors = CardColors( + containerColor = MaterialTheme.colorScheme.surfaceVariant, + contentColor = MaterialTheme.colorScheme.contentColorFor( + backgroundColor = MaterialTheme.colorScheme.surfaceVariant, + ), + disabledContainerColor = MaterialTheme.colorScheme.surfaceVariant, + disabledContentColor = MaterialTheme.colorScheme.contentColorFor( + backgroundColor = MaterialTheme.colorScheme.surfaceVariant, + ) + ) + ) { + Image( + bitmap = charaImageBitmapData.imageBitmap, + contentDescription = "Icon", + modifier = Modifier + .size(charaImageBitmapData.dpWidth) + .padding(8.dp), + filterQuality = FilterQuality.None + ) + } + } + + Spacer( + modifier = Modifier.width(8.dp) + ) + + Column { + Text("Get your device Ready!") + Text("You will need ${cardDetails.name} card!") + } + } + + } + + Button( + onClick = onClickConfirm, + ) { + Text("Confirm") + } + } + } +} diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/screens/WriteCharacterScreen.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/screens/WriteCharacterScreen.kt new file mode 100644 index 0000000..abd2606 --- /dev/null +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/screens/WriteCharacterScreen.kt @@ -0,0 +1,135 @@ +package com.github.nacabaro.vbhelper.screens.scanScreen.screens + +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.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.material3.Button +import androidx.compose.material3.Card +import androidx.compose.material3.CardColors +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.material3.contentColorFor +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.FilterQuality +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.unit.dp +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.source.ScanRepository +import com.github.nacabaro.vbhelper.utils.BitmapData +import com.github.nacabaro.vbhelper.utils.getImageBitmap + +@Composable +fun WriteCharacterScreen( + characterId: Long, + onClickCancel: () -> Unit, + onClickConfirm: () -> Unit +) { + val application = LocalContext.current.applicationContext as VBHelper + val database = application.container.db + val scanRepository = ScanRepository(database) + val cardDetails by scanRepository.getCardDetails(characterId).collectAsState(Card( + id = 0, + cardId = 0, + name = "", + logo = byteArrayOf(), + logoHeight = 0, + logoWidth = 0, + stageCount = 0, + isBEm = false + )) + + Scaffold( + topBar = { + TopBanner( + text = "Writing character", + onBackClick = onClickCancel + ) + } + ) { innerPadding -> + Column( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center, + modifier = Modifier + .padding(innerPadding) + .fillMaxSize() + ) { + Card ( + modifier = Modifier + .fillMaxWidth() + .padding(8.dp) + ) { + Row ( + modifier = Modifier.padding(16.dp), + ){ + if (cardDetails.logoHeight > 0 && cardDetails.logoWidth > 0) { + val charaBitmapData = BitmapData( + bitmap = cardDetails.logo, + width = cardDetails.logoWidth, + height = cardDetails.logoHeight + ) + val charaImageBitmapData = charaBitmapData.getImageBitmap( + context = LocalContext.current, + multiplier = 4, + obscure = false + ) + + Card ( + colors = CardColors( + containerColor = MaterialTheme.colorScheme.surfaceVariant, + contentColor = MaterialTheme.colorScheme.contentColorFor( + backgroundColor = MaterialTheme.colorScheme.surfaceVariant, + ), + disabledContainerColor = MaterialTheme.colorScheme.surfaceVariant, + disabledContentColor = MaterialTheme.colorScheme.contentColorFor( + backgroundColor = MaterialTheme.colorScheme.surfaceVariant, + ) + ) + ) { + Image( + bitmap = charaImageBitmapData.imageBitmap, + contentDescription = "Icon", + modifier = Modifier + .size(charaImageBitmapData.dpWidth) + .padding(8.dp), + filterQuality = FilterQuality.None + ) + } + } + + Spacer( + modifier = Modifier.width(8.dp) + ) + + Column { + Text("Card installed successfully!!") + Text("Wait until your device is ready, then tap 'Confirm'") + } + } + + } + + Spacer(modifier = Modifier.height(8.dp)) + + Button( + onClick = onClickConfirm, + ) { + Text("Confirm") + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/screens/WritingScreen.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/screens/WritingScreen.kt new file mode 100644 index 0000000..9680f41 --- /dev/null +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/screens/WritingScreen.kt @@ -0,0 +1,140 @@ +package com.github.nacabaro.vbhelper.screens.scanScreen.screens + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.platform.LocalContext +import com.github.cfogrady.vbnfc.data.NfcCharacter +import com.github.nacabaro.vbhelper.ActivityLifecycleListener +import com.github.nacabaro.vbhelper.di.VBHelper +import com.github.nacabaro.vbhelper.screens.scanScreen.SCAN_SCREEN_ACTIVITY_LIFECYCLE_LISTENER +import com.github.nacabaro.vbhelper.screens.scanScreen.ScanScreenController +import com.github.nacabaro.vbhelper.source.StorageRepository +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext + +@Composable +fun WritingScreen( + scanScreenController: ScanScreenController, + characterId: Long, + nfcCharacter: NfcCharacter, + onComplete: () -> Unit, + onCancel: () -> Unit, +) { + val secrets by scanScreenController.secretsFlow.collectAsState(null) + + val application = LocalContext.current.applicationContext as VBHelper + val storageRepository = StorageRepository(application.container.db) + + var writing by remember { mutableStateOf(false) } + var writingScreen by remember { mutableStateOf(false) } + var writingConfirmScreen by remember { mutableStateOf(false) } + var isDoneSendingCard by remember { mutableStateOf(false) } + var isDoneWritingCharacter by remember { mutableStateOf(false) } + + DisposableEffect(writing) { + if (writing) { + scanScreenController.registerActivityLifecycleListener( + SCAN_SCREEN_ACTIVITY_LIFECYCLE_LISTENER, + object : ActivityLifecycleListener { + override fun onPause() { + scanScreenController.cancelRead() + } + + override fun onResume() { + if (!isDoneSendingCard) { + scanScreenController.onClickCheckCard(secrets!!, nfcCharacter) { + isDoneSendingCard = true + } + } else if (!isDoneWritingCharacter) { + scanScreenController.onClickWrite(secrets!!, nfcCharacter) { + isDoneWritingCharacter = true + } + } + } + } + ) + + if (secrets != null) { + if (!isDoneSendingCard) { + scanScreenController.onClickCheckCard(secrets!!, nfcCharacter) { + isDoneSendingCard = true + } + } else if (!isDoneWritingCharacter) { + scanScreenController.onClickWrite(secrets!!, nfcCharacter) { + isDoneWritingCharacter = true + } + } + } + } + + onDispose { + if (writing) { + scanScreenController.unregisterActivityLifecycleListener( + SCAN_SCREEN_ACTIVITY_LIFECYCLE_LISTENER + ) + scanScreenController.cancelRead() + } + } + } + + if (!writingScreen) { + writing = false + WriteCardScreen ( + characterId = characterId, + onClickCancel = { + scanScreenController.cancelRead() + onCancel() + }, + onClickConfirm = { + writingScreen = true + } + ) + } else if (!isDoneSendingCard) { + writing = true + ActionScreen("Sending card") { + scanScreenController.cancelRead() + onCancel() + } + } else if (!writingConfirmScreen) { + writing = false + WriteCharacterScreen ( + characterId = characterId, + onClickCancel = { + scanScreenController.cancelRead() + onCancel() + }, + onClickConfirm = { + writingConfirmScreen = true + } + ) + } else if (!isDoneWritingCharacter) { + writing = true + ActionScreen("Writing character") { + isDoneSendingCard = false + scanScreenController.cancelRead() + onCancel() + } + } + + var completedWriting by remember { mutableStateOf(false) } + + LaunchedEffect(isDoneSendingCard, isDoneWritingCharacter) { + withContext(Dispatchers.IO) { + if (isDoneSendingCard && isDoneWritingCharacter) { + storageRepository + .deleteCharacter(characterId) + completedWriting = true + } + } + } + + if (completedWriting) { + onComplete() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/storageScreen/StorageDialog.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/storageScreen/StorageDialog.kt index 9b0b1db..eebb1e6 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/storageScreen/StorageDialog.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/storageScreen/StorageDialog.kt @@ -38,6 +38,7 @@ import kotlinx.coroutines.launch fun StorageDialog( characterId: Long, onDismissRequest: () -> Unit, + onClickDelete: () -> Unit, onSendToBracelet: () -> Unit, onClickSetActive: () -> Unit, onClickSendToAdventure: (time: Long) -> Unit @@ -141,6 +142,13 @@ fun StorageDialog( ) { Text(text = "Send on adventure") } + Button( + modifier = Modifier + .fillMaxWidth(), + onClick = onClickDelete + ) { + Text(text = "Delete character") + } Button( modifier = Modifier .fillMaxWidth(), diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/storageScreen/StorageScreen.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/storageScreen/StorageScreen.kt index b5736bb..b57a542 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/storageScreen/StorageScreen.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/storageScreen/StorageScreen.kt @@ -1,5 +1,6 @@ package com.github.nacabaro.vbhelper.screens.storageScreen +import android.util.Log import android.widget.Toast import androidx.compose.foundation.gestures.Orientation import androidx.compose.foundation.gestures.scrollable @@ -14,11 +15,10 @@ import androidx.compose.foundation.rememberScrollState import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState 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.Alignment import androidx.compose.ui.Modifier @@ -28,12 +28,10 @@ import androidx.navigation.NavController import com.github.nacabaro.vbhelper.components.CharacterEntry import com.github.nacabaro.vbhelper.components.TopBanner import com.github.nacabaro.vbhelper.di.VBHelper -import com.github.nacabaro.vbhelper.dtos.CharacterDtos import com.github.nacabaro.vbhelper.navigation.NavigationItems import com.github.nacabaro.vbhelper.screens.adventureScreen.AdventureScreenControllerImpl import com.github.nacabaro.vbhelper.source.StorageRepository import com.github.nacabaro.vbhelper.utils.BitmapData -import kotlinx.coroutines.launch @Composable @@ -42,18 +40,11 @@ fun StorageScreen( storageScreenController: StorageScreenControllerImpl, adventureScreenController: AdventureScreenControllerImpl ) { - val coroutineScope = rememberCoroutineScope() val application = LocalContext.current.applicationContext as VBHelper val storageRepository = StorageRepository(application.container.db) - val monList = remember { mutableStateOf>(emptyList()) } - var selectedCharacter by remember { mutableStateOf(null) } + val characterList by storageRepository.getAllCharacters().collectAsState(initial = emptyList()) - LaunchedEffect(storageRepository, selectedCharacter) { - coroutineScope.launch { - val characterList = storageRepository.getAllCharacters() - monList.value = characterList - } - } + var selectedCharacter by remember { mutableStateOf(null) } Scaffold ( topBar = { @@ -65,7 +56,7 @@ fun StorageScreen( ) } ) { contentPadding -> - if (monList.value.isEmpty()) { + if (characterList.isEmpty()) { Column ( horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center, @@ -86,7 +77,7 @@ fun StorageScreen( .scrollable(state = rememberScrollState(), orientation = Orientation.Vertical) .padding(top = contentPadding.calculateTopPadding()) ) { - items(monList.value) { index -> + items(characterList) { index -> CharacterEntry( icon = BitmapData( bitmap = index.spriteIdle, @@ -138,6 +129,16 @@ fun StorageScreen( timeInMinutes = time ) selectedCharacter = null + }, + onClickDelete = { + storageScreenController + .deleteCharacter( + characterId = selectedCharacter!!, + onCompletion = { + Log.d("StorageScreen", "Character deleted") + } + ) + selectedCharacter = null } ) } diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/storageScreen/StorageScreenController.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/storageScreen/StorageScreenController.kt index b958c18..a37cbb0 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/storageScreen/StorageScreenController.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/storageScreen/StorageScreenController.kt @@ -2,4 +2,5 @@ package com.github.nacabaro.vbhelper.screens.storageScreen interface StorageScreenController { fun setActive(characterId: Long, onCompletion: () -> Unit) + fun deleteCharacter(characterId: Long, onCompletion: () -> Unit) } \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/storageScreen/StorageScreenControllerImpl.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/storageScreen/StorageScreenControllerImpl.kt index 4473d4d..3eaf31d 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/storageScreen/StorageScreenControllerImpl.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/storageScreen/StorageScreenControllerImpl.kt @@ -28,4 +28,21 @@ class StorageScreenControllerImpl( } } } + + override fun deleteCharacter(characterId: Long, onCompletion: () -> Unit) { + componentActivity.lifecycleScope.launch(Dispatchers.IO) { + database + .userCharacterDao() + .deleteCharacterById(characterId) + + componentActivity.runOnUiThread { + Toast.makeText( + componentActivity, + "Character deleted!", + Toast.LENGTH_SHORT + ).show() + onCompletion() + } + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/source/ScanRepository.kt b/app/src/main/java/com/github/nacabaro/vbhelper/source/ScanRepository.kt new file mode 100644 index 0000000..4bc05f4 --- /dev/null +++ b/app/src/main/java/com/github/nacabaro/vbhelper/source/ScanRepository.kt @@ -0,0 +1,13 @@ +package com.github.nacabaro.vbhelper.source + +import com.github.nacabaro.vbhelper.database.AppDatabase +import com.github.nacabaro.vbhelper.domain.card.Card +import kotlinx.coroutines.flow.Flow + +class ScanRepository( + val database: AppDatabase +) { + fun getCardDetails(characterId: Long): Flow { + return database.cardDao().getCardByCharacterId(characterId) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/source/StorageRepository.kt b/app/src/main/java/com/github/nacabaro/vbhelper/source/StorageRepository.kt index 61aff4a..2ad6da8 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/source/StorageRepository.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/source/StorageRepository.kt @@ -6,11 +6,12 @@ import com.github.nacabaro.vbhelper.domain.device_data.SpecialMissions import com.github.nacabaro.vbhelper.domain.device_data.VBCharacterData import com.github.nacabaro.vbhelper.dtos.CharacterDtos import com.github.nacabaro.vbhelper.dtos.ItemDtos +import kotlinx.coroutines.flow.Flow class StorageRepository ( private val db: AppDatabase ) { - suspend fun getAllCharacters(): List { + fun getAllCharacters(): Flow> { return db.userCharacterDao().getAllCharacters() }