Compare commits

..
Author SHA1 Message Date
nacabaro 489e27b038 More things
- Active characters display different
- Adventure missions award credits (same as special missions)
- Clicking on about will open the github page
- Corrected from arean to arena
- Removed rename from the settings (it was not used)
2025-11-16 21:59:08 +00:00
nacabaro 6be167bbed Might be incomplete uhhh 2025-11-16 21:13:28 +01:00
nacabaroandGitHub 61dfc2ce0d Merge pull request #43 from nacabaro/screen/upload_screen
Other few things
2025-11-16 01:35:31 +01:00
nacabaro dce186737d 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
2025-11-16 01:34:31 +01:00
nacabaroandGitHub cf272e8030 Merge pull request #42 from nacabaro/dex/possible_fusions
Few things
2025-11-15 20:12:36 +01:00
nacabaroandGitHub 1d9879fb50 Merge pull request #41 from nacabaro/store/updated_store
Updated the store
2025-11-15 10:23:07 +01:00
32 changed files with 847 additions and 275 deletions
+1 -1
View File
@@ -15,7 +15,7 @@ android {
minSdk = 28
targetSdk = 35
versionCode = 1
versionName = "Alpha 0.6.1"
versionName = "Alpha 0.6.2"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
@@ -12,6 +12,7 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.Card
import androidx.compose.material3.CardColors
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
@@ -44,6 +45,9 @@ fun CharacterEntry(
disabled: Boolean = false,
shape: Shape = MaterialTheme.shapes.medium,
multiplier: Int = 4,
cardColors: CardColors = CardDefaults.cardColors(
containerColor = MaterialTheme.colorScheme.surfaceContainerHighest
),
onClick: () -> Unit = { }
) {
val bitmap = remember (icon.bitmap) {
@@ -61,7 +65,8 @@ fun CharacterEntry(
},
modifier = modifier
.aspectRatio(1f)
.padding(8.dp)
.padding(8.dp),
colors = cardColors
) {
Box(
contentAlignment = Alignment.BottomCenter,
@@ -3,6 +3,7 @@ package com.github.nacabaro.vbhelper.daos
import androidx.room.Dao
import androidx.room.Query
import com.github.nacabaro.vbhelper.dtos.CharacterDtos
import kotlinx.coroutines.flow.Flow
@Dao
@@ -37,7 +38,7 @@ interface AdventureDao {
JOIN Adventure a ON uc.id = a.characterId
"""
)
suspend fun getAdventureCharacters(): List<CharacterDtos.AdventureCharacterWithSprites>
fun getAdventureCharacters(): Flow<List<CharacterDtos.AdventureCharacterWithSprites>>
@Query("""
DELETE FROM Adventure
@@ -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<Card>
@Query("UPDATE Card SET name = :newName WHERE id = :id")
suspend fun renameCard(id: Int, newName: String)
@@ -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 {
@@ -68,7 +69,8 @@ interface UserCharacterDao {
c.nameWidth as nameSpriteWidth,
c.nameHeight as nameSpriteHeight,
d.isBEm as isBemCard,
a.characterId = uc.id as isInAdventure
a.characterId = uc.id as isInAdventure,
uc.isActive as active
FROM UserCharacter uc
JOIN CardCharacter c ON uc.charId = c.id
JOIN Card d ON d.id = c.cardId
@@ -76,7 +78,7 @@ interface UserCharacterDao {
LEFT JOIN Adventure a ON a.characterId = uc.id
"""
)
suspend fun getAllCharacters(): List<CharacterDtos.CharacterWithSprites>
fun getAllCharacters(): Flow<List<CharacterDtos.CharacterWithSprites>>
@Query(
"""
@@ -92,7 +94,8 @@ interface UserCharacterDao {
c.nameWidth as nameSpriteWidth,
c.nameHeight as nameSpriteHeight,
d.isBEm as isBemCard,
a.characterId = uc.id as isInAdventure
a.characterId = uc.id as isInAdventure,
uc.isActive as active
FROM UserCharacter uc
JOIN CardCharacter c ON uc.charId = c.id
JOIN Card d ON c.cardId = d.id
@@ -129,7 +132,8 @@ interface UserCharacterDao {
c.nameWidth as nameSpriteWidth,
c.nameHeight as nameSpriteHeight,
d.isBEm as isBemCard,
a.characterId as isInAdventure
a.characterId as isInAdventure,
uc.isActive as active
FROM UserCharacter uc
JOIN CardCharacter c ON uc.charId = c.id
JOIN Card d ON c.cardId = d.id
@@ -193,7 +197,8 @@ interface UserCharacterDao {
c.nameWidth as nameSpriteWidth,
c.nameHeight as nameSpriteHeight,
d.isBEm as isBemCard,
a.characterId = uc.id as isInAdventure
a.characterId = uc.id as isInAdventure,
uc.isActive as active
FROM UserCharacter uc
JOIN CardCharacter c ON uc.charId = c.id
JOIN Card d ON d.id = c.cardId
@@ -218,7 +223,8 @@ interface UserCharacterDao {
c.nameWidth as nameSpriteWidth,
c.nameHeight as nameSpriteHeight,
d.isBEm as isBemCard,
a.characterId = uc.id as isInAdventure
a.characterId = uc.id as isInAdventure,
uc.isActive as active
FROM UserCharacter uc
JOIN CardCharacter c ON uc.charId = c.id
JOIN Card d ON d.id = c.cardId
@@ -31,7 +31,8 @@ object CharacterDtos {
val nameSpriteWidth: Int,
val nameSpriteHeight: Int,
val isBemCard: Boolean,
val isInAdventure: Boolean
val isInAdventure: Boolean,
val active: Boolean
)
data class CardCharacterInfo(
@@ -9,12 +9,11 @@ import androidx.compose.foundation.lazy.items
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.produceState
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
@@ -29,7 +28,6 @@ import com.github.nacabaro.vbhelper.navigation.NavigationItems
import com.github.nacabaro.vbhelper.source.StorageRepository
import com.github.nacabaro.vbhelper.utils.BitmapData
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import java.time.Instant
@Composable
@@ -37,16 +35,17 @@ fun AdventureScreen(
navController: NavController,
storageScreenController: AdventureScreenControllerImpl
) {
val coroutineScope = rememberCoroutineScope()
val application = LocalContext.current.applicationContext as VBHelper
val database = application.container.db
val storageRepository = StorageRepository(database)
val characterList = remember {
mutableStateOf<List<CharacterDtos.AdventureCharacterWithSprites>>(emptyList())
}
val characterList by storageRepository.getAdventureCharacters().collectAsState(emptyList())
var obtainedItem by remember {
mutableStateOf<ItemDtos.PurchasedItem?>(null)
}
var obtainedCurrency by remember {
mutableStateOf(0)
}
val currentTime by produceState(initialValue = Instant.now().epochSecond) {
while (true) {
@@ -59,13 +58,6 @@ fun AdventureScreen(
mutableStateOf<CharacterDtos.AdventureCharacterWithSprites?>(null)
}
LaunchedEffect(storageRepository) {
coroutineScope.launch {
characterList.value = storageRepository
.getAdventureCharacters()
}
}
Scaffold(
topBar = {
TopBanner(
@@ -76,7 +68,7 @@ fun AdventureScreen(
)
}
) { contentPadding ->
if (characterList.value.isEmpty()) {
if (characterList.isEmpty()) {
Column(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
@@ -91,7 +83,7 @@ fun AdventureScreen(
modifier = Modifier
.padding(top = contentPadding.calculateTopPadding())
) {
items(characterList.value) {
items(characterList) {
AdventureEntry(
icon = BitmapData(
bitmap = it.spriteIdle,
@@ -102,8 +94,9 @@ fun AdventureScreen(
onClick = {
if (it.finishesAdventure < currentTime) {
storageScreenController
.getItemFromAdventure(it.id) { adventureResult ->
.getItemFromAdventure(it.id) { adventureResult, generatedCurrency ->
obtainedItem = adventureResult
obtainedCurrency = generatedCurrency
}
} else {
cancelAdventureDialog = it
@@ -118,6 +111,7 @@ fun AdventureScreen(
if (obtainedItem != null) {
ObtainedItemDialog(
obtainedItem = obtainedItem!!,
obtainedCurrency = obtainedCurrency,
onClickDismiss = {
obtainedItem = null
}
@@ -4,6 +4,6 @@ import com.github.nacabaro.vbhelper.dtos.ItemDtos
interface AdventureScreenController {
fun sendCharacterToAdventure(characterId: Long, timeInMinutes: Long)
fun getItemFromAdventure(characterId: Long, onResult: (ItemDtos.PurchasedItem) -> Unit)
fun getItemFromAdventure(characterId: Long, onResult: (ItemDtos.PurchasedItem, Int) -> Unit)
fun cancelAdventure(characterId: Long, onResult: () -> Unit)
}
@@ -38,19 +38,29 @@ class AdventureScreenControllerImpl(
override fun getItemFromAdventure(
characterId: Long,
onResult: (ItemDtos.PurchasedItem) -> Unit
onResult: (ItemDtos.PurchasedItem, Int) -> Unit
) {
componentActivity.lifecycleScope.launch(Dispatchers.IO) {
database
.adventureDao()
.deleteAdventure(characterId)
val generatedCurrency = generateRandomCurrency()
val generatedItem = generateItem(characterId)
onResult(generatedItem)
onResult(generatedItem, generatedCurrency)
}
}
private suspend fun generateRandomCurrency(): Int {
val currentValue = application.container.currencyRepository.currencyValue.first()
val random = (2..6).random() * 1000
application.container.currencyRepository.setCurrencyValue(currentValue + random)
return random
}
override fun cancelAdventure(characterId: Long, onResult: () -> Unit) {
componentActivity.lifecycleScope.launch(Dispatchers.IO) {
database
@@ -55,6 +55,7 @@ fun HomeScreen(
var adventureMissionsFinished by rememberSaveable { mutableStateOf(false) }
var betaWarning by rememberSaveable { mutableStateOf(true) }
var collectedItem by remember { mutableStateOf<ItemDtos.PurchasedItem?>(null) }
var collectedCurrency by remember { mutableStateOf<Int?>(null) }
LaunchedEffect(storageRepository, activeMon, collectedItem) {
withContext(Dispatchers.IO) {
@@ -123,8 +124,9 @@ fun HomeScreen(
contentPadding = contentPadding,
specialMissions = vbSpecialMissions.value,
homeScreenController = homeScreenController,
onClickCollect = { item ->
onClickCollect = { item, currency ->
collectedItem = item
collectedCurrency = currency
}
)
}
@@ -134,8 +136,10 @@ fun HomeScreen(
if (collectedItem != null) {
ObtainedItemDialog(
obtainedItem = collectedItem!!,
obtainedCurrency = collectedCurrency!!,
onClickDismiss = {
collectedItem = null
collectedCurrency = null
}
)
}
@@ -5,5 +5,5 @@ import com.github.nacabaro.vbhelper.dtos.ItemDtos
interface HomeScreenController {
fun didAdventureMissionsFinish(onCompletion: (Boolean) -> Unit)
fun clearSpecialMission(missionId: Long, missionCompletion: SpecialMission.Status, onCleared: (ItemDtos.PurchasedItem?) -> Unit)
fun clearSpecialMission(missionId: Long, missionCompletion: SpecialMission.Status, onCleared: (ItemDtos.PurchasedItem?, Int?) -> Unit)
}
@@ -23,6 +23,7 @@ class HomeScreenControllerImpl(
val adventureCharacters = database
.adventureDao()
.getAdventureCharacters()
.first()
val finishedAdventureCharacters = adventureCharacters.filter { character ->
character.finishesAdventure <= currentTime
@@ -32,7 +33,7 @@ class HomeScreenControllerImpl(
}
}
override fun clearSpecialMission(missionId: Long, missionCompletion: SpecialMission.Status, onCleared: (ItemDtos.PurchasedItem?) -> Unit) {
override fun clearSpecialMission(missionId: Long, missionCompletion: SpecialMission.Status, onCleared: (ItemDtos.PurchasedItem?, Int?) -> Unit) {
componentActivity.lifecycleScope.launch {
database
.specialMissionDao()
@@ -64,9 +65,13 @@ class HomeScreenControllerImpl(
itemType = randomItem.itemType
)
onCleared(purchasedItem)
val randomAmount = (2..6).random() * 1000
val currentCurrency = application.container.currencyRepository.currencyValue.first()
application.container.currencyRepository.setCurrencyValue(currentCurrency + randomAmount)
onCleared(purchasedItem, randomAmount)
} else {
onCleared(null)
onCleared(null, null)
}
}
@@ -34,7 +34,7 @@ fun VBDiMHomeScreen(
homeScreenController: HomeScreenControllerImpl,
transformationHistory: List<CharacterDtos.TransformationHistory>,
contentPadding: PaddingValues,
onClickCollect: (ItemDtos.PurchasedItem?) -> Unit
onClickCollect: (ItemDtos.PurchasedItem?, Int?) -> Unit
) {
Column(
modifier = Modifier
@@ -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<List<CharacterDtos.CharacterWithSprites>>(emptyList())
}
@@ -57,7 +58,7 @@ fun ChooseCharacterScreen(
characterList.value = storageRepository.getVBCharacters()
}
else -> {
characterList.value = storageRepository.getAllCharacters()
characterList.value = storageRepository.getAllCharacters().first()
}
}
}
@@ -23,6 +23,7 @@ import com.github.nacabaro.vbhelper.dtos.ItemDtos
@Composable
fun ObtainedItemDialog(
obtainedItem: ItemDtos.PurchasedItem,
obtainedCurrency: Int,
onClickDismiss: () -> Unit
) {
Dialog(
@@ -84,7 +85,16 @@ fun ObtainedItemDialog(
text = "You have obtained ${obtainedItem.itemAmount} of this item",
modifier = Modifier
.fillMaxWidth()
.padding(5.dp)
.padding(top = 4.dp)
)
Text(
textAlign = TextAlign.Center,
fontSize = MaterialTheme.typography.bodySmall.fontSize,
fontFamily = MaterialTheme.typography.bodySmall.fontFamily,
text = "You also got $obtainedCurrency credits",
modifier = Modifier
.fillMaxWidth()
.padding(bottom = 4.dp)
)
Button(
onClick = onClickDismiss,
@@ -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)
)
}
}
@@ -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<NfcCharacter?>(null) }
var cardsRead by remember { mutableStateOf<List<Card>?>(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!!,
if (writingScreen && nfcCharacter != null && characterId != null) {
WritingScreen(
scanScreenController = scanScreenController,
nfcCharacter = nfcCharacter!!,
characterId = characterId,
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!!)
},
onCancel = {
writingScreen = false
navController.navigate(NavigationItems.Home.route)
}
}
}
if (readingScreen) {
ReadingCharacterScreen("Reading character") {
)
} else if (readingScreen) {
ReadingScreen(
scanScreenController = scanScreenController,
onCancel = {
readingScreen = false
scanScreenController.cancelRead()
}
} else if (writingScreen) {
if (!isDoneSendingCard) {
ReadingCharacterScreen("Sending card") {
writingScreen = false
scanScreenController.cancelRead()
}
} else if (!isDoneWritingCharacter) {
ReadingCharacterScreen("Writing character") {
isDoneSendingCard = false
writingScreen = false
scanScreenController.cancelRead()
}
}
} else if (cardSelectScreen) {
ChooseCard(
cards = cardsRead!!,
onCardSelected = { card ->
cardSelectScreen = false
scanScreenController.flushCharacter(card.id)
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() {
@@ -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()
@@ -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<UShort>(3) {
0u
@@ -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 (
@@ -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")
}
}
}
}
@@ -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<List<Card>?>(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)
}
)
}
}
@@ -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")
}
}
}
}
@@ -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")
}
}
}
}
@@ -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()
}
}
@@ -1,5 +1,7 @@
package com.github.nacabaro.vbhelper.screens.settingsScreen
import android.content.Intent
import android.net.Uri
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
@@ -13,6 +15,7 @@ import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
@@ -25,6 +28,8 @@ fun SettingsScreen(
navController: NavController,
settingsScreenController: SettingsScreenControllerImpl,
) {
val context = LocalContext.current
Scaffold (
topBar = {
TopBanner(
@@ -44,19 +49,22 @@ fun SettingsScreen(
.verticalScroll(rememberScrollState())
) {
SettingsSection("NFC Communication")
SettingsEntry(title = "Import APK", description = "Import Secrets From Vital Arean 2.1.0 APK") {
SettingsEntry(title = "Import APK", description = "Import Secrets From Vital Arena 2.1.0 APK") {
settingsScreenController.onClickImportApk()
}
SettingsSection("DiM/BEm management")
SettingsEntry(title = "Import card", description = "Import DiM/BEm card file") {
settingsScreenController.onClickImportCard()
}
SettingsEntry(title = "Rename DiM/BEm", description = "Set card name") { }
SettingsSection("About and credits")
SettingsEntry(title = "Credits", description = "Credits") {
navController.navigate(NavigationItems.Credits.route)
}
SettingsEntry(title = "About", description = "About") { }
SettingsEntry(title = "About", description = "About") {
val browserIntent = Intent(
Intent.ACTION_VIEW, Uri.parse("https://github.com/nacabaro/vbhelper/"))
context.startActivity(browserIntent)
}
SettingsSection("Data management")
SettingsEntry(title = "Export data", description = "Export application database") {
settingsScreenController.onClickOpenDirectory()
@@ -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(),
@@ -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
@@ -11,14 +12,15 @@ import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.items
import androidx.compose.foundation.rememberScrollState
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.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 +30,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 +42,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<List<CharacterDtos.CharacterWithSprites>>(emptyList()) }
var selectedCharacter by remember { mutableStateOf<Long?>(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<Long?>(null) }
Scaffold (
topBar = {
@@ -65,7 +58,7 @@ fun StorageScreen(
)
}
) { contentPadding ->
if (monList.value.isEmpty()) {
if (characterList.isEmpty()) {
Column (
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
@@ -86,7 +79,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,
@@ -107,6 +100,16 @@ fun StorageScreen(
)
}
},
cardColors = if (index.active) {
CardDefaults.cardColors(
containerColor = MaterialTheme.colorScheme.primary
)
} else {
CardDefaults.cardColors(
containerColor = MaterialTheme.colorScheme.surfaceContainerHighest
)
}
)
}
}
@@ -138,6 +141,16 @@ fun StorageScreen(
timeInMinutes = time
)
selectedCharacter = null
},
onClickDelete = {
storageScreenController
.deleteCharacter(
characterId = selectedCharacter!!,
onCompletion = {
Log.d("StorageScreen", "Character deleted")
}
)
selectedCharacter = null
}
)
}
@@ -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)
}
@@ -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()
}
}
}
}
@@ -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<Card> {
return database.cardDao().getCardByCharacterId(characterId)
}
}
@@ -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<CharacterDtos.CharacterWithSprites> {
fun getAllCharacters(): Flow<List<CharacterDtos.CharacterWithSprites>> {
return db.userCharacterDao().getAllCharacters()
}
@@ -46,7 +47,7 @@ class StorageRepository (
return db.userCharacterDao().deleteCharacterById(id)
}
suspend fun getAdventureCharacters(): List<CharacterDtos.AdventureCharacterWithSprites> {
fun getAdventureCharacters(): Flow<List<CharacterDtos.AdventureCharacterWithSprites>> {
return db.adventureDao().getAdventureCharacters()
}