Compare commits

..
Author SHA1 Message Date
Nacho 8dc5bbbdde Small fix related to special missions
- Now you can clear failed special missions. When a special mission fails, you will not get an item after clearing it.

- Huge note: I have not tested this fix, but it should work... At least that's what my intuition is telling me!

Jokes aside, this should work, but the home screen will not update. I should update this to make use of StateFlows and Flows to keep the home screen updated at all times.
2025-09-05 02:19:09 +02:00
Nacho 2586190e5c Update libraries 2025-09-02 18:01:11 +02:00
Nacho 1a6753d2aa Fixed date issues with TransformationHistory 2025-09-02 04:54:08 +02:00
Nacho f8ec899335 Version up and credits 2025-08-09 12:59:01 +02:00
Nacho b95edfa2a1 New icon 2025-08-09 12:55:38 +02:00
Nacho 1a570170ca Merge remote-tracking branch 'origin/main' 2025-08-06 20:18:43 +02:00
Nacho 1566876106 Version up 2025-08-06 20:18:32 +02:00
nacabaroandGitHub 019f07d827 Merge pull request #36 from nacabaro/card/card_management
Few things here and there
2025-08-06 20:13:09 +02:00
Nacho 4474e6689d Few things here and there
- Card management, you can now modify the name of the cards and remove cards too.
- Also, support for multiple cards with the same ID works too. When scanning for the first time, if a repeat card exists, the user will be asked to choose which card the character comes from. Future scans will not ask since it is stored in the watch app reserved area.
2025-08-06 20:10:21 +02:00
44 changed files with 763 additions and 206 deletions
+1 -1
View File
@@ -15,7 +15,7 @@ android {
minSdk = 28 minSdk = 28
targetSdk = 35 targetSdk = 35
versionCode = 1 versionCode = 1
versionName = "Alpha 0.4.1" versionName = "Alpha 0.5.2"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
} }
Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

@@ -14,6 +14,7 @@ import com.github.nacabaro.vbhelper.screens.itemsScreen.ItemsScreenControllerImp
import com.github.nacabaro.vbhelper.screens.scanScreen.ScanScreenControllerImpl import com.github.nacabaro.vbhelper.screens.scanScreen.ScanScreenControllerImpl
import com.github.nacabaro.vbhelper.screens.settingsScreen.SettingsScreenControllerImpl import com.github.nacabaro.vbhelper.screens.settingsScreen.SettingsScreenControllerImpl
import com.github.nacabaro.vbhelper.screens.adventureScreen.AdventureScreenControllerImpl import com.github.nacabaro.vbhelper.screens.adventureScreen.AdventureScreenControllerImpl
import com.github.nacabaro.vbhelper.screens.cardScreen.CardScreenControllerImpl
import com.github.nacabaro.vbhelper.screens.spriteViewer.SpriteViewerControllerImpl import com.github.nacabaro.vbhelper.screens.spriteViewer.SpriteViewerControllerImpl
import com.github.nacabaro.vbhelper.screens.storageScreen.StorageScreenControllerImpl import com.github.nacabaro.vbhelper.screens.storageScreen.StorageScreenControllerImpl
import com.github.nacabaro.vbhelper.ui.theme.VBHelperTheme import com.github.nacabaro.vbhelper.ui.theme.VBHelperTheme
@@ -47,6 +48,7 @@ class MainActivity : ComponentActivity() {
val storageScreenController = StorageScreenControllerImpl(this) val storageScreenController = StorageScreenControllerImpl(this)
val homeScreenController = HomeScreenControllerImpl(this) val homeScreenController = HomeScreenControllerImpl(this)
val spriteViewerController = SpriteViewerControllerImpl(this) val spriteViewerController = SpriteViewerControllerImpl(this)
val cardScreenController = CardScreenControllerImpl(this)
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@@ -61,7 +63,8 @@ class MainActivity : ComponentActivity() {
adventureScreenController = adventureScreenController, adventureScreenController = adventureScreenController,
homeScreenController = homeScreenController, homeScreenController = homeScreenController,
storageScreenController = storageScreenController, storageScreenController = storageScreenController,
spriteViewerController = spriteViewerController spriteViewerController = spriteViewerController,
cardScreenController = cardScreenController
) )
} }
} }
@@ -93,7 +96,8 @@ class MainActivity : ComponentActivity() {
adventureScreenController: AdventureScreenControllerImpl, adventureScreenController: AdventureScreenControllerImpl,
storageScreenController: StorageScreenControllerImpl, storageScreenController: StorageScreenControllerImpl,
homeScreenController: HomeScreenControllerImpl, homeScreenController: HomeScreenControllerImpl,
spriteViewerController: SpriteViewerControllerImpl spriteViewerController: SpriteViewerControllerImpl,
cardScreenController: CardScreenControllerImpl
) { ) {
AppNavigation( AppNavigation(
applicationNavigationHandlers = AppNavigationHandlers( applicationNavigationHandlers = AppNavigationHandlers(
@@ -103,7 +107,8 @@ class MainActivity : ComponentActivity() {
adventureScreenController, adventureScreenController,
storageScreenController, storageScreenController,
homeScreenController, homeScreenController,
spriteViewerController spriteViewerController,
cardScreenController
) )
) )
} }
@@ -169,7 +169,7 @@ fun SpecialMissionsEntry(
Card( Card(
modifier = modifier, modifier = modifier,
shape = androidx.compose.material.MaterialTheme.shapes.small, shape = androidx.compose.material.MaterialTheme.shapes.small,
onClick = if (specialMission.status == SpecialMission.Status.COMPLETED) { onClick = if (specialMission.status == SpecialMission.Status.COMPLETED || specialMission.status == SpecialMission.Status.FAILED) {
onClickCard onClickCard
} else { } else {
{ } { }
@@ -24,7 +24,8 @@ fun TopBanner(
onGearClick: (() -> Unit)? = null, onGearClick: (() -> Unit)? = null,
onBackClick: (() -> Unit)? = null, onBackClick: (() -> Unit)? = null,
onScanClick: (() -> Unit)? = null, onScanClick: (() -> Unit)? = null,
onAdventureClick: (() -> Unit)? = null onAdventureClick: (() -> Unit)? = null,
onModifyClick: (() -> Unit)? = null
) { ) {
Box( // Use Box to overlay elements Box( // Use Box to overlay elements
modifier = modifier modifier = modifier
@@ -37,16 +38,16 @@ fun TopBanner(
textAlign = TextAlign.Center, textAlign = TextAlign.Center,
fontSize = 24.sp, fontSize = 24.sp,
modifier = Modifier modifier = Modifier
.align(Alignment.Center) // Center the text .align(Alignment.Center)
) )
if (onGearClick != null) { if (onGearClick != null) {
IconButton( IconButton(
onClick = onGearClick, onClick = onGearClick,
modifier = Modifier modifier = Modifier
.align(Alignment.CenterEnd) // Place gear icon at the end .align(Alignment.CenterEnd)
) { ) {
Icon( Icon(
painter = painterResource(R.drawable.baseline_settings_24), // Use a gear icon painter = painterResource(R.drawable.baseline_settings_24),
contentDescription = "Settings" contentDescription = "Settings"
) )
} }
@@ -54,10 +55,21 @@ fun TopBanner(
IconButton( IconButton(
onClick = onAdventureClick, onClick = onAdventureClick,
modifier = Modifier modifier = Modifier
.align(Alignment.CenterEnd) // Place gear icon at the end .align(Alignment.CenterEnd)
) { ) {
Icon( Icon(
painter = painterResource(R.drawable.baseline_fort_24), // Use a gear icon painter = painterResource(R.drawable.baseline_fort_24),
contentDescription = "Adventure"
)
}
} else if (onModifyClick != null) {
IconButton(
onClick = onModifyClick,
modifier = Modifier
.align(Alignment.CenterEnd)
) {
Icon(
painter = painterResource(R.drawable.baseline_edit_24),
contentDescription = "Adventure" contentDescription = "Adventure"
) )
} }
@@ -67,10 +79,10 @@ fun TopBanner(
IconButton( IconButton(
onClick = onScanClick, onClick = onScanClick,
modifier = Modifier modifier = Modifier
.align(Alignment.CenterStart) // Place gear icon at the end .align(Alignment.CenterStart)
) { ) {
Icon( Icon(
painter = painterResource(R.drawable.baseline_nfc_24), // Use a gear icon painter = painterResource(R.drawable.baseline_nfc_24),
contentDescription = "Scan" contentDescription = "Scan"
) )
} }
@@ -78,10 +90,10 @@ fun TopBanner(
IconButton( IconButton(
onClick = onBackClick, onClick = onBackClick,
modifier = Modifier modifier = Modifier
.align(Alignment.CenterStart) // Place gear icon at the end .align(Alignment.CenterStart)
) { ) {
Icon( Icon(
painter = painterResource(R.drawable.baseline_arrow_back_24), // Use a gear icon painter = painterResource(R.drawable.baseline_arrow_back_24),
contentDescription = "Settings" contentDescription = "Settings"
) )
} }
@@ -9,8 +9,26 @@ import com.github.nacabaro.vbhelper.domain.card.Card
@Dao @Dao
interface CardDao { interface CardDao {
@Insert(onConflict = OnConflictStrategy.IGNORE) @Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun insertNewDim(card: Card): Long suspend fun insertNewCard(card: Card): Long
@Query("SELECT * FROM Card WHERE cardId = :id") @Query("SELECT * FROM Card WHERE cardId = :id")
fun getDimById(id: Int): Card? fun getCardByCardId(id: Int): List<Card>
@Query("SELECT * FROM Card WHERE id = :id")
fun getCardById(id: Long): Card?
@Query("""
SELECT ca.*
FROM Card ca
JOIN Character ch ON ca.id = ch.dimId
JOIN UserCharacter uc ON ch.id = uc.charId
WHERE uc.id = :id
""")
suspend fun getCardByCharacterId(id: Long): Card
@Query("UPDATE Card SET name = :newName WHERE id = :id")
suspend fun renameCard(id: Int, newName: String)
@Query("DELETE FROM Card WHERE id = :id")
suspend fun deleteCard(id: Long)
} }
@@ -19,8 +19,8 @@ import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController import androidx.navigation.compose.rememberNavController
import com.github.nacabaro.vbhelper.di.VBHelper import com.github.nacabaro.vbhelper.di.VBHelper
import com.github.nacabaro.vbhelper.screens.BattlesScreen import com.github.nacabaro.vbhelper.screens.BattlesScreen
import com.github.nacabaro.vbhelper.screens.DexScreen import com.github.nacabaro.vbhelper.screens.cardScreen.CardsScreen
import com.github.nacabaro.vbhelper.screens.DiMScreen import com.github.nacabaro.vbhelper.screens.cardScreen.CardViewScreen
import com.github.nacabaro.vbhelper.screens.homeScreens.HomeScreen import com.github.nacabaro.vbhelper.screens.homeScreens.HomeScreen
import com.github.nacabaro.vbhelper.screens.itemsScreen.ItemsScreen import com.github.nacabaro.vbhelper.screens.itemsScreen.ItemsScreen
import com.github.nacabaro.vbhelper.screens.scanScreen.ScanScreen import com.github.nacabaro.vbhelper.screens.scanScreen.ScanScreen
@@ -34,6 +34,7 @@ import com.github.nacabaro.vbhelper.screens.itemsScreen.ItemsScreenControllerImp
import com.github.nacabaro.vbhelper.screens.settingsScreen.SettingsScreenControllerImpl import com.github.nacabaro.vbhelper.screens.settingsScreen.SettingsScreenControllerImpl
import com.github.nacabaro.vbhelper.screens.adventureScreen.AdventureScreen import com.github.nacabaro.vbhelper.screens.adventureScreen.AdventureScreen
import com.github.nacabaro.vbhelper.screens.adventureScreen.AdventureScreenControllerImpl import com.github.nacabaro.vbhelper.screens.adventureScreen.AdventureScreenControllerImpl
import com.github.nacabaro.vbhelper.screens.cardScreen.CardScreenControllerImpl
import com.github.nacabaro.vbhelper.screens.settingsScreen.CreditsScreen import com.github.nacabaro.vbhelper.screens.settingsScreen.CreditsScreen
import com.github.nacabaro.vbhelper.screens.spriteViewer.SpriteViewerControllerImpl import com.github.nacabaro.vbhelper.screens.spriteViewer.SpriteViewerControllerImpl
import com.github.nacabaro.vbhelper.screens.storageScreen.StorageScreenControllerImpl import com.github.nacabaro.vbhelper.screens.storageScreen.StorageScreenControllerImpl
@@ -46,7 +47,8 @@ data class AppNavigationHandlers(
val adventureScreenController: AdventureScreenControllerImpl, val adventureScreenController: AdventureScreenControllerImpl,
val storageScreenController: StorageScreenControllerImpl, val storageScreenController: StorageScreenControllerImpl,
val homeScreenController: HomeScreenControllerImpl, val homeScreenController: HomeScreenControllerImpl,
val spriteViewerController: SpriteViewerControllerImpl val spriteViewerController: SpriteViewerControllerImpl,
val cardScreenController: CardScreenControllerImpl
) )
@Composable @Composable
@@ -121,8 +123,9 @@ fun AppNavigation(
) )
} }
composable(NavigationItems.Dex.route) { composable(NavigationItems.Dex.route) {
DexScreen( CardsScreen(
navController = navController navController = navController,
cardScreenController = applicationNavigationHandlers.cardScreenController
) )
} }
composable(NavigationItems.Settings.route) { composable(NavigationItems.Settings.route) {
@@ -140,7 +143,7 @@ fun AppNavigation(
composable(NavigationItems.CardView.route) { composable(NavigationItems.CardView.route) {
val cardId = it.arguments?.getString("cardId") val cardId = it.arguments?.getString("cardId")
if (cardId != null) { if (cardId != null) {
DiMScreen( CardViewScreen(
navController = navController, navController = navController,
dimId = cardId.toLong() dimId = cardId.toLong()
) )
@@ -1,83 +0,0 @@
package com.github.nacabaro.vbhelper.screens
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
import com.github.nacabaro.vbhelper.utils.BitmapData
import com.github.nacabaro.vbhelper.components.DexDiMEntry
import com.github.nacabaro.vbhelper.components.TopBanner
import com.github.nacabaro.vbhelper.di.VBHelper
import com.github.nacabaro.vbhelper.dtos.CardDtos
import com.github.nacabaro.vbhelper.navigation.NavigationItems
import com.github.nacabaro.vbhelper.source.DexRepository
import kotlinx.coroutines.launch
@Composable
fun DexScreen(
navController: NavController
) {
val coroutineScope = rememberCoroutineScope()
val application = LocalContext.current.applicationContext as VBHelper
val dexRepository = DexRepository(application.container.db)
val cardList = remember { mutableStateOf<List<CardDtos.CardProgress>>(emptyList()) }
LaunchedEffect(dexRepository) {
coroutineScope.launch {
val newDimList = dexRepository.getAllDims()
cardList.value = newDimList // Replace the entire list atomically
}
}
Scaffold (
topBar = {
TopBanner(
text = "Discovered characters",
onGearClick = {
navController.navigate(NavigationItems.Viewer.route)
}
)
}
) { contentPadding ->
LazyColumn (
modifier = Modifier
.padding(top = contentPadding.calculateTopPadding())
) {
items(cardList.value) {
DexDiMEntry(
name = it.cardName,
logo = BitmapData(
bitmap = it.cardLogo,
width = it.logoWidth,
height = it.logoHeight
),
onClick = {
navController
.navigate(
NavigationItems
.CardView.route
.replace("{cardId}", "${it.cardId}")
)
},
obtainedCharacters = it.obtainedCharacters,
totalCharacters = it.totalCharacters,
modifier = Modifier
.fillMaxWidth()
.padding(8.dp)
)
}
}
}
}
@@ -0,0 +1,109 @@
package com.github.nacabaro.vbhelper.screens.cardScreen
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material.icons.filled.Edit
import androidx.compose.material3.Card
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.FilterQuality
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import com.github.nacabaro.vbhelper.utils.BitmapData
import com.github.nacabaro.vbhelper.utils.getBitmap
@Composable
fun CardEntry(
name: String,
logo: BitmapData,
obtainedCharacters: Int,
totalCharacters: Int,
onClick: () -> Unit,
displayModify: Boolean,
onClickModify: () -> Unit,
onClickDelete: () -> Unit,
modifier: Modifier = Modifier
) {
val bitmap = remember (logo.bitmap) { logo.getBitmap() }
val imageBitmap = remember(bitmap) { bitmap.asImageBitmap() }
val density: Float = LocalContext.current.resources.displayMetrics.density
val dpSize = (logo.width * 4 / density).dp
Card (
shape = MaterialTheme.shapes.medium,
modifier = modifier,
onClick = if (!displayModify) {
onClick
} else {
{ }
}
) {
Row (
horizontalArrangement = Arrangement.Start,
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.padding(8.dp)
) {
Image (
bitmap = imageBitmap,
contentDescription = name,
filterQuality = FilterQuality.None,
modifier = Modifier
.padding(8.dp)
.size(dpSize)
)
Column(
modifier = Modifier
.padding(8.dp)
.weight(1f)
) {
Text(
text = name,
modifier = Modifier
)
Text(
text = "$obtainedCharacters of $totalCharacters characters obtained",
fontFamily = MaterialTheme.typography.labelSmall.fontFamily,
fontSize = MaterialTheme.typography.labelSmall.fontSize,
modifier = Modifier
)
}
if (displayModify) {
Row (
modifier = Modifier,
horizontalArrangement = Arrangement.End,
) {
IconButton(
onClick = onClickModify
) {
Icon(
imageVector = Icons.Default.Edit,
contentDescription = "Edit"
)
}
IconButton(
onClick = onClickDelete
) {
Icon(
imageVector = Icons.Default.Delete,
contentDescription = "Delete"
)
}
}
}
}
}
}
@@ -0,0 +1,6 @@
package com.github.nacabaro.vbhelper.screens.cardScreen
interface CardScreenController {
fun renameCard(cardId: Long, newName: String, onRenamed: (String) -> Unit)
fun deleteCard(cardId: Long, onDeleted: () -> Unit)
}
@@ -0,0 +1,34 @@
package com.github.nacabaro.vbhelper.screens.cardScreen
import androidx.activity.ComponentActivity
import androidx.lifecycle.lifecycleScope
import com.github.nacabaro.vbhelper.di.VBHelper
import kotlinx.coroutines.launch
class CardScreenControllerImpl(
private val componentActivity: ComponentActivity,
) : CardScreenController {
private val application = componentActivity.applicationContext as VBHelper
private val database = application.container.db
override fun renameCard(cardId: Long, newName: String, onRenamed: (String) -> Unit) {
componentActivity.lifecycleScope.launch {
database
.cardDao()
.renameCard(cardId.toInt(), newName)
onRenamed(newName)
}
}
override fun deleteCard(cardId: Long, onDeleted: () -> Unit) {
componentActivity.lifecycleScope.launch {
database
.cardDao()
.deleteCard(cardId)
onDeleted()
}
}
}
@@ -1,4 +1,4 @@
package com.github.nacabaro.vbhelper.screens package com.github.nacabaro.vbhelper.screens.cardScreen
import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
@@ -14,14 +14,13 @@ import androidx.navigation.NavController
import com.github.nacabaro.vbhelper.utils.BitmapData import com.github.nacabaro.vbhelper.utils.BitmapData
import com.github.nacabaro.vbhelper.components.CharacterEntry import com.github.nacabaro.vbhelper.components.CharacterEntry
import com.github.nacabaro.vbhelper.components.TopBanner import com.github.nacabaro.vbhelper.components.TopBanner
import com.github.nacabaro.vbhelper.domain.characters.Character
import com.github.nacabaro.vbhelper.di.VBHelper import com.github.nacabaro.vbhelper.di.VBHelper
import com.github.nacabaro.vbhelper.dtos.CharacterDtos import com.github.nacabaro.vbhelper.dtos.CharacterDtos
import com.github.nacabaro.vbhelper.source.DexRepository import com.github.nacabaro.vbhelper.source.DexRepository
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@Composable @Composable
fun DiMScreen( fun CardViewScreen(
navController: NavController, navController: NavController,
dimId: Long dimId: Long
) { ) {
@@ -0,0 +1,145 @@
package com.github.nacabaro.vbhelper.screens.cardScreen
import android.util.Log
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
import com.github.nacabaro.vbhelper.utils.BitmapData
import com.github.nacabaro.vbhelper.components.TopBanner
import com.github.nacabaro.vbhelper.di.VBHelper
import com.github.nacabaro.vbhelper.dtos.CardDtos
import com.github.nacabaro.vbhelper.navigation.NavigationItems
import com.github.nacabaro.vbhelper.screens.cardScreen.dialogs.CardDeleteDialog
import com.github.nacabaro.vbhelper.screens.cardScreen.dialogs.CardRenameDialog
import com.github.nacabaro.vbhelper.source.DexRepository
import kotlinx.coroutines.launch
@Composable
fun CardsScreen(
navController: NavController,
cardScreenController: CardScreenControllerImpl
) {
val coroutineScope = rememberCoroutineScope()
val application = LocalContext.current.applicationContext as VBHelper
val dexRepository = DexRepository(application.container.db)
val cardList = remember { mutableStateOf<List<CardDtos.CardProgress>>(emptyList()) }
val selectedCard = remember { mutableStateOf<CardDtos.CardProgress?>(null) }
var clickedDelete by remember { mutableStateOf(false) }
var clickedRename by remember { mutableStateOf(false) }
var modifyCards by remember { mutableStateOf(false) }
LaunchedEffect(dexRepository) {
coroutineScope.launch {
val newDimList = dexRepository.getAllDims()
cardList.value = newDimList
}
}
Scaffold (
topBar = {
TopBanner(
text = "My cards",
onModifyClick = {
modifyCards = !modifyCards
}
)
}
) { contentPadding ->
LazyColumn (
modifier = Modifier
.padding(top = contentPadding.calculateTopPadding())
) {
items(cardList.value) {
CardEntry(
name = it.cardName,
logo = BitmapData(
bitmap = it.cardLogo,
width = it.logoWidth,
height = it.logoHeight
),
onClick = {
navController
.navigate(
NavigationItems
.CardView.route
.replace("{cardId}", "${it.cardId}")
)
},
obtainedCharacters = it.obtainedCharacters,
totalCharacters = it.totalCharacters,
modifier = Modifier
.fillMaxWidth()
.padding(8.dp),
displayModify = modifyCards,
onClickModify = {
selectedCard.value = it
clickedRename = true
},
onClickDelete = {
selectedCard.value = it
clickedDelete = true
}
)
}
}
}
if (clickedRename) {
CardRenameDialog(
onDismiss = {
clickedRename = false
selectedCard.value = null
},
onRename = { newName ->
Log.d("CardsScreen", "New name: $newName")
Log.d("CardsScreen", "Card: ${selectedCard.value.toString()}")
cardScreenController
.renameCard(
cardId = selectedCard.value!!.cardId,
newName = newName,
onRenamed = {
clickedRename = false
selectedCard.value = null
}
)
},
currentName = selectedCard.value!!.cardName
)
}
if (clickedDelete) {
CardDeleteDialog(
cardName = selectedCard.value!!.cardName,
onDismiss = {
clickedDelete = false
selectedCard.value = null
},
onConfirm = {
cardScreenController
.deleteCard(
cardId = selectedCard.value!!.cardId,
onDeleted = {
clickedDelete = false
selectedCard.value = null
}
)
}
)
}
}
@@ -0,0 +1,51 @@
package com.github.nacabaro.vbhelper.screens.cardScreen.dialogs
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.Card
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
@Composable
fun CardDeleteDialog(
cardName: String,
onDismiss: () -> Unit,
onConfirm: () -> Unit
) {
Dialog(
onDismissRequest = onDismiss
) {
Card ( ) {
Column (
modifier = Modifier
.padding(16.dp)
) {
Text(text = "Are you sure you want to delete $cardName. This action will also delete all the characters raised from this card.")
Spacer(modifier = Modifier.padding(8.dp))
Row {
Button(
onClick = {
onDismiss()
}
) {
Text(text = "Confirm")
}
Button(
onClick = {
onConfirm()
}
) {
Text(text = "Delete")
}
}
}
}
}
}
@@ -0,0 +1,52 @@
package com.github.nacabaro.vbhelper.screens.cardScreen.dialogs
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.Card
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
@Composable
fun CardRenameDialog(
onDismiss: () -> Unit,
onRename: (String) -> Unit,
currentName: String
) {
var cardName by remember { mutableStateOf(currentName) }
Dialog(
onDismissRequest = onDismiss
) {
Card ( ) {
Column (
modifier = Modifier
.padding(16.dp)
) {
TextField(
value = cardName,
onValueChange = { cardName = it }
)
Spacer(modifier = Modifier.padding(8.dp))
Button(
onClick = {
onRename(cardName)
onDismiss()
}
) {
Text(text = "Rename")
}
}
}
}
}
@@ -1,8 +1,9 @@
package com.github.nacabaro.vbhelper.screens.homeScreens package com.github.nacabaro.vbhelper.screens.homeScreens
import com.github.cfogrady.vbnfc.vb.SpecialMission
import com.github.nacabaro.vbhelper.dtos.ItemDtos import com.github.nacabaro.vbhelper.dtos.ItemDtos
interface HomeScreenController { interface HomeScreenController {
fun didAdventureMissionsFinish(onCompletion: (Boolean) -> Unit) fun didAdventureMissionsFinish(onCompletion: (Boolean) -> Unit)
fun clearSpecialMission(missionId: Long, onCleared: (ItemDtos.PurchasedItem) -> Unit) fun clearSpecialMission(missionId: Long, missionCompletion: SpecialMission.Status, onCleared: (ItemDtos.PurchasedItem?) -> Unit)
} }
@@ -2,6 +2,7 @@ package com.github.nacabaro.vbhelper.screens.homeScreens
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.github.cfogrady.vbnfc.vb.SpecialMission
import com.github.nacabaro.vbhelper.di.VBHelper import com.github.nacabaro.vbhelper.di.VBHelper
import com.github.nacabaro.vbhelper.dtos.ItemDtos import com.github.nacabaro.vbhelper.dtos.ItemDtos
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@@ -30,12 +31,13 @@ class HomeScreenControllerImpl(
} }
} }
override fun clearSpecialMission(missionId: Long, onCleared: (ItemDtos.PurchasedItem) -> Unit) { override fun clearSpecialMission(missionId: Long, missionCompletion: SpecialMission.Status, onCleared: (ItemDtos.PurchasedItem?) -> Unit) {
componentActivity.lifecycleScope.launch { componentActivity.lifecycleScope.launch {
database database
.specialMissionDao() .specialMissionDao()
.clearSpecialMission(missionId) .clearSpecialMission(missionId)
if (missionCompletion == SpecialMission.Status.COMPLETED) {
val randomItem = database val randomItem = database
.itemDao() .itemDao()
.getAllItems() .getAllItems()
@@ -61,6 +63,10 @@ class HomeScreenControllerImpl(
) )
onCleared(purchasedItem) onCleared(purchasedItem)
} else {
onCleared(null)
}
} }
} }
} }
@@ -34,7 +34,7 @@ fun VBDiMHomeScreen(
homeScreenController: HomeScreenControllerImpl, homeScreenController: HomeScreenControllerImpl,
transformationHistory: List<CharacterDtos.TransformationHistory>, transformationHistory: List<CharacterDtos.TransformationHistory>,
contentPadding: PaddingValues, contentPadding: PaddingValues,
onClickCollect: (ItemDtos.PurchasedItem) -> Unit onClickCollect: (ItemDtos.PurchasedItem?) -> Unit
) { ) {
Column( Column(
modifier = Modifier modifier = Modifier
@@ -181,7 +181,7 @@ fun VBDiMHomeScreen(
.padding(8.dp), .padding(8.dp),
) { ) {
homeScreenController homeScreenController
.clearSpecialMission(mission.id, onClickCollect) .clearSpecialMission(mission.id, mission.status, onClickCollect)
} }
} }
} }
@@ -30,7 +30,9 @@ import com.github.cfogrady.vbnfc.data.NfcCharacter
import com.github.nacabaro.vbhelper.ActivityLifecycleListener import com.github.nacabaro.vbhelper.ActivityLifecycleListener
import com.github.nacabaro.vbhelper.components.TopBanner import com.github.nacabaro.vbhelper.components.TopBanner
import com.github.nacabaro.vbhelper.di.VBHelper 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.navigation.NavigationItems
import com.github.nacabaro.vbhelper.screens.cardScreen.ChooseCard
import com.github.nacabaro.vbhelper.source.StorageRepository import com.github.nacabaro.vbhelper.source.StorageRepository
import com.github.nacabaro.vbhelper.source.isMissingSecrets import com.github.nacabaro.vbhelper.source.isMissingSecrets
import com.github.nacabaro.vbhelper.source.proto.Secrets import com.github.nacabaro.vbhelper.source.proto.Secrets
@@ -53,6 +55,8 @@ fun ScanScreen(
val storageRepository = StorageRepository(application.container.db) val storageRepository = StorageRepository(application.container.db)
var nfcCharacter by remember { mutableStateOf<NfcCharacter?>(null) } var nfcCharacter by remember { mutableStateOf<NfcCharacter?>(null) }
var cardsRead by remember { mutableStateOf<List<Card>?>(null) }
val context = LocalContext.current val context = LocalContext.current
LaunchedEffect(storageRepository) { LaunchedEffect(storageRepository) {
@@ -71,6 +75,7 @@ fun ScanScreen(
var readingScreen by remember { mutableStateOf(false) } var readingScreen by remember { mutableStateOf(false) }
var writingScreen by remember { mutableStateOf(false) } var writingScreen by remember { mutableStateOf(false) }
var cardSelectScreen by remember { mutableStateOf(false) }
var isDoneReadingCharacter by remember { mutableStateOf(false) } var isDoneReadingCharacter by remember { mutableStateOf(false) }
var isDoneSendingCard by remember { mutableStateOf(false) } var isDoneSendingCard by remember { mutableStateOf(false) }
var isDoneWritingCharacter by remember { mutableStateOf(false) } var isDoneWritingCharacter by remember { mutableStateOf(false) }
@@ -85,15 +90,33 @@ fun ScanScreen(
} }
override fun onResume() { override fun onResume() {
scanScreenController.onClickRead(secrets!!) { scanScreenController.onClickRead(
secrets = secrets!!,
onComplete = {
isDoneReadingCharacter = true
},
onMultipleCards = { cards ->
cardsRead = cards
readingScreen = false
cardSelectScreen = true
isDoneReadingCharacter = true isDoneReadingCharacter = true
} }
)
} }
} }
) )
scanScreenController.onClickRead(secrets!!) { scanScreenController.onClickRead(
secrets = secrets!!,
onComplete = {
isDoneReadingCharacter = true
},
onMultipleCards = { cards ->
cardsRead = cards
readingScreen = false
cardSelectScreen = true
isDoneReadingCharacter = true isDoneReadingCharacter = true
} }
)
} }
onDispose { onDispose {
if(readingScreen) { if(readingScreen) {
@@ -149,7 +172,7 @@ fun ScanScreen(
} }
} }
if (isDoneReadingCharacter) { if (isDoneReadingCharacter && !cardSelectScreen) {
readingScreen = false readingScreen = false
navController.navigate(NavigationItems.Home.route) navController.navigate(NavigationItems.Home.route)
} else if (isDoneSendingCard && isDoneWritingCharacter) { } else if (isDoneSendingCard && isDoneWritingCharacter) {
@@ -181,6 +204,14 @@ fun ScanScreen(
scanScreenController.cancelRead() scanScreenController.cancelRead()
} }
} }
} else if (cardSelectScreen) {
ChooseCard(
cards = cardsRead!!,
onCardSelected = { card ->
cardSelectScreen = false
scanScreenController.flushCharacter(card.id)
}
)
} else { } else {
ChooseConnectOption( ChooseConnectOption(
onClickRead = when { onClickRead = when {
@@ -290,11 +321,12 @@ fun ScanScreenPreview() {
) { ) {
} }
override fun onClickRead(secrets: Secrets, onComplete: ()->Unit) {} override fun flushCharacter(cardId: Long) {}
override fun onClickRead(secrets: Secrets, onComplete: ()->Unit, onMultipleCards: (List<Card>) -> Unit) {}
override fun onClickCheckCard(secrets: Secrets, nfcCharacter: NfcCharacter, onComplete: () -> Unit) {} override fun onClickCheckCard(secrets: Secrets, nfcCharacter: NfcCharacter, onComplete: () -> Unit) {}
override fun onClickWrite(secrets: Secrets, nfcCharacter: NfcCharacter, onComplete: () -> Unit) {} override fun onClickWrite(secrets: Secrets, nfcCharacter: NfcCharacter, onComplete: () -> Unit) {}
override fun cancelRead() {} override fun cancelRead() {}
override fun characterFromNfc(nfcCharacter: NfcCharacter): String { return "" } override fun characterFromNfc(nfcCharacter: NfcCharacter, onMultipleCards: (List<Card>, NfcCharacter) -> Unit): String { return "" }
override suspend fun characterToNfc(characterId: Long): NfcCharacter? { return null } override suspend fun characterToNfc(characterId: Long): NfcCharacter? { return null }
}, },
characterId = null, characterId = null,
@@ -2,12 +2,13 @@ package com.github.nacabaro.vbhelper.screens.scanScreen
import com.github.cfogrady.vbnfc.data.NfcCharacter import com.github.cfogrady.vbnfc.data.NfcCharacter
import com.github.nacabaro.vbhelper.ActivityLifecycleListener import com.github.nacabaro.vbhelper.ActivityLifecycleListener
import com.github.nacabaro.vbhelper.domain.card.Card
import com.github.nacabaro.vbhelper.source.proto.Secrets import com.github.nacabaro.vbhelper.source.proto.Secrets
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
interface ScanScreenController { interface ScanScreenController {
val secretsFlow: Flow<Secrets> val secretsFlow: Flow<Secrets>
fun onClickRead(secrets: Secrets, onComplete: ()->Unit) fun onClickRead(secrets: Secrets, onComplete: ()->Unit, onMultipleCards: (List<Card>) -> Unit)
fun onClickCheckCard(secrets: Secrets, nfcCharacter: NfcCharacter, onComplete: () -> Unit) fun onClickCheckCard(secrets: Secrets, nfcCharacter: NfcCharacter, onComplete: () -> Unit)
fun onClickWrite(secrets: Secrets, nfcCharacter: NfcCharacter, onComplete: () -> Unit) fun onClickWrite(secrets: Secrets, nfcCharacter: NfcCharacter, onComplete: () -> Unit)
@@ -16,6 +17,11 @@ interface ScanScreenController {
fun registerActivityLifecycleListener(key: String, activityLifecycleListener: ActivityLifecycleListener) fun registerActivityLifecycleListener(key: String, activityLifecycleListener: ActivityLifecycleListener)
fun unregisterActivityLifecycleListener(key: String) fun unregisterActivityLifecycleListener(key: String)
fun characterFromNfc(nfcCharacter: NfcCharacter): String fun flushCharacter(cardId: Long)
fun characterFromNfc(
nfcCharacter: NfcCharacter,
onMultipleCards: (List<Card>, NfcCharacter) -> Unit
): String
suspend fun characterToNfc(characterId: Long): NfcCharacter? suspend fun characterToNfc(characterId: Long): NfcCharacter?
} }
@@ -15,6 +15,7 @@ import com.github.cfogrady.vbnfc.be.BENfcCharacter
import com.github.cfogrady.vbnfc.data.NfcCharacter import com.github.cfogrady.vbnfc.data.NfcCharacter
import com.github.cfogrady.vbnfc.vb.VBNfcCharacter import com.github.cfogrady.vbnfc.vb.VBNfcCharacter
import com.github.nacabaro.vbhelper.ActivityLifecycleListener import com.github.nacabaro.vbhelper.ActivityLifecycleListener
import com.github.nacabaro.vbhelper.domain.card.Card
import com.github.nacabaro.vbhelper.screens.scanScreen.converters.FromNfcConverter import com.github.nacabaro.vbhelper.screens.scanScreen.converters.FromNfcConverter
import com.github.nacabaro.vbhelper.screens.scanScreen.converters.ToNfcConverter import com.github.nacabaro.vbhelper.screens.scanScreen.converters.ToNfcConverter
import com.github.nacabaro.vbhelper.source.getCryptographicTransformerMap import com.github.nacabaro.vbhelper.source.getCryptographicTransformerMap
@@ -31,7 +32,7 @@ class ScanScreenControllerImpl(
private val registerActivityLifecycleListener: (String, ActivityLifecycleListener)->Unit, private val registerActivityLifecycleListener: (String, ActivityLifecycleListener)->Unit,
private val unregisterActivityLifecycleListener: (String)->Unit, private val unregisterActivityLifecycleListener: (String)->Unit,
): ScanScreenController { ): ScanScreenController {
private var lastScannedCharacter: NfcCharacter? = null
private val nfcAdapter: NfcAdapter private val nfcAdapter: NfcAdapter
init { init {
@@ -43,10 +44,14 @@ class ScanScreenControllerImpl(
checkSecrets() checkSecrets()
} }
override fun onClickRead(secrets: Secrets, onComplete: ()->Unit) { override fun onClickRead(secrets: Secrets, onComplete: ()->Unit, onMultipleCards: (List<Card>) -> Unit) {
handleTag(secrets) { tagCommunicator -> handleTag(secrets) { tagCommunicator ->
val character = tagCommunicator.receiveCharacter() val character = tagCommunicator.receiveCharacter()
val resultMessage = characterFromNfc(character) val resultMessage = characterFromNfc(character) { cards, nfcCharacter ->
lastScannedCharacter = nfcCharacter
onMultipleCards(cards)
}
onComplete.invoke() onComplete.invoke()
resultMessage resultMessage
} }
@@ -156,11 +161,14 @@ class ScanScreenControllerImpl(
componentActivity.startActivity(Intent(Settings.ACTION_WIRELESS_SETTINGS)) componentActivity.startActivity(Intent(Settings.ACTION_WIRELESS_SETTINGS))
} }
override fun characterFromNfc(nfcCharacter: NfcCharacter): String { override fun characterFromNfc(
nfcCharacter: NfcCharacter,
onMultipleCards: (List<Card>, NfcCharacter) -> Unit
): String {
val nfcConverter = FromNfcConverter( val nfcConverter = FromNfcConverter(
componentActivity = componentActivity componentActivity = componentActivity
) )
return nfcConverter.addCharacter(nfcCharacter) return nfcConverter.addCharacter(nfcCharacter, onMultipleCards)
} }
override suspend fun characterToNfc(characterId: Long): NfcCharacter { override suspend fun characterToNfc(characterId: Long): NfcCharacter {
@@ -172,4 +180,17 @@ class ScanScreenControllerImpl(
Log.d("CharacterType", character.toString()) Log.d("CharacterType", character.toString())
return character return character
} }
override fun flushCharacter(cardId: Long) {
val nfcConverter = FromNfcConverter(
componentActivity = componentActivity
)
componentActivity.lifecycleScope.launch(Dispatchers.IO) {
if (lastScannedCharacter != null) {
nfcConverter.addCharacterUsingCard(lastScannedCharacter!!, cardId)
lastScannedCharacter = null
}
}
}
} }
@@ -0,0 +1,48 @@
package com.github.nacabaro.vbhelper.screens.cardScreen
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.github.nacabaro.vbhelper.components.TopBanner
import com.github.nacabaro.vbhelper.domain.card.Card
import com.github.nacabaro.vbhelper.screens.scanScreen.cardSelect.ScanCardEntry
import com.github.nacabaro.vbhelper.utils.BitmapData
@Composable
fun ChooseCard(
cards: List<Card>,
onCardSelected: (Card) -> Unit
) {
Scaffold (
topBar = {
TopBanner(
text = "Choose card",
)
}
) { contentPadding ->
LazyColumn (
modifier = Modifier
.padding(top = contentPadding.calculateTopPadding())
) {
items(cards) {
ScanCardEntry(
name = it.name,
logo = BitmapData(
it.logo,
it.logoWidth,
it.logoHeight
),
onClick = {
onCardSelected(it)
},
modifier = Modifier
.padding(8.dp)
)
}
}
}
}
@@ -1,4 +1,4 @@
package com.github.nacabaro.vbhelper.components package com.github.nacabaro.vbhelper.screens.scanScreen.cardSelect
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
@@ -21,11 +21,9 @@ import com.github.nacabaro.vbhelper.utils.BitmapData
import com.github.nacabaro.vbhelper.utils.getBitmap import com.github.nacabaro.vbhelper.utils.getBitmap
@Composable @Composable
fun DexDiMEntry( fun ScanCardEntry(
name: String, name: String,
logo: BitmapData, logo: BitmapData,
obtainedCharacters: Int,
totalCharacters: Int,
onClick: () -> Unit, onClick: () -> Unit,
modifier: Modifier = Modifier modifier: Modifier = Modifier
) { ) {
@@ -56,17 +54,12 @@ fun DexDiMEntry(
Column( Column(
modifier = Modifier modifier = Modifier
.padding(8.dp) .padding(8.dp)
.weight(1f)
) { ) {
Text( Text(
text = name, text = name,
modifier = Modifier modifier = Modifier
) )
Text(
text = "$obtainedCharacters of $totalCharacters characters obtained",
fontFamily = MaterialTheme.typography.labelSmall.fontFamily,
fontSize = MaterialTheme.typography.labelSmall.fontSize,
modifier = Modifier
)
} }
} }
} }
@@ -23,15 +23,68 @@ class FromNfcConverter (
private val database = application.container.db private val database = application.container.db
fun addCharacterUsingCard(
fun addCharacter(nfcCharacter: NfcCharacter): String { nfcCharacter: NfcCharacter,
cardId: Long
): String {
val cardData = database val cardData = database
.cardDao() .cardDao()
.getDimById(nfcCharacter.dimId.toInt()) .getCardById(cardId)
if (cardData == null) if (cardData == null) {
return "Card not found"
}
return insertCharacter(nfcCharacter, cardData)
}
fun addCharacter(
nfcCharacter: NfcCharacter,
onMultipleCards: (List<Card>, NfcCharacter) -> Unit
): String {
val appReservedCardId = nfcCharacter
.appReserved2[0].toLong()
var cardData: Card? = null
if (appReservedCardId != 0L) {
val fetchedCard = database
.cardDao()
.getCardById(appReservedCardId)
if (fetchedCard == null) {
return "Card not found"
} else if (fetchedCard.cardId == nfcCharacter.dimId.toInt()) {
cardData = fetchedCard
}
}
if (cardData == null) {
val allCards = database
.cardDao()
.getCardByCardId(nfcCharacter.dimId.toInt())
if (allCards.isEmpty())
return "Card not found" return "Card not found"
if (allCards.size > 1) {
onMultipleCards(allCards, nfcCharacter)
return "Multiple cards found"
}
cardData = allCards[0]
}
return insertCharacter(nfcCharacter, cardData)
}
private fun insertCharacter(
nfcCharacter: NfcCharacter,
cardData: Card
): String {
val cardCharData = database val cardCharData = database
.characterDao() .characterDao()
.getCharacterByMonIndex(nfcCharacter.charIndex.toInt(), cardData.id) .getCharacterByMonIndex(nfcCharacter.charIndex.toInt(), cardData.id)
@@ -1,6 +1,7 @@
package com.github.nacabaro.vbhelper.screens.scanScreen.converters package com.github.nacabaro.vbhelper.screens.scanScreen.converters
import android.icu.util.Calendar import android.icu.util.Calendar
import android.icu.util.TimeZone
import android.util.Log import android.util.Log
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import com.github.cfogrady.vbnfc.be.BENfcCharacter import com.github.cfogrady.vbnfc.be.BENfcCharacter
@@ -84,7 +85,7 @@ class ToNfcConverter(
transformationHistory = paddedTransformationArray, transformationHistory = paddedTransformationArray,
vitalHistory = generateVitalsHistoryArray(characterId), vitalHistory = generateVitalsHistoryArray(characterId),
appReserved1 = ByteArray(12) {0}, appReserved1 = ByteArray(12) {0},
appReserved2 = Array(3) {0u}, appReserved2 = generateUShortAppReserved(userCharacter),
generation = vbData.generation.toUShort(), generation = vbData.generation.toUShort(),
totalTrophies = vbData.totalTrophies.toUShort(), totalTrophies = vbData.totalTrophies.toUShort(),
specialMissions = watchSpecialMissions.toTypedArray() specialMissions = watchSpecialMissions.toTypedArray()
@@ -94,6 +95,23 @@ class ToNfcConverter(
} }
private suspend fun generateUShortAppReserved(
userCharacter: UserCharacter
): Array<UShort> {
val cardData = database
.cardDao()
.getCardByCharacterId(userCharacter.id)
val appReserved = Array<UShort>(3) {
0u
}
appReserved[0] = cardData.id.toUShort()
return appReserved
}
private suspend fun generateSpecialMissionsArray( private suspend fun generateSpecialMissionsArray(
characterId: Long characterId: Long
@@ -226,16 +244,23 @@ class ToNfcConverter(
.getTransformationHistory(characterId)!! .getTransformationHistory(characterId)!!
.map { .map {
val date = Date(it.transformationDate) val date = Date(it.transformationDate)
val calendar = android.icu.util.GregorianCalendar() val calendar = android.icu.util.GregorianCalendar(TimeZone.getTimeZone("UTC"))
calendar.time = date calendar.time = date
Log.d(
"TransformationHistory",
"Year: ${calendar.get(Calendar.YEAR)}, " +
"Month: ${calendar.get(Calendar.MONTH) + 1}, " +
"Day: ${calendar.get(Calendar.DAY_OF_MONTH)}"
)
NfcCharacter.Transformation( NfcCharacter.Transformation(
toCharIndex = it.monIndex.toUByte(), toCharIndex = it.monIndex.toUByte(),
year = calendar year = calendar
.get(Calendar.YEAR) .get(Calendar.YEAR)
.toUShort(), .toUShort(),
month = calendar month = (calendar
.get(Calendar.MONTH) .get(Calendar.MONTH) + 1)
.toUByte(), .toUByte(),
day = calendar day = calendar
.get(Calendar.DAY_OF_MONTH) .get(Calendar.DAY_OF_MONTH)
@@ -35,6 +35,7 @@ fun CreditsScreen(
SettingsEntry(title = "cfogrady", description = "Developed vb-lib-nfc and part of this application.") { } SettingsEntry(title = "cfogrady", description = "Developed vb-lib-nfc and part of this application.") { }
SettingsEntry(title = "nacabaro", description = "Developed this application.") { } SettingsEntry(title = "nacabaro", description = "Developed this application.") { }
SettingsEntry(title = "lightheel", description = "Developing the battling part for this application, including server. Still in the works.") { } SettingsEntry(title = "lightheel", description = "Developing the battling part for this application, including server. Still in the works.") { }
SettingsEntry(title = "shvstrz", description = "Designing the app icon in SVG.") { }
} }
} }
} }
@@ -129,7 +129,7 @@ class SettingsScreenControllerImpl(
val dimId = database val dimId = database
.cardDao() .cardDao()
.insertNewDim(cardModel) .insertNewCard(cardModel)
val cardProgress = CardProgress( val cardProgress = CardProgress(
cardId = dimId, cardId = dimId,
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:pathData="M200,760h57l391,-391 -57,-57 -391,391v57ZM120,840v-170l528,-527q12,-11 26.5,-17t30.5,-6q16,0 31,6t26,18l55,56q12,11 17.5,26t5.5,30q0,16 -5.5,30.5T817,313L290,840L120,840ZM760,256 L704,200 760,256ZM619,341 L591,312 648,369 619,341Z"
android:fillColor="#000000"/>
</vector>
@@ -1,30 +1,39 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" <vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp" android:width="108dp"
android:height="108dp" android:height="108dp"
android:viewportWidth="108" android:viewportWidth="512"
android:viewportHeight="108"> android:viewportHeight="512">
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z"> <group android:scaleX="0.79"
<aapt:attr name="android:fillColor"> android:scaleY="0.79"
<gradient android:translateX="53.76"
android:endX="85.84757" android:translateY="53.76">
android:endY="92.4963" <group>
android:startX="42.9492" <clip-path
android:startY="49.59793" android:pathData="M0,0h512v512h-512z"/>
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path <path
android:fillColor="#FFFFFF" android:pathData="M0,0h512v512h-512z"
android:fillType="nonZero" android:fillColor="#000000"/>
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z" <path
android:strokeWidth="1" android:pathData="M164,107L186.19,107L186.19,129C186.19,132.86 189.32,136 193.19,136L219,136L235.95,163.3C236.53,164.24 237.9,165 239,165L267,165C268.1,165 269.47,164.24 270.05,163.3L287,136L312.8,136C316.66,136 319.8,132.86 319.8,129L319.8,107L313.1,107L313.1,128C313.1,129.1 312.2,130 311.1,130L283,130L267.04,156.29C266.46,157.23 265.1,158 264,158L242,158C240.9,158 239.54,157.23 238.96,156.29L223,130L194.94,130C193.83,130 192.94,129.1 192.94,128L192.94,107L217.75,107L229,92L277,92L288,107L342,107L342,405L319.81,405L319.81,383C319.81,379.14 316.68,376 312.81,376L287,376L270.05,348.7C269.47,347.76 268.1,347 267,347L239,347C237.9,347 236.53,347.76 235.95,348.7L219,376L193.2,376C189.34,376 186.2,379.14 186.2,383L186.2,405L192.94,405L192.94,384C192.94,382.9 193.83,382 194.94,382L223,382L238.96,355.71C239.54,354.77 240.9,354 242,354L264,354C265.1,354 266.46,354.77 267.04,355.71L283,382L311.06,382C312.17,382 313.06,382.9 313.06,384L313.06,405L288.25,405L277,420L229,420L218,405L164,405L164,107Z"
android:strokeColor="#00000000" /> android:fillColor="#EBEBEB"/>
<path
android:pathData="M212.61,181.5L299.39,181.5C306.15,181.5 311.63,186.98 311.63,193.74L311.63,318.26C311.63,325.02 306.15,330.5 299.39,330.5L212.61,330.5C205.85,330.5 200.37,325.02 200.37,318.26L200.37,193.74C200.37,186.98 205.85,181.5 212.61,181.5Z"
android:fillColor="#000000"
android:strokeColor="#00000000"/>
<path
android:pathData="M0,249L192.88,249L214,208L232.38,249L253.69,171L274.85,255L296,194L310.27,249L512,249L512,269L296,269L294,261L274.85,331L262.59,283.5L253.69,249L232.38,316L213.38,255L205.75,269L0,269L0,249Z"
android:fillColor="#EBEBEB"/>
<group>
<clip-path
android:pathData="M-1024,-1024h2048v2048h-2048zM0,249L192.88,249L214,208L232.38,249L253.69,171L274.85,255L296,194L310.27,249L512,249L512,269L296,269L294,261L274.85,331L262.59,283.5L253.69,249L232.38,316L213.38,255L205.75,269L0,269L0,249Z"/>
<path
android:pathData="M0,249L192.88,249L214,208L232.38,249L253.69,171L274.85,255L296,194L310.27,249L512,249L512,269L296,269L294,261L274.85,331L262.59,283.5L253.69,249L232.38,316L213.38,255L205.75,269L0,269L0,249Z"
android:strokeLineJoin="round"
android:strokeWidth="10"
android:fillColor="#EBEBEB"
android:strokeColor="#000000"
android:strokeLineCap="square"/>
</group>
</group>
</group>
</vector> </vector>
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" /> <background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground" /> <foreground android:drawable="@drawable/ic_launcher_foreground"/>
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon> </adaptive-icon>
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" /> <background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground" /> <foreground android:drawable="@drawable/ic_launcher_foreground"/>
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon> </adaptive-icon>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 938 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 982 B

After

Width:  |  Height:  |  Size: 658 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_background">#000000</color>
</resources>
+1 -1
View File
@@ -1,5 +1,5 @@
[versions] [versions]
agp = "8.7.3" agp = "8.12.2"
datastore = "1.1.2" datastore = "1.1.2"
kotlin = "2.0.0" kotlin = "2.0.0"
coreKtx = "1.15.0" coreKtx = "1.15.0"
+1 -1
View File
@@ -1,6 +1,6 @@
#Tue Dec 24 10:55:57 UTC 2024 #Tue Dec 24 10:55:57 UTC 2024
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists