From 4d8dcde26e1c80c85305abe6736f9f9d48bc95fb Mon Sep 17 00:00:00 2001 From: Nacho Date: Mon, 4 Aug 2025 13:15:07 +0200 Subject: [PATCH 01/10] Forgot to add all the UI methods to preview VB characters --- .../screens/homeScreens/HomeScreen.kt | 8 +- .../screens/homeScreens/VBDiMHomeScreen.kt | 141 +++++++++++++++++- .../scanScreen/ScanScreenControllerImpl.kt | 17 ++- .../scanScreen/converters/ToNfcConverter.kt | 12 +- .../vbhelper/source/StorageRepository.kt | 5 + 5 files changed, 174 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/HomeScreen.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/HomeScreen.kt index 26931ad..b0ca324 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/HomeScreen.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/HomeScreen.kt @@ -1,5 +1,6 @@ package com.github.nacabaro.vbhelper.screens.homeScreens +import android.util.Log import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize @@ -51,9 +52,12 @@ fun HomeScreen( LaunchedEffect(storageRepository, activeMon) { withContext(Dispatchers.IO) { activeMon.value = storageRepository.getActiveCharacter() - if (activeMon.value != null) { + if (activeMon.value != null && activeMon.value!!.characterType == DeviceType.BEDevice) { beData.value = storageRepository.getCharacterBeData(activeMon.value!!.id) transformationHistory.value = storageRepository.getTransformationHistory(activeMon.value!!.id) + } else if (activeMon.value != null && activeMon.value!!.characterType == DeviceType.VBDevice) { + vbData.value = storageRepository.getCharacterVbData(activeMon.value!!.id) + transformationHistory.value = storageRepository.getTransformationHistory(activeMon.value!!.id) } } } @@ -79,6 +83,7 @@ fun HomeScreen( } ) { contentPadding -> if (activeMon.value == null || (beData.value == null && vbData.value == null) || transformationHistory.value == null) { + Log.d("TetTet", "Something is null") Column ( horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center, @@ -89,6 +94,7 @@ fun HomeScreen( Text(text = "Nothing to see here") } } else { + Log.d("TetTet", "Something is not null") if (activeMon.value!!.isBemCard) { BEBEmHomeScreen( activeMon = activeMon.value!!, diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/VBDiMHomeScreen.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/VBDiMHomeScreen.kt index 2670fa7..a91bd7a 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/VBDiMHomeScreen.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/VBDiMHomeScreen.kt @@ -1,9 +1,24 @@ package com.github.nacabaro.vbhelper.screens.homeScreens +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.aspectRatio +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import com.github.nacabaro.vbhelper.R +import com.github.nacabaro.vbhelper.components.CharacterEntry +import com.github.nacabaro.vbhelper.components.ItemDisplay +import com.github.nacabaro.vbhelper.components.TransformationHistoryCard import com.github.nacabaro.vbhelper.domain.device_data.VBCharacterData import com.github.nacabaro.vbhelper.dtos.CharacterDtos +import com.github.nacabaro.vbhelper.utils.BitmapData +import java.util.Locale @Composable fun VBDiMHomeScreen( @@ -12,5 +27,129 @@ fun VBDiMHomeScreen( transformationHistory: List, contentPadding: PaddingValues ) { - TODO("Not implemented yet") + Column( + modifier = Modifier + .padding(top = contentPadding.calculateTopPadding()) + .verticalScroll(state = rememberScrollState()) + ) { + Row( + modifier = Modifier + .fillMaxWidth() + ) { + CharacterEntry( + icon = BitmapData( + bitmap = activeMon.spriteIdle, + width = activeMon.spriteWidth, + height = activeMon.spriteHeight + ), + multiplier = 8, + shape = androidx.compose.material.MaterialTheme.shapes.small, + modifier = Modifier + .weight(1f) + .aspectRatio(1f) + ) + Column( + modifier = Modifier + .weight(0.5f) + .aspectRatio(0.5f) + ) { + ItemDisplay( + icon = R.drawable.baseline_vitals_24, + textValue = activeMon.vitalPoints.toString(), + definition = "Vitals", + modifier = Modifier + .weight(0.5f) + .aspectRatio(1f) + .padding(8.dp) + ) + ItemDisplay( + icon = R.drawable.baseline_trophy_24, + textValue = activeMon.trophies.toString(), + definition = "Trophies", + modifier = Modifier + .weight(0.5f) + .aspectRatio(1f) + .padding(8.dp) + ) + } + } + Row( + modifier = Modifier + .fillMaxWidth() + ) { + ItemDisplay( + icon = R.drawable.baseline_mood_24, + textValue = activeMon.mood.toString(), + definition = "Mood", + modifier = Modifier + .weight(1f) + .aspectRatio(1f) + .padding(8.dp) + ) + val transformationCountdownInHours = activeMon.transformationCountdown / 60 + ItemDisplay( + icon = R.drawable.baseline_next_24, + textValue = when (transformationCountdownInHours) { + 0 -> "${activeMon.transformationCountdown} m" + else -> "$transformationCountdownInHours h" + }, + definition = "Next timer", + modifier = Modifier + .weight(1f) + .aspectRatio(1f) + .padding(8.dp) + ) + ItemDisplay( + icon = R.drawable.baseline_swords_24, + textValue = when { + activeMon.totalBattlesLost == 0 -> "0.00 %" + else -> { + val battleWinPercentage = + activeMon.totalBattlesWon.toFloat() / (activeMon.totalBattlesWon + activeMon.totalBattlesLost).toFloat() + String.format( + Locale.getDefault(), + "%.2f", + battleWinPercentage * 100 + ) + " %" // Specify locale + } + }, + definition = "Total battle win %", + modifier = Modifier + .weight(1f) + .aspectRatio(1f) + .padding(8.dp) + ) + ItemDisplay( + icon = R.drawable.baseline_swords_24, + textValue = when { + activeMon.totalBattlesLost == 0 -> "0.00 %" + else -> { + val battleWinPercentage = + activeMon.currentPhaseBattlesWon.toFloat() / (activeMon.currentPhaseBattlesWon + activeMon.currentPhaseBattlesLost).toFloat() + String.format( + Locale.getDefault(), + "%.2f", + battleWinPercentage * 100 + ) + " %" // Specify locale + } + }, + definition = "Current phase win %", + modifier = Modifier + .weight(1f) + .aspectRatio(1f) + .padding(8.dp) + ) + } + Row( + modifier = Modifier + .fillMaxWidth() + ) { + TransformationHistoryCard( + transformationHistory = transformationHistory, + modifier = Modifier + .weight(1f) + .padding(8.dp) + ) + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/ScanScreenControllerImpl.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/ScanScreenControllerImpl.kt index f610f5f..7721ceb 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/ScanScreenControllerImpl.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/ScanScreenControllerImpl.kt @@ -11,7 +11,9 @@ import android.widget.Toast import androidx.activity.ComponentActivity import androidx.lifecycle.lifecycleScope import com.github.cfogrady.vbnfc.TagCommunicator +import com.github.cfogrady.vbnfc.be.BENfcCharacter import com.github.cfogrady.vbnfc.data.NfcCharacter +import com.github.cfogrady.vbnfc.vb.VBNfcCharacter import com.github.nacabaro.vbhelper.ActivityLifecycleListener import com.github.nacabaro.vbhelper.screens.scanScreen.converters.FromNfcConverter import com.github.nacabaro.vbhelper.screens.scanScreen.converters.ToNfcConverter @@ -118,7 +120,15 @@ class ScanScreenControllerImpl( ) { handleTag(secrets) { tagCommunicator -> try { - tagCommunicator.sendCharacter(nfcCharacter) + if (nfcCharacter is VBNfcCharacter) { + Log.d("SendCharacter", "VBNfcCharacter") + val castNfcCharacter: VBNfcCharacter = nfcCharacter + tagCommunicator.sendCharacter(castNfcCharacter) + } else if (nfcCharacter is BENfcCharacter) { + Log.d("SendCharacter", "BENfcCharacter") + val castNfcCharacter: BENfcCharacter = nfcCharacter + tagCommunicator.sendCharacter(castNfcCharacter) + } onComplete.invoke() "Sent character successfully!" } catch (e: Throwable) { @@ -157,6 +167,9 @@ class ScanScreenControllerImpl( val nfcGenerator = ToNfcConverter( componentActivity = componentActivity ) - return nfcGenerator.characterToNfc(characterId) + + val character = nfcGenerator.characterToNfc(characterId) + Log.d("CharacterType", character.toString()) + return character } } \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/converters/ToNfcConverter.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/converters/ToNfcConverter.kt index 819aa2a..db0f88d 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/converters/ToNfcConverter.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/converters/ToNfcConverter.kt @@ -59,7 +59,7 @@ class ToNfcConverter( .userCharacterDao() .getVbData(characterId) - val paddedTransformationArray = generateTransformationHistory(characterId) + val paddedTransformationArray = generateTransformationHistory(characterId, 9) val watchSpecialMissions = generateSpecialMissionsArray(characterId) @@ -218,7 +218,8 @@ class ToNfcConverter( private suspend fun generateTransformationHistory( - characterId: Long + characterId: Long, + length: Int = 8 ): Array { val transformationHistory = database .userCharacterDao() @@ -242,7 +243,7 @@ class ToNfcConverter( ) }.toTypedArray() - val paddedTransformationArray = padTransformationArray(transformationHistory) + val paddedTransformationArray = padTransformationArray(transformationHistory, length) return paddedTransformationArray } @@ -250,13 +251,14 @@ class ToNfcConverter( private fun padTransformationArray( - transformationArray: Array + transformationArray: Array, + length: Int ): Array { if (transformationArray.size >= 8) { return transformationArray } - val paddedArray = Array(8) { + val paddedArray = Array(length) { NfcCharacter.Transformation( toCharIndex = 255u, year = 65535u, diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/source/StorageRepository.kt b/app/src/main/java/com/github/nacabaro/vbhelper/source/StorageRepository.kt index d4caee8..65bdeb0 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/source/StorageRepository.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/source/StorageRepository.kt @@ -2,6 +2,7 @@ package com.github.nacabaro.vbhelper.source import com.github.nacabaro.vbhelper.database.AppDatabase import com.github.nacabaro.vbhelper.domain.device_data.BECharacterData +import com.github.nacabaro.vbhelper.domain.device_data.VBCharacterData import com.github.nacabaro.vbhelper.dtos.CharacterDtos class StorageRepository ( @@ -23,6 +24,10 @@ class StorageRepository ( return db.userCharacterDao().getTransformationHistory(characterId) } + suspend fun getCharacterVbData(id: Long): VBCharacterData { + return db.userCharacterDao().getVbData(id) + } + suspend fun getActiveCharacter(): CharacterDtos.CharacterWithSprites? { return db.userCharacterDao().getActiveCharacter() } From 7bb7693876b1164746518000c0afb9778280691b Mon Sep 17 00:00:00 2001 From: Nacho Date: Mon, 4 Aug 2025 19:57:39 +0200 Subject: [PATCH 02/10] Version Up --- app/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 753ad0e..5f7830d 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -15,7 +15,7 @@ android { minSdk = 28 targetSdk = 35 versionCode = 1 - versionName = "Alpha 0.2" + versionName = "Alpha 0.3" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } From f8ce81e932dd5c6163724cbe7231983a55f3d677 Mon Sep 17 00:00:00 2001 From: Nacho Date: Wed, 6 Aug 2025 01:19:19 +0200 Subject: [PATCH 03/10] Special missions - Basic implementation is complete - Added 8 different special missions, 4 are easy and more expensive, and 4 are cheaper and more difficult - Added 9 missions of each so people can test themselves - Also added checks to disallow BE digimon to have special missions - UI elements to display the status of the missions - Finishing a mission awards a random price (TODO: Make the price be based on the mission difficulty) --- app/src/main/assets/items.db | Bin 16384 -> 16384 bytes .../vbhelper/components/CharacterEntry.kt | 92 ++++++++++++++++ .../github/nacabaro/vbhelper/daos/ItemDao.kt | 4 +- .../vbhelper/daos/SpecialMissionDao.kt | 15 +++ .../vbhelper/daos/UserCharacterDao.kt | 52 ++++++++- .../nacabaro/vbhelper/database/AppDatabase.kt | 2 + .../nacabaro/vbhelper/domain/items/Items.kt | 10 +- .../github/nacabaro/vbhelper/dtos/ItemDtos.kt | 6 +- .../AdventureScreenControllerImpl.kt | 3 +- .../screens/homeScreens/HomeScreen.kt | 27 ++++- .../homeScreens/HomeScreenController.kt | 3 + .../homeScreens/HomeScreenControllerImpl.kt | 37 +++++++ .../{ => screens}/BEBEmHomeScreen.kt | 2 +- .../{ => screens}/BEDiMHomeScreen.kt | 2 +- .../{ => screens}/VBDiMHomeScreen.kt | 38 ++++++- .../itemsScreen/ChooseCharacterScreen.kt | 19 +++- .../itemsScreen/ItemsScreenControllerImpl.kt | 103 ++++++++++++++++-- .../vbhelper/source/StorageRepository.kt | 18 +++ 18 files changed, 410 insertions(+), 23 deletions(-) create mode 100644 app/src/main/java/com/github/nacabaro/vbhelper/daos/SpecialMissionDao.kt rename app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/{ => screens}/BEBEmHomeScreen.kt (99%) rename app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/{ => screens}/BEDiMHomeScreen.kt (99%) rename app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/{ => screens}/VBDiMHomeScreen.kt (81%) diff --git a/app/src/main/assets/items.db b/app/src/main/assets/items.db index 80a883beb7fd6788fdcd27cd3dd0822b17cc3111..b67ef1dae747cf6756e77008cfa16598fb134b40 100644 GIT binary patch literal 16384 zcmeI2&u<$=6o6;_bH}lrK!7xqCexIVSWT#%#3WTx72DmysNzzuK%+yFPg4R8b8z_Vx|oD*iB#lBCfXZ(4|_037a zI0t`zyxGaZoy&gzz`%g;@tUEomo(YX%C-6rS1KnZ6bUJbWLiQa_1Fj+55N(9V+6`s zMS4rhq3oi9vdii81ddnKvNi%0senUFctfidtNN~??^HTu@ki%!TB~dtTb-dKFBeH^ zr%Ucu^&-Azr_}p7cRYtbAR{KJbWo9 zXA-#^=$dq6oQQ}7A|Y=GjJgl!dcW??h7ZGEg;Sy5L!X7_g1-bm30~;=x#v#LWZ+TY z^T1*t0Dpj6@QnDYcwf9NUhqHh|LFh7FMAfz3xZ3Ctmb#MqOO)uMz7WIA-9HRwo%Q{c57%nhPHP0s*x47w2fw> z(P$&CSCE-m2!JD)Uy@RCBAt=*d3iCrIt0OT!qu5PN6FK5a?M1~SLQ?_}uC)lZk zY*V>;7J?P0)t!n*(FeMrmRj3VQmYlj#EG=JsD+@d|62%x)s({`#K`_@!kas@PG>W0 z^F6la0f?W0U^Ip=Ar47md?Hm!MRz>RXbi32mc9*;D{E+ z2O&5cXA3ilLUL!PW|u zczrR&e7O$n`g^`lO0ptl@R^c%0fO0O21QP)Dmsr}{p66XwHj*Y?vpdCKE_q>`$bB; z)(=5EZzsl&2R4QX6Mk2L@>0)Zp1DR@@-EnHaB^zKkY@bvsLzckB-sZ+EP)R-ahUDojr#G`-iH|_NBx0Mn4pZMUI;F&5XNAlR6EQvI>t;(eF>4N_fhFZ+Xf>-#IDq0FVL(LmY7ijCw6B+V?qZTye42{LfeXI-A z98kZ}^?Jb<6q*l0Fs}*_Pk(6%Db1~FpFPfQ>{ZD&riyF|7z6npN3KHtO&+~0_$9|4U%0w;Owy;Jd6lMq~JtXFwPzecC%nV1;5=DjI+mrT`br~ z!R;!nOXsA5T{;i5c8ZsR+f`V>PAXWin+1C)xLt)6?4*JPyI8QBg4X2Ajlx2v#%om8-37Xv=&x4WFj4R8b805`x5a0A=`H^2>W1Ka>N wzzuK%Pptv+zx?i}wmaS!H^2>W1Ka>NzzuK%+yFPg4R8b805`x5{3iqd0;x^hTmS$7 delta 705 zcmY+8Pfrs;7{+IoQf6oWOtIG8c027#17(9sH%e+%FiXJD$O=JGk%RHiMQIF1O}tt3 zqA|w8Ts%nh0~lk&Nn<#P(VI6vf(O!gGqERU2DZe*Wb*qx&%E>2>#$yj4X4)z0K|5W z!f$u=&`~ar+<*orXtsdaGF#2%U;1jJqQI-ol|_gjC%Stq%YsiHv2W&|`PsQ@7|boW zXD1g1`)=Lr^ROo#Q?vv~lUyfB^Nabw958+wPmPMct*`4ddQAJM-O-NWzjzZr#3l8& z`dNLZUQ|8QM&HpZwA5Vj5FVlk^!o}_5W8R%@NkwR--6?FtB8l44u0i$IDMhvvW?kHmCa=aOf ua7s2m?KTzO?24kbJTEE{1xzd{K7W}fS_mUQCH;?laRa*X#74Z-CjS5g!iU5F diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/components/CharacterEntry.kt b/app/src/main/java/com/github/nacabaro/vbhelper/components/CharacterEntry.kt index 114e946..16ae708 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/components/CharacterEntry.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/components/CharacterEntry.kt @@ -5,11 +5,14 @@ import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.aspectRatio +import androidx.compose.foundation.layout.fillMaxHeight 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.CardDefaults import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text @@ -28,6 +31,9 @@ import com.github.nacabaro.vbhelper.utils.getBitmap import androidx.compose.ui.graphics.Shape import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.font.FontWeight +import com.github.cfogrady.vbnfc.vb.SpecialMission +import com.github.nacabaro.vbhelper.R +import com.github.nacabaro.vbhelper.domain.device_data.SpecialMissions import com.github.nacabaro.vbhelper.utils.getObscuredBitmap @Composable @@ -114,4 +120,90 @@ fun ItemDisplay( ) } } +} + +@Composable +fun SpecialMissionsEntry( + specialMission: SpecialMissions, + modifier: Modifier = Modifier, + onClickCard: () -> Unit = { }, +) { + val textValue = when (specialMission.missionType) { + SpecialMission.Type.NONE -> "No mission selected" + SpecialMission.Type.STEPS -> "Walk ${specialMission.goal} steps" + SpecialMission.Type.BATTLES -> "Battle ${specialMission.goal} times" + SpecialMission.Type.WINS -> "Win ${specialMission.goal} battles" + SpecialMission.Type.VITALS -> "Earn ${specialMission.goal} vitals" + } + + val progress = if (specialMission.status == SpecialMission.Status.COMPLETED) { + specialMission.goal + } else { + specialMission.progress + } + + val completion = when (specialMission.missionType) { + SpecialMission.Type.NONE -> "" + SpecialMission.Type.STEPS -> "Walked $progress steps" + SpecialMission.Type.BATTLES -> "Battled $progress times" + SpecialMission.Type.WINS -> "Won $progress battles" + SpecialMission.Type.VITALS -> "Earned $progress vitals" + } + + val icon = when (specialMission.missionType) { + SpecialMission.Type.NONE -> R.drawable.baseline_free_24 + SpecialMission.Type.STEPS -> R.drawable.baseline_agility_24 + SpecialMission.Type.BATTLES -> R.drawable.baseline_swords_24 + SpecialMission.Type.WINS -> R.drawable.baseline_trophy_24 + SpecialMission.Type.VITALS -> R.drawable.baseline_vitals_24 + } + + val color = when (specialMission.status) + { + SpecialMission.Status.IN_PROGRESS -> MaterialTheme.colorScheme.secondary + SpecialMission.Status.COMPLETED -> MaterialTheme.colorScheme.primary + SpecialMission.Status.FAILED -> MaterialTheme.colorScheme.error + else -> MaterialTheme.colorScheme.surfaceContainerHighest + } + + Card( + modifier = modifier, + shape = androidx.compose.material.MaterialTheme.shapes.small, + onClick = if (specialMission.status == SpecialMission.Status.COMPLETED) { + onClickCard + } else { + { } + }, + colors = CardDefaults.cardColors( + containerColor = color + ) + + ) { + Row ( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(8.dp), + modifier = Modifier + .fillMaxSize() + .padding(16.dp) + ) { + Icon( + painter = painterResource(icon), + contentDescription = "Vitals", + modifier = Modifier + .fillMaxHeight() + .padding(16.dp) + ) + Column { + Text( + text = textValue, + fontFamily = MaterialTheme.typography.titleLarge.fontFamily, + fontWeight = FontWeight.Bold, + ) + Text( + text = completion, + fontFamily = MaterialTheme.typography.titleSmall.fontFamily, + ) + } + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/daos/ItemDao.kt b/app/src/main/java/com/github/nacabaro/vbhelper/daos/ItemDao.kt index a655369..1164815 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/daos/ItemDao.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/daos/ItemDao.kt @@ -31,7 +31,7 @@ interface ItemDao { WHERE Items.id = :itemId """ ) - fun getItem(itemId: Long): ItemDtos.ItemsWithQuantities + suspend fun getItem(itemId: Long): ItemDtos.ItemsWithQuantities @Query( """ @@ -40,7 +40,7 @@ interface ItemDao { WHERE id = :itemId """ ) - fun useItem(itemId: Long) + suspend fun useItem(itemId: Long) @Query( """ diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/daos/SpecialMissionDao.kt b/app/src/main/java/com/github/nacabaro/vbhelper/daos/SpecialMissionDao.kt new file mode 100644 index 0000000..7dc5c74 --- /dev/null +++ b/app/src/main/java/com/github/nacabaro/vbhelper/daos/SpecialMissionDao.kt @@ -0,0 +1,15 @@ +package com.github.nacabaro.vbhelper.daos + +import androidx.room.Dao +import androidx.room.Query + +@Dao +interface SpecialMissionDao { + @Query(""" + UPDATE SpecialMissions SET + missionType = "NONE", + status = "UNAVAILABLE" + WHERE id = :id + """) + suspend fun clearSpecialMission(id: Long) +} \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/daos/UserCharacterDao.kt b/app/src/main/java/com/github/nacabaro/vbhelper/daos/UserCharacterDao.kt index 39d43ee..352f180 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/daos/UserCharacterDao.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/daos/UserCharacterDao.kt @@ -34,7 +34,7 @@ interface UserCharacterDao { @Insert(onConflict = OnConflictStrategy.REPLACE) fun insertTransformationHistory(vararg transformationHistory: TransformationHistory) - @Insert + @Upsert fun insertSpecialMissions(vararg specialMissions: SpecialMissions) @Query(""" @@ -172,4 +172,54 @@ interface UserCharacterDao { @Query("""SELECT * FROM VitalsHistory WHERE charId = :charId ORDER BY id ASC""") suspend fun getVitalsHistory(charId: Long): List + + @Query( + """ + SELECT + uc.*, + c.stage, + c.attribute, + s.spriteIdle1 AS spriteIdle, + s.spriteIdle2 AS spriteIdle2, + s.width AS spriteWidth, + s.height AS spriteHeight, + c.name as nameSprite, + c.nameWidth as nameSpriteWidth, + c.nameHeight as nameSpriteHeight, + d.isBEm as isBemCard, + a.characterId = uc.id as isInAdventure + FROM UserCharacter uc + JOIN Character c ON uc.charId = c.id + JOIN Card d ON d.id = c.dimId + JOIN Sprite s ON s.id = c.spriteId + LEFT JOIN Adventure a ON a.characterId = uc.id + WHERE d.isBEm = 1 + """ + ) + suspend fun getBEBemCharacters(): List + + @Query( + """ + SELECT + uc.*, + c.stage, + c.attribute, + s.spriteIdle1 AS spriteIdle, + s.spriteIdle2 AS spriteIdle2, + s.width AS spriteWidth, + s.height AS spriteHeight, + c.name as nameSprite, + c.nameWidth as nameSpriteWidth, + c.nameHeight as nameSpriteHeight, + d.isBEm as isBemCard, + a.characterId = uc.id as isInAdventure + FROM UserCharacter uc + JOIN Character c ON uc.charId = c.id + JOIN Card d ON d.id = c.dimId + JOIN Sprite s ON s.id = c.spriteId + LEFT JOIN Adventure a ON a.characterId = uc.id + WHERE uc.characterType = "VBDevice" + """ + ) + suspend fun getVBDimCharacters(): List } \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/database/AppDatabase.kt b/app/src/main/java/com/github/nacabaro/vbhelper/database/AppDatabase.kt index 1cd5dfe..0f963ce 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/database/AppDatabase.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/database/AppDatabase.kt @@ -8,6 +8,7 @@ import com.github.nacabaro.vbhelper.daos.DexDao import com.github.nacabaro.vbhelper.daos.CardDao import com.github.nacabaro.vbhelper.daos.CardProgressDao import com.github.nacabaro.vbhelper.daos.ItemDao +import com.github.nacabaro.vbhelper.daos.SpecialMissionDao import com.github.nacabaro.vbhelper.daos.SpriteDao import com.github.nacabaro.vbhelper.daos.UserCharacterDao import com.github.nacabaro.vbhelper.domain.characters.Character @@ -51,4 +52,5 @@ abstract class AppDatabase : RoomDatabase() { abstract fun itemDao(): ItemDao abstract fun adventureDao(): AdventureDao abstract fun spriteDao(): SpriteDao + abstract fun specialMissionDao(): SpecialMissionDao } \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/domain/items/Items.kt b/app/src/main/java/com/github/nacabaro/vbhelper/domain/items/Items.kt index 18dfc68..3a7747e 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/domain/items/Items.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/domain/items/Items.kt @@ -3,6 +3,13 @@ package com.github.nacabaro.vbhelper.domain.items import androidx.room.Entity import androidx.room.PrimaryKey +enum class ItemType { + VBITEM, + BEITEM, + UNIVERSAL, + SPECIALMISSION +} + @Entity data class Items( @PrimaryKey val id: Long, @@ -11,5 +18,6 @@ data class Items( val itemIcon: Int, val itemLength: Int, val price: Int, - val quantity: Int + val quantity: Int, + val itemType: ItemType ) diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/dtos/ItemDtos.kt b/app/src/main/java/com/github/nacabaro/vbhelper/dtos/ItemDtos.kt index e2e7df6..4738280 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/dtos/ItemDtos.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/dtos/ItemDtos.kt @@ -1,5 +1,7 @@ package com.github.nacabaro.vbhelper.dtos +import com.github.nacabaro.vbhelper.domain.items.ItemType + object ItemDtos { data class ItemsWithQuantities( @@ -10,6 +12,7 @@ object ItemDtos { val itemLength: Int, val price: Int, val quantity: Int, + val itemType: ItemType ) data class PurchasedItem( @@ -18,6 +21,7 @@ object ItemDtos { val itemDescription: String, val itemIcon: Int, val itemLength: Int, - val itemAmount: Int + val itemAmount: Int, + val itemType: ItemType ) } \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/adventureScreen/AdventureScreenControllerImpl.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/adventureScreen/AdventureScreenControllerImpl.kt index b775728..535d1f4 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/adventureScreen/AdventureScreenControllerImpl.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/adventureScreen/AdventureScreenControllerImpl.kt @@ -94,7 +94,8 @@ class AdventureScreenControllerImpl( itemName = randomItem.name, itemIcon = randomItem.itemIcon, itemLength = randomItem.itemLength, - itemDescription = randomItem.description + itemDescription = randomItem.description, + itemType = randomItem.itemType ) } } \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/HomeScreen.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/HomeScreen.kt index b0ca324..4a5d3a0 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/HomeScreen.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/HomeScreen.kt @@ -28,9 +28,15 @@ import com.github.nacabaro.vbhelper.components.TopBanner import com.github.nacabaro.vbhelper.di.VBHelper import com.github.nacabaro.vbhelper.utils.DeviceType 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.VBCharacterData import com.github.nacabaro.vbhelper.dtos.CharacterDtos +import com.github.nacabaro.vbhelper.dtos.ItemDtos import com.github.nacabaro.vbhelper.navigation.NavigationItems +import com.github.nacabaro.vbhelper.screens.homeScreens.screens.BEBEmHomeScreen +import com.github.nacabaro.vbhelper.screens.homeScreens.screens.BEDiMHomeScreen +import com.github.nacabaro.vbhelper.screens.homeScreens.screens.VBDiMHomeScreen +import com.github.nacabaro.vbhelper.screens.itemsScreen.ObtainedItemDialog import com.github.nacabaro.vbhelper.source.StorageRepository import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext @@ -46,10 +52,12 @@ fun HomeScreen( val transformationHistory = remember { mutableStateOf?>(null) } val beData = remember { mutableStateOf(null) } val vbData = remember { mutableStateOf(null) } + val vbSpecialMissions = remember { mutableStateOf>(emptyList()) } var adventureMissionsFinished by rememberSaveable { mutableStateOf(false) } var betaWarning by rememberSaveable { mutableStateOf(true) } + var collectedItem by remember { mutableStateOf(null) } - LaunchedEffect(storageRepository, activeMon) { + LaunchedEffect(storageRepository, activeMon, collectedItem) { withContext(Dispatchers.IO) { activeMon.value = storageRepository.getActiveCharacter() if (activeMon.value != null && activeMon.value!!.characterType == DeviceType.BEDevice) { @@ -57,6 +65,7 @@ fun HomeScreen( transformationHistory.value = storageRepository.getTransformationHistory(activeMon.value!!.id) } else if (activeMon.value != null && activeMon.value!!.characterType == DeviceType.VBDevice) { vbData.value = storageRepository.getCharacterVbData(activeMon.value!!.id) + vbSpecialMissions.value = storageRepository.getSpecialMissions(activeMon.value!!.id) transformationHistory.value = storageRepository.getTransformationHistory(activeMon.value!!.id) } } @@ -114,12 +123,26 @@ fun HomeScreen( activeMon = activeMon.value!!, vbData = vbData.value!!, transformationHistory = transformationHistory.value!!, - contentPadding = contentPadding + contentPadding = contentPadding, + specialMissions = vbSpecialMissions.value, + homeScreenController = homeScreenController, + onClickCollect = { item -> + collectedItem = item + } ) } } } + if (collectedItem != null) { + ObtainedItemDialog( + obtainedItem = collectedItem!!, + onClickDismiss = { + collectedItem = null + } + ) + } + if (adventureMissionsFinished) { Dialog( onDismissRequest = { adventureMissionsFinished = false }, diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/HomeScreenController.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/HomeScreenController.kt index b524ba2..52572ce 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/HomeScreenController.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/HomeScreenController.kt @@ -1,5 +1,8 @@ package com.github.nacabaro.vbhelper.screens.homeScreens +import com.github.nacabaro.vbhelper.dtos.ItemDtos + interface HomeScreenController { fun didAdventureMissionsFinish(onCompletion: (Boolean) -> Unit) + fun clearSpecialMission(missionId: Long, onCleared: (ItemDtos.PurchasedItem) -> Unit) } \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/HomeScreenControllerImpl.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/HomeScreenControllerImpl.kt index a38bf0b..89fbdd0 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/HomeScreenControllerImpl.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/HomeScreenControllerImpl.kt @@ -3,8 +3,11 @@ package com.github.nacabaro.vbhelper.screens.homeScreens import androidx.activity.ComponentActivity import androidx.lifecycle.lifecycleScope import com.github.nacabaro.vbhelper.di.VBHelper +import com.github.nacabaro.vbhelper.dtos.ItemDtos import kotlinx.coroutines.launch import java.time.Instant +import kotlin.math.roundToInt +import kotlin.random.Random class HomeScreenControllerImpl( private val componentActivity: ComponentActivity, @@ -26,4 +29,38 @@ class HomeScreenControllerImpl( onCompletion(finishedAdventureCharacters.isNotEmpty()) } } + + override fun clearSpecialMission(missionId: Long, onCleared: (ItemDtos.PurchasedItem) -> Unit) { + componentActivity.lifecycleScope.launch { + database + .specialMissionDao() + .clearSpecialMission(missionId) + + val randomItem = database + .itemDao() + .getAllItems() + .random() + + val randomItemAmount = (Random.nextFloat() * 5).roundToInt() + + database + .itemDao() + .purchaseItem( + itemId = randomItem.id, + itemAmount = randomItemAmount + ) + + val purchasedItem = ItemDtos.PurchasedItem( + itemId = randomItem.id, + itemName = randomItem.name, + itemDescription = randomItem.description, + itemIcon = randomItem.itemIcon, + itemLength = randomItem.itemLength, + itemAmount = randomItemAmount, + itemType = randomItem.itemType + ) + + onCleared(purchasedItem) + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/BEBEmHomeScreen.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/screens/BEBEmHomeScreen.kt similarity index 99% rename from app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/BEBEmHomeScreen.kt rename to app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/screens/BEBEmHomeScreen.kt index 344ecc5..a35d577 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/BEBEmHomeScreen.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/screens/BEBEmHomeScreen.kt @@ -1,4 +1,4 @@ -package com.github.nacabaro.vbhelper.screens.homeScreens +package com.github.nacabaro.vbhelper.screens.homeScreens.screens import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/BEDiMHomeScreen.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/screens/BEDiMHomeScreen.kt similarity index 99% rename from app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/BEDiMHomeScreen.kt rename to app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/screens/BEDiMHomeScreen.kt index 05b250b..065b6c6 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/BEDiMHomeScreen.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/screens/BEDiMHomeScreen.kt @@ -1,4 +1,4 @@ -package com.github.nacabaro.vbhelper.screens.homeScreens +package com.github.nacabaro.vbhelper.screens.homeScreens.screens import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/VBDiMHomeScreen.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/screens/VBDiMHomeScreen.kt similarity index 81% rename from app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/VBDiMHomeScreen.kt rename to app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/screens/VBDiMHomeScreen.kt index a91bd7a..22e67b8 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/VBDiMHomeScreen.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/screens/VBDiMHomeScreen.kt @@ -1,4 +1,4 @@ -package com.github.nacabaro.vbhelper.screens.homeScreens +package com.github.nacabaro.vbhelper.screens.homeScreens.screens import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues @@ -8,15 +8,21 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll +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.unit.sp import com.github.nacabaro.vbhelper.R import com.github.nacabaro.vbhelper.components.CharacterEntry import com.github.nacabaro.vbhelper.components.ItemDisplay +import com.github.nacabaro.vbhelper.components.SpecialMissionsEntry import com.github.nacabaro.vbhelper.components.TransformationHistoryCard +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 com.github.nacabaro.vbhelper.screens.homeScreens.HomeScreenControllerImpl import com.github.nacabaro.vbhelper.utils.BitmapData import java.util.Locale @@ -24,8 +30,11 @@ import java.util.Locale fun VBDiMHomeScreen( activeMon: CharacterDtos.CharacterWithSprites, vbData: VBCharacterData, + specialMissions: List, + homeScreenController: HomeScreenControllerImpl, transformationHistory: List, - contentPadding: PaddingValues + contentPadding: PaddingValues, + onClickCollect: (ItemDtos.PurchasedItem) -> Unit ) { Column( modifier = Modifier @@ -151,5 +160,30 @@ fun VBDiMHomeScreen( .padding(8.dp) ) } + Row ( + modifier = Modifier + .padding(16.dp) + ) { + Text( + text = "Special missions", + fontSize = 24.sp + ) + } + for (mission in specialMissions) { + Row( + modifier = Modifier + .fillMaxWidth() + ) { + SpecialMissionsEntry( + specialMission = mission, + modifier = Modifier + .weight(1f) + .padding(8.dp), + ) { + homeScreenController + .clearSpecialMission(mission.id, onClickCollect) + } + } + } } } \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/itemsScreen/ChooseCharacterScreen.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/itemsScreen/ChooseCharacterScreen.kt index fe52df1..0d09f13 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/itemsScreen/ChooseCharacterScreen.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/itemsScreen/ChooseCharacterScreen.kt @@ -19,7 +19,9 @@ 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.domain.items.ItemType 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.launch @@ -39,10 +41,25 @@ fun ChooseCharacterScreen( } var selectedCharacter by remember { mutableStateOf(null) } + var selectedItem by remember { mutableStateOf(null) } LaunchedEffect(storageRepository) { coroutineScope.launch { - characterList.value = storageRepository.getAllCharacters() + selectedItem = storageRepository.getItem(itemId) + when (selectedItem?.itemType) { + ItemType.BEITEM -> { + characterList.value = storageRepository.getBEBEmCharacters() + } + ItemType.VBITEM -> { + characterList.value = storageRepository.getVBCharacters() + } + ItemType.SPECIALMISSION-> { + characterList.value = storageRepository.getVBCharacters() + } + else -> { + characterList.value = storageRepository.getAllCharacters() + } + } } } diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/itemsScreen/ItemsScreenControllerImpl.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/itemsScreen/ItemsScreenControllerImpl.kt index f1a6716..db9194d 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/itemsScreen/ItemsScreenControllerImpl.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/itemsScreen/ItemsScreenControllerImpl.kt @@ -2,9 +2,12 @@ package com.github.nacabaro.vbhelper.screens.itemsScreen import androidx.activity.ComponentActivity import androidx.lifecycle.lifecycleScope +import com.github.cfogrady.vbnfc.vb.SpecialMission +import com.github.nacabaro.vbhelper.domain.device_data.SpecialMissions import com.github.nacabaro.vbhelper.database.AppDatabase import com.github.nacabaro.vbhelper.di.VBHelper import com.github.nacabaro.vbhelper.domain.device_data.BECharacterData +import com.github.nacabaro.vbhelper.domain.device_data.VBCharacterData import com.github.nacabaro.vbhelper.dtos.ItemDtos import com.github.nacabaro.vbhelper.utils.DeviceType import kotlinx.coroutines.Dispatchers @@ -24,7 +27,15 @@ class ItemsScreenControllerImpl ( AllTraining(5), EvoTimer(6), LimitTimer(7), - Vitals(8) + Vitals(8), + Step8k(9), + Step4k(10), + Vitals1000(11), + Vitals250(12), + Battle20(13), + Battle5(14), + Win10(15), + Win4(16) } init { @@ -37,17 +48,20 @@ class ItemsScreenControllerImpl ( withContext(Dispatchers.IO) { val item = getItem(itemId) val characterData = database.userCharacterDao().getCharacter(characterId) - val beCharacterData: BECharacterData - //var vbCharacterData: VBCharacterData + var beCharacterData: BECharacterData? = null + var vbCharacterData: VBCharacterData? = null if (characterData.characterType == DeviceType.BEDevice) { beCharacterData = database.userCharacterDao().getBeData(characterId) - } else { - TODO("Not implemented") - //vbCharacterData = database.userCharacterDao().getVbData(characterId) + } else if (characterData.characterType == DeviceType.VBDevice) { + vbCharacterData = database.userCharacterDao().getVbData(characterId) } - if (item.itemIcon in 1 .. 5 && characterData.characterType == DeviceType.BEDevice) { + if ( + item.itemIcon in 1 .. 5 && + characterData.characterType == DeviceType.BEDevice && + beCharacterData != null + ) { beCharacterData.itemType = item.itemIcon beCharacterData.itemMultiplier = 3 beCharacterData.itemRemainingTime = item.itemLength @@ -72,7 +86,11 @@ class ItemsScreenControllerImpl ( .userCharacterDao() .updateCharacter(characterData) - } else if (item.itemIcon == ItemTypes.LimitTimer.id) { + } else if ( + item.itemIcon == ItemTypes.LimitTimer.id && + characterData.characterType == DeviceType.BEDevice && + beCharacterData != null + ) { beCharacterData.remainingTrainingTimeInMinutes += item.itemLength if (beCharacterData.remainingTrainingTimeInMinutes > 6000) { beCharacterData.remainingTrainingTimeInMinutes = 6000 @@ -93,6 +111,12 @@ class ItemsScreenControllerImpl ( database .userCharacterDao() .updateCharacter(characterData) + + } else if (item.itemIcon in ItemTypes.Step8k.id .. ItemTypes.Win4.id && + characterData.characterType == DeviceType.VBDevice && + vbCharacterData != null + ) { + applySpecialMission(item.itemIcon, item.itemLength, characterId) } consumeItem(item.id) @@ -104,13 +128,72 @@ class ItemsScreenControllerImpl ( } } - private fun getItem(itemId: Long): ItemDtos.ItemsWithQuantities { + private suspend fun applySpecialMission(itemIcon: Int, itemLength: Int, characterId: Long) { + // Hello, it's me, naca! No! I don't like this, I'll see how I can improve it later on... + val specialMissionType = when (itemIcon) { + ItemTypes.Step8k.id -> SpecialMission.Type.STEPS + ItemTypes.Step4k.id -> SpecialMission.Type.STEPS + ItemTypes.Vitals1000.id -> SpecialMission.Type.VITALS + ItemTypes.Vitals250.id -> SpecialMission.Type.VITALS + ItemTypes.Battle20.id -> SpecialMission.Type.BATTLES + ItemTypes.Battle5.id -> SpecialMission.Type.BATTLES + ItemTypes.Win10.id -> SpecialMission.Type.WINS + ItemTypes.Win4.id -> SpecialMission.Type.WINS + else -> SpecialMission.Type.NONE + } + + val specialMissionGoal = when (itemIcon) { + ItemTypes.Step8k.id -> 8000 + ItemTypes.Step4k.id -> 4000 + ItemTypes.Vitals1000.id -> 1000 + ItemTypes.Vitals250.id -> 250 + ItemTypes.Battle20.id -> 20 + ItemTypes.Battle5.id -> 5 + ItemTypes.Win10.id -> 10 + ItemTypes.Win4.id -> 4 + else -> 0 + } + + val availableSpecialMissions = database + .userCharacterDao() + .getSpecialMissions(characterId) + + var firstUnavailableMissionSlot: Long = 0 + var watchId = 0 + + for ((index, mission) in availableSpecialMissions.withIndex()) { + if ( + mission.status == SpecialMission.Status.UNAVAILABLE + ) { + firstUnavailableMissionSlot = mission.id + watchId = index + 1 + } + } + + val newSpecialMission = SpecialMissions( + id = firstUnavailableMissionSlot, + characterId = characterId, + missionType = specialMissionType, + goal = specialMissionGoal, + timeLimitInMinutes = itemLength, + watchId = watchId, + status = SpecialMission.Status.AVAILABLE, + progress = 0, + timeElapsedInMinutes = 0 + ) + + database + .userCharacterDao() + .insertSpecialMissions(newSpecialMission) + } + + private suspend fun getItem(itemId: Long): ItemDtos.ItemsWithQuantities { return database .itemDao() .getItem(itemId) } - private fun consumeItem(itemId: Long) { + private suspend fun consumeItem(itemId: Long) { database .itemDao() .useItem(itemId) diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/source/StorageRepository.kt b/app/src/main/java/com/github/nacabaro/vbhelper/source/StorageRepository.kt index 65bdeb0..a222a21 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/source/StorageRepository.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/source/StorageRepository.kt @@ -2,8 +2,10 @@ package com.github.nacabaro.vbhelper.source import com.github.nacabaro.vbhelper.database.AppDatabase 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.VBCharacterData import com.github.nacabaro.vbhelper.dtos.CharacterDtos +import com.github.nacabaro.vbhelper.dtos.ItemDtos class StorageRepository ( private val db: AppDatabase @@ -28,6 +30,14 @@ class StorageRepository ( return db.userCharacterDao().getVbData(id) } + suspend fun getSpecialMissions(id: Long): List { + return db.userCharacterDao().getSpecialMissions(id) + } + + suspend fun getItem(id: Long): ItemDtos.ItemsWithQuantities { + return db.itemDao().getItem(id) + } + suspend fun getActiveCharacter(): CharacterDtos.CharacterWithSprites? { return db.userCharacterDao().getActiveCharacter() } @@ -39,4 +49,12 @@ class StorageRepository ( suspend fun getAdventureCharacters(): List { return db.adventureDao().getAdventureCharacters() } + + suspend fun getBEBEmCharacters(): List { + return db.userCharacterDao().getBEBemCharacters() + } + + suspend fun getVBCharacters(): List { + return db.userCharacterDao().getVBDimCharacters() + } } \ No newline at end of file From f96f00e05abe3e76559dded40f84d0a397af2eba Mon Sep 17 00:00:00 2001 From: Nacho Date: Wed, 6 Aug 2025 01:22:43 +0200 Subject: [PATCH 04/10] Version up And change the original pop up to mention this works with VB, my bad --- app/build.gradle.kts | 2 +- .../github/nacabaro/vbhelper/screens/homeScreens/BetaWarning.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 5f7830d..809098c 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -15,7 +15,7 @@ android { minSdk = 28 targetSdk = 35 versionCode = 1 - versionName = "Alpha 0.3" + versionName = "Alpha 0.4" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/BetaWarning.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/BetaWarning.kt index ff62f8e..fb9a567 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/BetaWarning.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/BetaWarning.kt @@ -28,7 +28,7 @@ fun BetaWarning( ) Spacer(modifier = Modifier.padding(8.dp)) Text( - text = "Also, this application does not work yet with the original VB." + text = "Application should work now with the original VB and the VH." ) Spacer(modifier = Modifier.padding(8.dp)) Text( From dd1a62d18468d2bd1988a16d65b631595de7258e Mon Sep 17 00:00:00 2001 From: Nacho Date: Wed, 6 Aug 2025 01:31:18 +0200 Subject: [PATCH 05/10] Made a mistake Accidentally locked BE watch items to only BEm characters. I have modified it to also include DiM characters in the BE. --- .../com/github/nacabaro/vbhelper/daos/UserCharacterDao.kt | 4 ++-- .../vbhelper/screens/itemsScreen/ChooseCharacterScreen.kt | 2 +- .../com/github/nacabaro/vbhelper/source/StorageRepository.kt | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/daos/UserCharacterDao.kt b/app/src/main/java/com/github/nacabaro/vbhelper/daos/UserCharacterDao.kt index 352f180..3b08448 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/daos/UserCharacterDao.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/daos/UserCharacterDao.kt @@ -193,10 +193,10 @@ interface UserCharacterDao { JOIN Card d ON d.id = c.dimId JOIN Sprite s ON s.id = c.spriteId LEFT JOIN Adventure a ON a.characterId = uc.id - WHERE d.isBEm = 1 + WHERE uc.characterType = "BEDevice" """ ) - suspend fun getBEBemCharacters(): List + suspend fun getBECharacters(): List @Query( """ diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/itemsScreen/ChooseCharacterScreen.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/itemsScreen/ChooseCharacterScreen.kt index 0d09f13..26be571 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/itemsScreen/ChooseCharacterScreen.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/itemsScreen/ChooseCharacterScreen.kt @@ -48,7 +48,7 @@ fun ChooseCharacterScreen( selectedItem = storageRepository.getItem(itemId) when (selectedItem?.itemType) { ItemType.BEITEM -> { - characterList.value = storageRepository.getBEBEmCharacters() + characterList.value = storageRepository.getBECharacters() } ItemType.VBITEM -> { characterList.value = storageRepository.getVBCharacters() diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/source/StorageRepository.kt b/app/src/main/java/com/github/nacabaro/vbhelper/source/StorageRepository.kt index a222a21..61aff4a 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/source/StorageRepository.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/source/StorageRepository.kt @@ -50,8 +50,8 @@ class StorageRepository ( return db.adventureDao().getAdventureCharacters() } - suspend fun getBEBEmCharacters(): List { - return db.userCharacterDao().getBEBemCharacters() + suspend fun getBECharacters(): List { + return db.userCharacterDao().getBECharacters() } suspend fun getVBCharacters(): List { From fb09004d13d6dee48b3059a9c9da5f74d4affcb6 Mon Sep 17 00:00:00 2001 From: Nacho Date: Wed, 6 Aug 2025 01:32:10 +0200 Subject: [PATCH 06/10] Version up, again --- app/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 809098c..e459160 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -15,7 +15,7 @@ android { minSdk = 28 targetSdk = 35 versionCode = 1 - versionName = "Alpha 0.4" + versionName = "Alpha 0.4.1" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } From 4474e6689d96977ecfef521ff2595f1d433b5580 Mon Sep 17 00:00:00 2001 From: Nacho Date: Wed, 6 Aug 2025 20:10:21 +0200 Subject: [PATCH 07/10] Few things here and there - Card management, you can now modify the name of the cards and remove cards too. - Also, support for multiple cards with the same ID works too. When scanning for the first time, if a repeat card exists, the user will be asked to choose which card the character comes from. Future scans will not ask since it is stored in the watch app reserved area. --- .../github/nacabaro/vbhelper/MainActivity.kt | 13 +- .../nacabaro/vbhelper/components/TopBanner.kt | 32 ++-- .../github/nacabaro/vbhelper/daos/CardDao.kt | 22 ++- .../vbhelper/navigation/AppNavigation.kt | 15 +- .../nacabaro/vbhelper/screens/DexScreen.kt | 83 ---------- .../vbhelper/screens/cardScreen/CardEntry.kt | 109 +++++++++++++ .../cardScreen/CardScreenController.kt | 6 + .../cardScreen/CardScreenControllerImpl.kt | 34 ++++ .../CardViewScreen.kt} | 5 +- .../screens/cardScreen/CardsScreen.kt | 145 ++++++++++++++++++ .../cardScreen/dialogs/CardDeleteDialog.kt | 51 ++++++ .../cardScreen/dialogs/CardRenameDialog.kt | 52 +++++++ .../vbhelper/screens/scanScreen/ScanScreen.kt | 50 ++++-- .../scanScreen/ScanScreenController.kt | 10 +- .../scanScreen/ScanScreenControllerImpl.kt | 31 +++- .../scanScreen/cardSelect/ChooseCard.kt | 48 ++++++ .../scanScreen/cardSelect/ScanCardEntry.kt} | 13 +- .../scanScreen/converters/FromNfcConverter.kt | 67 +++++++- .../scanScreen/converters/ToNfcConverter.kt | 19 ++- .../SettingsScreenControllerImpl.kt | 2 +- .../main/res/drawable/baseline_edit_24.xml | 9 ++ 21 files changed, 673 insertions(+), 143 deletions(-) delete mode 100644 app/src/main/java/com/github/nacabaro/vbhelper/screens/DexScreen.kt create mode 100644 app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardEntry.kt create mode 100644 app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardScreenController.kt create mode 100644 app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardScreenControllerImpl.kt rename app/src/main/java/com/github/nacabaro/vbhelper/screens/{DimScreen.kt => cardScreen/CardViewScreen.kt} (94%) create mode 100644 app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardsScreen.kt create mode 100644 app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/dialogs/CardDeleteDialog.kt create mode 100644 app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/dialogs/CardRenameDialog.kt create mode 100644 app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/cardSelect/ChooseCard.kt rename app/src/main/java/com/github/nacabaro/vbhelper/{components/DexDimEntry.kt => screens/scanScreen/cardSelect/ScanCardEntry.kt} (82%) create mode 100644 app/src/main/res/drawable/baseline_edit_24.xml diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/MainActivity.kt b/app/src/main/java/com/github/nacabaro/vbhelper/MainActivity.kt index cc8eff4..c8f358b 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/MainActivity.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/MainActivity.kt @@ -14,6 +14,7 @@ import com.github.nacabaro.vbhelper.screens.itemsScreen.ItemsScreenControllerImp import com.github.nacabaro.vbhelper.screens.scanScreen.ScanScreenControllerImpl import com.github.nacabaro.vbhelper.screens.settingsScreen.SettingsScreenControllerImpl import com.github.nacabaro.vbhelper.screens.adventureScreen.AdventureScreenControllerImpl +import com.github.nacabaro.vbhelper.screens.cardScreen.CardScreenControllerImpl import com.github.nacabaro.vbhelper.screens.spriteViewer.SpriteViewerControllerImpl import com.github.nacabaro.vbhelper.screens.storageScreen.StorageScreenControllerImpl import com.github.nacabaro.vbhelper.ui.theme.VBHelperTheme @@ -47,6 +48,7 @@ class MainActivity : ComponentActivity() { val storageScreenController = StorageScreenControllerImpl(this) val homeScreenController = HomeScreenControllerImpl(this) val spriteViewerController = SpriteViewerControllerImpl(this) + val cardScreenController = CardScreenControllerImpl(this) super.onCreate(savedInstanceState) @@ -61,7 +63,8 @@ class MainActivity : ComponentActivity() { adventureScreenController = adventureScreenController, homeScreenController = homeScreenController, storageScreenController = storageScreenController, - spriteViewerController = spriteViewerController + spriteViewerController = spriteViewerController, + cardScreenController = cardScreenController ) } } @@ -93,8 +96,9 @@ class MainActivity : ComponentActivity() { adventureScreenController: AdventureScreenControllerImpl, storageScreenController: StorageScreenControllerImpl, homeScreenController: HomeScreenControllerImpl, - spriteViewerController: SpriteViewerControllerImpl - ) { + spriteViewerController: SpriteViewerControllerImpl, + cardScreenController: CardScreenControllerImpl + ) { AppNavigation( applicationNavigationHandlers = AppNavigationHandlers( settingsScreenController, @@ -103,7 +107,8 @@ class MainActivity : ComponentActivity() { adventureScreenController, storageScreenController, homeScreenController, - spriteViewerController + spriteViewerController, + cardScreenController ) ) } diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/components/TopBanner.kt b/app/src/main/java/com/github/nacabaro/vbhelper/components/TopBanner.kt index b184cee..79c1103 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/components/TopBanner.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/components/TopBanner.kt @@ -24,7 +24,8 @@ fun TopBanner( onGearClick: (() -> Unit)? = null, onBackClick: (() -> Unit)? = null, onScanClick: (() -> Unit)? = null, - onAdventureClick: (() -> Unit)? = null + onAdventureClick: (() -> Unit)? = null, + onModifyClick: (() -> Unit)? = null ) { Box( // Use Box to overlay elements modifier = modifier @@ -37,16 +38,16 @@ fun TopBanner( textAlign = TextAlign.Center, fontSize = 24.sp, modifier = Modifier - .align(Alignment.Center) // Center the text + .align(Alignment.Center) ) if (onGearClick != null) { IconButton( onClick = onGearClick, modifier = Modifier - .align(Alignment.CenterEnd) // Place gear icon at the end + .align(Alignment.CenterEnd) ) { Icon( - painter = painterResource(R.drawable.baseline_settings_24), // Use a gear icon + painter = painterResource(R.drawable.baseline_settings_24), contentDescription = "Settings" ) } @@ -54,23 +55,34 @@ fun TopBanner( IconButton( onClick = onAdventureClick, modifier = Modifier - .align(Alignment.CenterEnd) // Place gear icon at the end + .align(Alignment.CenterEnd) ) { Icon( - painter = painterResource(R.drawable.baseline_fort_24), // Use a gear icon + painter = painterResource(R.drawable.baseline_fort_24), contentDescription = "Adventure" ) } + } else if (onModifyClick != null) { + IconButton( + onClick = onModifyClick, + modifier = Modifier + .align(Alignment.CenterEnd) + ) { + Icon( + painter = painterResource(R.drawable.baseline_edit_24), + contentDescription = "Adventure" + ) + } } if (onScanClick != null) { IconButton( onClick = onScanClick, modifier = Modifier - .align(Alignment.CenterStart) // Place gear icon at the end + .align(Alignment.CenterStart) ) { Icon( - painter = painterResource(R.drawable.baseline_nfc_24), // Use a gear icon + painter = painterResource(R.drawable.baseline_nfc_24), contentDescription = "Scan" ) } @@ -78,10 +90,10 @@ fun TopBanner( IconButton( onClick = onBackClick, modifier = Modifier - .align(Alignment.CenterStart) // Place gear icon at the end + .align(Alignment.CenterStart) ) { Icon( - painter = painterResource(R.drawable.baseline_arrow_back_24), // Use a gear icon + painter = painterResource(R.drawable.baseline_arrow_back_24), contentDescription = "Settings" ) } diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/daos/CardDao.kt b/app/src/main/java/com/github/nacabaro/vbhelper/daos/CardDao.kt index d9c1d3c..6fa7aec 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/daos/CardDao.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/daos/CardDao.kt @@ -9,8 +9,26 @@ import com.github.nacabaro.vbhelper.domain.card.Card @Dao interface CardDao { @Insert(onConflict = OnConflictStrategy.IGNORE) - suspend fun insertNewDim(card: Card): Long + suspend fun insertNewCard(card: Card): Long @Query("SELECT * FROM Card WHERE cardId = :id") - fun getDimById(id: Int): Card? + fun getCardByCardId(id: Int): List + + @Query("SELECT * FROM Card WHERE id = :id") + fun getCardById(id: Long): Card? + + @Query(""" + SELECT ca.* + FROM Card ca + JOIN Character ch ON ca.id = ch.dimId + JOIN UserCharacter uc ON ch.id = uc.charId + WHERE uc.id = :id + """) + suspend fun getCardByCharacterId(id: Long): Card + + @Query("UPDATE Card SET name = :newName WHERE id = :id") + suspend fun renameCard(id: Int, newName: String) + + @Query("DELETE FROM Card WHERE id = :id") + suspend fun deleteCard(id: Long) } \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/navigation/AppNavigation.kt b/app/src/main/java/com/github/nacabaro/vbhelper/navigation/AppNavigation.kt index ea20f0e..76d9009 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/navigation/AppNavigation.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/navigation/AppNavigation.kt @@ -19,8 +19,8 @@ import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController import com.github.nacabaro.vbhelper.di.VBHelper import com.github.nacabaro.vbhelper.screens.BattlesScreen -import com.github.nacabaro.vbhelper.screens.DexScreen -import com.github.nacabaro.vbhelper.screens.DiMScreen +import com.github.nacabaro.vbhelper.screens.cardScreen.CardsScreen +import com.github.nacabaro.vbhelper.screens.cardScreen.CardViewScreen import com.github.nacabaro.vbhelper.screens.homeScreens.HomeScreen import com.github.nacabaro.vbhelper.screens.itemsScreen.ItemsScreen import com.github.nacabaro.vbhelper.screens.scanScreen.ScanScreen @@ -34,6 +34,7 @@ import com.github.nacabaro.vbhelper.screens.itemsScreen.ItemsScreenControllerImp import com.github.nacabaro.vbhelper.screens.settingsScreen.SettingsScreenControllerImpl import com.github.nacabaro.vbhelper.screens.adventureScreen.AdventureScreen import com.github.nacabaro.vbhelper.screens.adventureScreen.AdventureScreenControllerImpl +import com.github.nacabaro.vbhelper.screens.cardScreen.CardScreenControllerImpl import com.github.nacabaro.vbhelper.screens.settingsScreen.CreditsScreen import com.github.nacabaro.vbhelper.screens.spriteViewer.SpriteViewerControllerImpl import com.github.nacabaro.vbhelper.screens.storageScreen.StorageScreenControllerImpl @@ -46,7 +47,8 @@ data class AppNavigationHandlers( val adventureScreenController: AdventureScreenControllerImpl, val storageScreenController: StorageScreenControllerImpl, val homeScreenController: HomeScreenControllerImpl, - val spriteViewerController: SpriteViewerControllerImpl + val spriteViewerController: SpriteViewerControllerImpl, + val cardScreenController: CardScreenControllerImpl ) @Composable @@ -121,8 +123,9 @@ fun AppNavigation( ) } composable(NavigationItems.Dex.route) { - DexScreen( - navController = navController + CardsScreen( + navController = navController, + cardScreenController = applicationNavigationHandlers.cardScreenController ) } composable(NavigationItems.Settings.route) { @@ -140,7 +143,7 @@ fun AppNavigation( composable(NavigationItems.CardView.route) { val cardId = it.arguments?.getString("cardId") if (cardId != null) { - DiMScreen( + CardViewScreen( navController = navController, dimId = cardId.toLong() ) diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/DexScreen.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/DexScreen.kt deleted file mode 100644 index 6a99b4f..0000000 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/DexScreen.kt +++ /dev/null @@ -1,83 +0,0 @@ -package com.github.nacabaro.vbhelper.screens - -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.items -import androidx.compose.material3.Scaffold -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.unit.dp -import androidx.navigation.NavController -import com.github.nacabaro.vbhelper.utils.BitmapData -import com.github.nacabaro.vbhelper.components.DexDiMEntry -import com.github.nacabaro.vbhelper.components.TopBanner -import com.github.nacabaro.vbhelper.di.VBHelper -import com.github.nacabaro.vbhelper.dtos.CardDtos -import com.github.nacabaro.vbhelper.navigation.NavigationItems -import com.github.nacabaro.vbhelper.source.DexRepository -import kotlinx.coroutines.launch - -@Composable -fun DexScreen( - navController: NavController -) { - val coroutineScope = rememberCoroutineScope() - val application = LocalContext.current.applicationContext as VBHelper - val dexRepository = DexRepository(application.container.db) - - val cardList = remember { mutableStateOf>(emptyList()) } - - LaunchedEffect(dexRepository) { - coroutineScope.launch { - val newDimList = dexRepository.getAllDims() - cardList.value = newDimList // Replace the entire list atomically - } - } - - Scaffold ( - topBar = { - TopBanner( - text = "Discovered characters", - onGearClick = { - navController.navigate(NavigationItems.Viewer.route) - } - ) - } - ) { contentPadding -> - LazyColumn ( - modifier = Modifier - .padding(top = contentPadding.calculateTopPadding()) - ) { - items(cardList.value) { - DexDiMEntry( - name = it.cardName, - logo = BitmapData( - bitmap = it.cardLogo, - width = it.logoWidth, - height = it.logoHeight - ), - onClick = { - navController - .navigate( - NavigationItems - .CardView.route - .replace("{cardId}", "${it.cardId}") - ) - }, - obtainedCharacters = it.obtainedCharacters, - totalCharacters = it.totalCharacters, - modifier = Modifier - .fillMaxWidth() - .padding(8.dp) - ) - } - } - } -} - diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardEntry.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardEntry.kt new file mode 100644 index 0000000..9d48d5a --- /dev/null +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardEntry.kt @@ -0,0 +1,109 @@ +package com.github.nacabaro.vbhelper.screens.cardScreen + +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Delete +import androidx.compose.material.icons.filled.Edit +import androidx.compose.material3.Card +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.FilterQuality +import androidx.compose.ui.graphics.asImageBitmap +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.unit.dp +import com.github.nacabaro.vbhelper.utils.BitmapData +import com.github.nacabaro.vbhelper.utils.getBitmap + +@Composable +fun CardEntry( + name: String, + logo: BitmapData, + obtainedCharacters: Int, + totalCharacters: Int, + onClick: () -> Unit, + displayModify: Boolean, + onClickModify: () -> Unit, + onClickDelete: () -> Unit, + modifier: Modifier = Modifier +) { + val bitmap = remember (logo.bitmap) { logo.getBitmap() } + val imageBitmap = remember(bitmap) { bitmap.asImageBitmap() } + val density: Float = LocalContext.current.resources.displayMetrics.density + val dpSize = (logo.width * 4 / density).dp + + Card ( + shape = MaterialTheme.shapes.medium, + modifier = modifier, + onClick = if (!displayModify) { + onClick + } else { + { } + } + ) { + Row ( + horizontalArrangement = Arrangement.Start, + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .padding(8.dp) + ) { + Image ( + bitmap = imageBitmap, + contentDescription = name, + filterQuality = FilterQuality.None, + modifier = Modifier + .padding(8.dp) + .size(dpSize) + ) + Column( + modifier = Modifier + .padding(8.dp) + .weight(1f) + ) { + Text( + text = name, + modifier = Modifier + ) + Text( + text = "$obtainedCharacters of $totalCharacters characters obtained", + fontFamily = MaterialTheme.typography.labelSmall.fontFamily, + fontSize = MaterialTheme.typography.labelSmall.fontSize, + modifier = Modifier + ) + } + if (displayModify) { + Row ( + modifier = Modifier, + horizontalArrangement = Arrangement.End, + ) { + IconButton( + onClick = onClickModify + ) { + Icon( + imageVector = Icons.Default.Edit, + contentDescription = "Edit" + ) + } + IconButton( + onClick = onClickDelete + ) { + Icon( + imageVector = Icons.Default.Delete, + contentDescription = "Delete" + ) + } + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardScreenController.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardScreenController.kt new file mode 100644 index 0000000..d47d888 --- /dev/null +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardScreenController.kt @@ -0,0 +1,6 @@ +package com.github.nacabaro.vbhelper.screens.cardScreen + +interface CardScreenController { + fun renameCard(cardId: Long, newName: String, onRenamed: (String) -> Unit) + fun deleteCard(cardId: Long, onDeleted: () -> Unit) +} \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardScreenControllerImpl.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardScreenControllerImpl.kt new file mode 100644 index 0000000..0e54d15 --- /dev/null +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardScreenControllerImpl.kt @@ -0,0 +1,34 @@ +package com.github.nacabaro.vbhelper.screens.cardScreen + +import androidx.activity.ComponentActivity +import androidx.lifecycle.lifecycleScope +import com.github.nacabaro.vbhelper.di.VBHelper +import kotlinx.coroutines.launch + +class CardScreenControllerImpl( + private val componentActivity: ComponentActivity, +) : CardScreenController { + private val application = componentActivity.applicationContext as VBHelper + private val database = application.container.db + + + override fun renameCard(cardId: Long, newName: String, onRenamed: (String) -> Unit) { + componentActivity.lifecycleScope.launch { + database + .cardDao() + .renameCard(cardId.toInt(), newName) + + onRenamed(newName) + } + } + + override fun deleteCard(cardId: Long, onDeleted: () -> Unit) { + componentActivity.lifecycleScope.launch { + database + .cardDao() + .deleteCard(cardId) + + onDeleted() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/DimScreen.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardViewScreen.kt similarity index 94% rename from app/src/main/java/com/github/nacabaro/vbhelper/screens/DimScreen.kt rename to app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardViewScreen.kt index 609be95..d042eae 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/DimScreen.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardViewScreen.kt @@ -1,4 +1,4 @@ -package com.github.nacabaro.vbhelper.screens +package com.github.nacabaro.vbhelper.screens.cardScreen import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.LazyVerticalGrid @@ -14,14 +14,13 @@ import androidx.navigation.NavController import com.github.nacabaro.vbhelper.utils.BitmapData import com.github.nacabaro.vbhelper.components.CharacterEntry import com.github.nacabaro.vbhelper.components.TopBanner -import com.github.nacabaro.vbhelper.domain.characters.Character import com.github.nacabaro.vbhelper.di.VBHelper import com.github.nacabaro.vbhelper.dtos.CharacterDtos import com.github.nacabaro.vbhelper.source.DexRepository import kotlinx.coroutines.launch @Composable -fun DiMScreen( +fun CardViewScreen( navController: NavController, dimId: Long ) { diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardsScreen.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardsScreen.kt new file mode 100644 index 0000000..e639444 --- /dev/null +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/CardsScreen.kt @@ -0,0 +1,145 @@ +package com.github.nacabaro.vbhelper.screens.cardScreen + +import android.util.Log +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.material3.Scaffold +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.unit.dp +import androidx.navigation.NavController +import com.github.nacabaro.vbhelper.utils.BitmapData +import com.github.nacabaro.vbhelper.components.TopBanner +import com.github.nacabaro.vbhelper.di.VBHelper +import com.github.nacabaro.vbhelper.dtos.CardDtos +import com.github.nacabaro.vbhelper.navigation.NavigationItems +import com.github.nacabaro.vbhelper.screens.cardScreen.dialogs.CardDeleteDialog +import com.github.nacabaro.vbhelper.screens.cardScreen.dialogs.CardRenameDialog +import com.github.nacabaro.vbhelper.source.DexRepository +import kotlinx.coroutines.launch + +@Composable +fun CardsScreen( + navController: NavController, + cardScreenController: CardScreenControllerImpl +) { + val coroutineScope = rememberCoroutineScope() + val application = LocalContext.current.applicationContext as VBHelper + val dexRepository = DexRepository(application.container.db) + val cardList = remember { mutableStateOf>(emptyList()) } + + val selectedCard = remember { mutableStateOf(null) } + var clickedDelete by remember { mutableStateOf(false) } + var clickedRename by remember { mutableStateOf(false) } + + var modifyCards by remember { mutableStateOf(false) } + + LaunchedEffect(dexRepository) { + coroutineScope.launch { + val newDimList = dexRepository.getAllDims() + cardList.value = newDimList + } + } + + Scaffold ( + topBar = { + TopBanner( + text = "My cards", + onModifyClick = { + modifyCards = !modifyCards + } + ) + } + ) { contentPadding -> + LazyColumn ( + modifier = Modifier + .padding(top = contentPadding.calculateTopPadding()) + ) { + items(cardList.value) { + CardEntry( + name = it.cardName, + logo = BitmapData( + bitmap = it.cardLogo, + width = it.logoWidth, + height = it.logoHeight + ), + onClick = { + navController + .navigate( + NavigationItems + .CardView.route + .replace("{cardId}", "${it.cardId}") + ) + }, + obtainedCharacters = it.obtainedCharacters, + totalCharacters = it.totalCharacters, + modifier = Modifier + .fillMaxWidth() + .padding(8.dp), + displayModify = modifyCards, + onClickModify = { + selectedCard.value = it + clickedRename = true + }, + onClickDelete = { + selectedCard.value = it + clickedDelete = true + } + ) + } + } + } + + if (clickedRename) { + CardRenameDialog( + onDismiss = { + clickedRename = false + selectedCard.value = null + }, + onRename = { newName -> + Log.d("CardsScreen", "New name: $newName") + Log.d("CardsScreen", "Card: ${selectedCard.value.toString()}") + cardScreenController + .renameCard( + cardId = selectedCard.value!!.cardId, + newName = newName, + onRenamed = { + clickedRename = false + selectedCard.value = null + } + ) + }, + currentName = selectedCard.value!!.cardName + ) + } + + if (clickedDelete) { + CardDeleteDialog( + cardName = selectedCard.value!!.cardName, + onDismiss = { + clickedDelete = false + selectedCard.value = null + }, + onConfirm = { + cardScreenController + .deleteCard( + cardId = selectedCard.value!!.cardId, + onDeleted = { + clickedDelete = false + selectedCard.value = null + } + ) + } + ) + } +} + diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/dialogs/CardDeleteDialog.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/dialogs/CardDeleteDialog.kt new file mode 100644 index 0000000..42797da --- /dev/null +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/dialogs/CardDeleteDialog.kt @@ -0,0 +1,51 @@ +package com.github.nacabaro.vbhelper.screens.cardScreen.dialogs + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Button +import androidx.compose.material3.Card +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import androidx.compose.ui.window.Dialog + +@Composable +fun CardDeleteDialog( + cardName: String, + onDismiss: () -> Unit, + onConfirm: () -> Unit +) { + Dialog( + onDismissRequest = onDismiss + + ) { + Card ( ) { + Column ( + modifier = Modifier + .padding(16.dp) + ) { + Text(text = "Are you sure you want to delete $cardName. This action will also delete all the characters raised from this card.") + Spacer(modifier = Modifier.padding(8.dp)) + Row { + Button( + onClick = { + onDismiss() + } + ) { + Text(text = "Confirm") + } + Button( + onClick = { + onConfirm() + } + ) { + Text(text = "Delete") + } + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/dialogs/CardRenameDialog.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/dialogs/CardRenameDialog.kt new file mode 100644 index 0000000..e3f9fe2 --- /dev/null +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/cardScreen/dialogs/CardRenameDialog.kt @@ -0,0 +1,52 @@ +package com.github.nacabaro.vbhelper.screens.cardScreen.dialogs + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Button +import androidx.compose.material3.Card +import androidx.compose.material3.Text +import androidx.compose.material3.TextField +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import androidx.compose.ui.window.Dialog + +@Composable +fun CardRenameDialog( + onDismiss: () -> Unit, + onRename: (String) -> Unit, + currentName: String +) { + var cardName by remember { mutableStateOf(currentName) } + + Dialog( + onDismissRequest = onDismiss + + ) { + Card ( ) { + Column ( + modifier = Modifier + .padding(16.dp) + ) { + TextField( + value = cardName, + onValueChange = { cardName = it } + ) + Spacer(modifier = Modifier.padding(8.dp)) + Button( + onClick = { + onRename(cardName) + onDismiss() + } + ) { + Text(text = "Rename") + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/ScanScreen.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/ScanScreen.kt index 8709ae2..a58b2b2 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/ScanScreen.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/ScanScreen.kt @@ -30,7 +30,9 @@ import com.github.cfogrady.vbnfc.data.NfcCharacter import com.github.nacabaro.vbhelper.ActivityLifecycleListener import com.github.nacabaro.vbhelper.components.TopBanner import com.github.nacabaro.vbhelper.di.VBHelper +import com.github.nacabaro.vbhelper.domain.card.Card import com.github.nacabaro.vbhelper.navigation.NavigationItems +import com.github.nacabaro.vbhelper.screens.cardScreen.ChooseCard import com.github.nacabaro.vbhelper.source.StorageRepository import com.github.nacabaro.vbhelper.source.isMissingSecrets import com.github.nacabaro.vbhelper.source.proto.Secrets @@ -53,6 +55,8 @@ fun ScanScreen( val storageRepository = StorageRepository(application.container.db) var nfcCharacter by remember { mutableStateOf(null) } + var cardsRead by remember { mutableStateOf?>(null) } + val context = LocalContext.current LaunchedEffect(storageRepository) { @@ -71,6 +75,7 @@ fun ScanScreen( var readingScreen by remember { mutableStateOf(false) } var writingScreen by remember { mutableStateOf(false) } + var cardSelectScreen by remember { mutableStateOf(false) } var isDoneReadingCharacter by remember { mutableStateOf(false) } var isDoneSendingCard by remember { mutableStateOf(false) } var isDoneWritingCharacter by remember { mutableStateOf(false) } @@ -85,15 +90,33 @@ fun ScanScreen( } override fun onResume() { - scanScreenController.onClickRead(secrets!!) { - isDoneReadingCharacter = true - } + scanScreenController.onClickRead( + secrets = secrets!!, + onComplete = { + isDoneReadingCharacter = true + }, + onMultipleCards = { cards -> + cardsRead = cards + readingScreen = false + cardSelectScreen = true + isDoneReadingCharacter = true + } + ) } } ) - scanScreenController.onClickRead(secrets!!) { - isDoneReadingCharacter = true - } + scanScreenController.onClickRead( + secrets = secrets!!, + onComplete = { + isDoneReadingCharacter = true + }, + onMultipleCards = { cards -> + cardsRead = cards + readingScreen = false + cardSelectScreen = true + isDoneReadingCharacter = true + } + ) } onDispose { if(readingScreen) { @@ -149,7 +172,7 @@ fun ScanScreen( } } - if (isDoneReadingCharacter) { + if (isDoneReadingCharacter && !cardSelectScreen) { readingScreen = false navController.navigate(NavigationItems.Home.route) } else if (isDoneSendingCard && isDoneWritingCharacter) { @@ -181,6 +204,14 @@ fun ScanScreen( scanScreenController.cancelRead() } } + } else if (cardSelectScreen) { + ChooseCard( + cards = cardsRead!!, + onCardSelected = { card -> + cardSelectScreen = false + scanScreenController.flushCharacter(card.id) + } + ) } else { ChooseConnectOption( onClickRead = when { @@ -290,11 +321,12 @@ fun ScanScreenPreview() { ) { } - override fun onClickRead(secrets: Secrets, onComplete: ()->Unit) {} + override fun flushCharacter(cardId: Long) {} + override fun onClickRead(secrets: Secrets, onComplete: ()->Unit, onMultipleCards: (List) -> Unit) {} override fun onClickCheckCard(secrets: Secrets, nfcCharacter: NfcCharacter, onComplete: () -> Unit) {} override fun onClickWrite(secrets: Secrets, nfcCharacter: NfcCharacter, onComplete: () -> Unit) {} override fun cancelRead() {} - override fun characterFromNfc(nfcCharacter: NfcCharacter): String { return "" } + override fun characterFromNfc(nfcCharacter: NfcCharacter, onMultipleCards: (List, NfcCharacter) -> Unit): String { return "" } override suspend fun characterToNfc(characterId: Long): NfcCharacter? { return null } }, characterId = null, diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/ScanScreenController.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/ScanScreenController.kt index 72032cf..485fab8 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/ScanScreenController.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/ScanScreenController.kt @@ -2,12 +2,13 @@ package com.github.nacabaro.vbhelper.screens.scanScreen import com.github.cfogrady.vbnfc.data.NfcCharacter import com.github.nacabaro.vbhelper.ActivityLifecycleListener +import com.github.nacabaro.vbhelper.domain.card.Card import com.github.nacabaro.vbhelper.source.proto.Secrets import kotlinx.coroutines.flow.Flow interface ScanScreenController { val secretsFlow: Flow - fun onClickRead(secrets: Secrets, onComplete: ()->Unit) + fun onClickRead(secrets: Secrets, onComplete: ()->Unit, onMultipleCards: (List) -> Unit) fun onClickCheckCard(secrets: Secrets, nfcCharacter: NfcCharacter, onComplete: () -> Unit) fun onClickWrite(secrets: Secrets, nfcCharacter: NfcCharacter, onComplete: () -> Unit) @@ -16,6 +17,11 @@ interface ScanScreenController { fun registerActivityLifecycleListener(key: String, activityLifecycleListener: ActivityLifecycleListener) fun unregisterActivityLifecycleListener(key: String) - fun characterFromNfc(nfcCharacter: NfcCharacter): String + fun flushCharacter(cardId: Long) + + fun characterFromNfc( + nfcCharacter: NfcCharacter, + onMultipleCards: (List, NfcCharacter) -> Unit + ): String suspend fun characterToNfc(characterId: Long): NfcCharacter? } \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/ScanScreenControllerImpl.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/ScanScreenControllerImpl.kt index 7721ceb..9ce42bc 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/ScanScreenControllerImpl.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/ScanScreenControllerImpl.kt @@ -15,6 +15,7 @@ import com.github.cfogrady.vbnfc.be.BENfcCharacter import com.github.cfogrady.vbnfc.data.NfcCharacter import com.github.cfogrady.vbnfc.vb.VBNfcCharacter import com.github.nacabaro.vbhelper.ActivityLifecycleListener +import com.github.nacabaro.vbhelper.domain.card.Card import com.github.nacabaro.vbhelper.screens.scanScreen.converters.FromNfcConverter import com.github.nacabaro.vbhelper.screens.scanScreen.converters.ToNfcConverter import com.github.nacabaro.vbhelper.source.getCryptographicTransformerMap @@ -31,7 +32,7 @@ class ScanScreenControllerImpl( private val registerActivityLifecycleListener: (String, ActivityLifecycleListener)->Unit, private val unregisterActivityLifecycleListener: (String)->Unit, ): ScanScreenController { - + private var lastScannedCharacter: NfcCharacter? = null private val nfcAdapter: NfcAdapter init { @@ -43,10 +44,14 @@ class ScanScreenControllerImpl( checkSecrets() } - override fun onClickRead(secrets: Secrets, onComplete: ()->Unit) { + override fun onClickRead(secrets: Secrets, onComplete: ()->Unit, onMultipleCards: (List) -> Unit) { handleTag(secrets) { tagCommunicator -> val character = tagCommunicator.receiveCharacter() - val resultMessage = characterFromNfc(character) + val resultMessage = characterFromNfc(character) { cards, nfcCharacter -> + lastScannedCharacter = nfcCharacter + onMultipleCards(cards) + + } onComplete.invoke() resultMessage } @@ -156,11 +161,14 @@ class ScanScreenControllerImpl( componentActivity.startActivity(Intent(Settings.ACTION_WIRELESS_SETTINGS)) } - override fun characterFromNfc(nfcCharacter: NfcCharacter): String { + override fun characterFromNfc( + nfcCharacter: NfcCharacter, + onMultipleCards: (List, NfcCharacter) -> Unit + ): String { val nfcConverter = FromNfcConverter( componentActivity = componentActivity ) - return nfcConverter.addCharacter(nfcCharacter) + return nfcConverter.addCharacter(nfcCharacter, onMultipleCards) } override suspend fun characterToNfc(characterId: Long): NfcCharacter { @@ -172,4 +180,17 @@ class ScanScreenControllerImpl( Log.d("CharacterType", character.toString()) return character } + + override fun flushCharacter(cardId: Long) { + val nfcConverter = FromNfcConverter( + componentActivity = componentActivity + ) + + componentActivity.lifecycleScope.launch(Dispatchers.IO) { + if (lastScannedCharacter != null) { + nfcConverter.addCharacterUsingCard(lastScannedCharacter!!, cardId) + lastScannedCharacter = null + } + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/cardSelect/ChooseCard.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/cardSelect/ChooseCard.kt new file mode 100644 index 0000000..ace9911 --- /dev/null +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/cardSelect/ChooseCard.kt @@ -0,0 +1,48 @@ +package com.github.nacabaro.vbhelper.screens.cardScreen + +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.material3.Scaffold +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import com.github.nacabaro.vbhelper.components.TopBanner +import com.github.nacabaro.vbhelper.domain.card.Card +import com.github.nacabaro.vbhelper.screens.scanScreen.cardSelect.ScanCardEntry +import com.github.nacabaro.vbhelper.utils.BitmapData + +@Composable +fun ChooseCard( + cards: List, + onCardSelected: (Card) -> Unit +) { + Scaffold ( + topBar = { + TopBanner( + text = "Choose card", + ) + } + ) { contentPadding -> + LazyColumn ( + modifier = Modifier + .padding(top = contentPadding.calculateTopPadding()) + ) { + items(cards) { + ScanCardEntry( + name = it.name, + logo = BitmapData( + it.logo, + it.logoWidth, + it.logoHeight + ), + onClick = { + onCardSelected(it) + }, + modifier = Modifier + .padding(8.dp) + ) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/components/DexDimEntry.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/cardSelect/ScanCardEntry.kt similarity index 82% rename from app/src/main/java/com/github/nacabaro/vbhelper/components/DexDimEntry.kt rename to app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/cardSelect/ScanCardEntry.kt index 36c4976..d81bb8e 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/components/DexDimEntry.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/cardSelect/ScanCardEntry.kt @@ -1,4 +1,4 @@ -package com.github.nacabaro.vbhelper.components +package com.github.nacabaro.vbhelper.screens.scanScreen.cardSelect import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Arrangement @@ -21,11 +21,9 @@ import com.github.nacabaro.vbhelper.utils.BitmapData import com.github.nacabaro.vbhelper.utils.getBitmap @Composable -fun DexDiMEntry( +fun ScanCardEntry( name: String, logo: BitmapData, - obtainedCharacters: Int, - totalCharacters: Int, onClick: () -> Unit, modifier: Modifier = Modifier ) { @@ -56,17 +54,12 @@ fun DexDiMEntry( Column( modifier = Modifier .padding(8.dp) + .weight(1f) ) { Text( text = name, modifier = Modifier ) - Text( - text = "$obtainedCharacters of $totalCharacters characters obtained", - fontFamily = MaterialTheme.typography.labelSmall.fontFamily, - fontSize = MaterialTheme.typography.labelSmall.fontSize, - modifier = Modifier - ) } } } diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/converters/FromNfcConverter.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/converters/FromNfcConverter.kt index 459afbc..a398aae 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/converters/FromNfcConverter.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/converters/FromNfcConverter.kt @@ -21,17 +21,70 @@ class FromNfcConverter ( ) { private val application = componentActivity.applicationContext as VBHelper private val database = application.container.db - - - - fun addCharacter(nfcCharacter: NfcCharacter): String { + + + fun addCharacterUsingCard( + nfcCharacter: NfcCharacter, + cardId: Long + ): String { val cardData = database .cardDao() - .getDimById(nfcCharacter.dimId.toInt()) + .getCardById(cardId) - if (cardData == null) + if (cardData == null) { return "Card not found" + } + return insertCharacter(nfcCharacter, cardData) + } + + + fun addCharacter( + nfcCharacter: NfcCharacter, + onMultipleCards: (List, NfcCharacter) -> Unit + ): String { + val appReservedCardId = nfcCharacter + .appReserved2[0].toLong() + + var cardData: Card? = null + + if (appReservedCardId != 0L) { + val fetchedCard = database + .cardDao() + .getCardById(appReservedCardId) + + if (fetchedCard == null) { + return "Card not found" + } else if (fetchedCard.cardId == nfcCharacter.dimId.toInt()) { + cardData = fetchedCard + } + } + + if (cardData == null) { + val allCards = database + .cardDao() + .getCardByCardId(nfcCharacter.dimId.toInt()) + + if (allCards.isEmpty()) + return "Card not found" + + if (allCards.size > 1) { + onMultipleCards(allCards, nfcCharacter) + return "Multiple cards found" + } + + cardData = allCards[0] + } + + return insertCharacter(nfcCharacter, cardData) + } + + + + private fun insertCharacter( + nfcCharacter: NfcCharacter, + cardData: Card + ): String { val cardCharData = database .characterDao() .getCharacterByMonIndex(nfcCharacter.charIndex.toInt(), cardData.id) @@ -92,7 +145,7 @@ class FromNfcConverter ( return "Done reading character!" } - + private fun updateCardProgress( diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/converters/ToNfcConverter.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/converters/ToNfcConverter.kt index db0f88d..644e69e 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/converters/ToNfcConverter.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/converters/ToNfcConverter.kt @@ -84,7 +84,7 @@ class ToNfcConverter( transformationHistory = paddedTransformationArray, vitalHistory = generateVitalsHistoryArray(characterId), appReserved1 = ByteArray(12) {0}, - appReserved2 = Array(3) {0u}, + appReserved2 = generateUShortAppReserved(userCharacter), generation = vbData.generation.toUShort(), totalTrophies = vbData.totalTrophies.toUShort(), specialMissions = watchSpecialMissions.toTypedArray() @@ -94,6 +94,23 @@ class ToNfcConverter( } + private suspend fun generateUShortAppReserved( + userCharacter: UserCharacter + ): Array { + val cardData = database + .cardDao() + .getCardByCharacterId(userCharacter.id) + + val appReserved = Array(3) { + 0u + } + + appReserved[0] = cardData.id.toUShort() + + return appReserved + } + + private suspend fun generateSpecialMissionsArray( characterId: Long diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/settingsScreen/SettingsScreenControllerImpl.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/settingsScreen/SettingsScreenControllerImpl.kt index 0e46146..632716d 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/settingsScreen/SettingsScreenControllerImpl.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/settingsScreen/SettingsScreenControllerImpl.kt @@ -129,7 +129,7 @@ class SettingsScreenControllerImpl( val dimId = database .cardDao() - .insertNewDim(cardModel) + .insertNewCard(cardModel) val cardProgress = CardProgress( cardId = dimId, diff --git a/app/src/main/res/drawable/baseline_edit_24.xml b/app/src/main/res/drawable/baseline_edit_24.xml new file mode 100644 index 0000000..9a3ef8b --- /dev/null +++ b/app/src/main/res/drawable/baseline_edit_24.xml @@ -0,0 +1,9 @@ + + + From 1566876106999b912f393fd599fac4e67bad9050 Mon Sep 17 00:00:00 2001 From: Nacho Date: Wed, 6 Aug 2025 20:18:32 +0200 Subject: [PATCH 08/10] Version up --- app/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index e459160..ed934d9 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -15,7 +15,7 @@ android { minSdk = 28 targetSdk = 35 versionCode = 1 - versionName = "Alpha 0.4.1" + versionName = "Alpha 0.5" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } From b95edfa2a18015b260797c34702ec4d925636df8 Mon Sep 17 00:00:00 2001 From: Nacho Date: Sat, 9 Aug 2025 12:55:38 +0200 Subject: [PATCH 09/10] New icon --- app/src/main/ic_launcher-playstore.png | Bin 0 -> 17390 bytes .../res/drawable/ic_launcher_foreground.xml | 63 ++++++++++-------- .../res/mipmap-anydpi-v26/ic_launcher.xml | 5 +- .../mipmap-anydpi-v26/ic_launcher_round.xml | 5 +- app/src/main/res/mipmap-hdpi/ic_launcher.webp | Bin 1404 -> 938 bytes .../res/mipmap-hdpi/ic_launcher_round.webp | Bin 2898 -> 1780 bytes app/src/main/res/mipmap-mdpi/ic_launcher.webp | Bin 982 -> 658 bytes .../res/mipmap-mdpi/ic_launcher_round.webp | Bin 1772 -> 1108 bytes .../main/res/mipmap-xhdpi/ic_launcher.webp | Bin 1900 -> 1182 bytes .../res/mipmap-xhdpi/ic_launcher_round.webp | Bin 3918 -> 2420 bytes .../main/res/mipmap-xxhdpi/ic_launcher.webp | Bin 2884 -> 1666 bytes .../res/mipmap-xxhdpi/ic_launcher_round.webp | Bin 5914 -> 4006 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.webp | Bin 3844 -> 2198 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.webp | Bin 7778 -> 5510 bytes .../res/values/ic_launcher_background.xml | 4 ++ 15 files changed, 44 insertions(+), 33 deletions(-) create mode 100644 app/src/main/ic_launcher-playstore.png create mode 100644 app/src/main/res/values/ic_launcher_background.xml diff --git a/app/src/main/ic_launcher-playstore.png b/app/src/main/ic_launcher-playstore.png new file mode 100644 index 0000000000000000000000000000000000000000..4a7f6e8a0a53c1d440102ff84758939de28e640c GIT binary patch literal 17390 zcmeIac{r4B{5E`(trYE2*^4$5MRrM9vK3O+WT|8+`#uw?q^xC0ma;EVb}=NCk*%y5 z%curf1`WnC#yp=Xe81n{@w~_J9LM|L^S*!S$bDV+bzjTpbAHbAyaem%sBPM?djo5r_FCYjb{F4!3V}Tztp6!bWa=KVU^^}39Ik9i9dPfI_c3ZsFhxODOy{CFl1S9#~ znLh9z;IG;Gj7fms{juPo>$&KMyV$Q@E#uGJ{syf+d4L~dbZV0CP1T&FzwU&)U$e~3 z(&D-CxGv6?I!qYnHQSclxRaA{mkT>(hM&8HG9bvC6c`Qvq5=F%4l=87kNcXU7EfEur(JMh;LkT;K#Mqw z_;#6jWQAC_zq(xbsXfokq?i?5d4U;8Y|lJjyR4zO-Ch}IRkOW6e|!kuHbkAn8xa}#oeUV1qdT(n>lzakOw6|bw8dBX4Yi<_3+?z2 zuNNDTf=uILJKJxsEr(`C+C~F*Ns|_>8lpu{si{{aAcW_K5NxD`&F9$8S0?d+ten{) zmZk{`KApMd7~FH8iSld=LU=-dW~eEi)uWPp=I8|B29{mCG8xCNtdqrvK2kOiz~1CGSQyR8Zx7=5DCaWtsGPP;i4#Y# z3Wi8{db)P}vm0&cADxS;wU(x8Ifq_`h;68P_PS>~S~?ry-&AbZv7u@%rrCka%;D%V z)Oc)mw8N^EpWg$f>mh-Myh*zE%Eed4X=3*3p{^Tz2cH^gH9b3FNMW2(*@2{Hx(qiD z6}BkqbDc;X`t>!XMz6+~8zJ01gs9)yNS1UPHRz{Y3vS%)*=-YZ>;nG)93hPxt*OBy z9XUoiWwH-W*nj&n^Xuzr(P5LxeoPZAS5u;bPkYeDk$4%Gbnc^9&~}rizgp8mvh-dA z$ksiCJ8nSk?BW-m@OiGT3EY6M@jhd4B-nvPii#~F` zVWGr#!MV_n;_k+9dWfKb?&~j~!rjbg=8+}3)Av4Wgw?(@i}M3|YoS*CVnls;gim*uLt!+CDNC{&N-hEC-}O zbq6BCh`fA=pm)KLf)RA4Ai{svfZ-PW$Yw(xY=NHP$00`K>i>n0X46mdTeq=nms~am zWXsD9X#H=D${{;N={vsNSvh;6e3)0i+}Xx0bjJv;_%H)<{slafx9(l$`4p`M(qM=C zcKQ;R|9E(~F(<#ujy%zKGn7v$(L+Qx(SULXzc?6UVNfyH(3~I_nB1LwYMp+%7B_TO zK9U~aSYXr8UNx_Rpw+UGlY_E|R)TED@td7m4r+qy*`=Y}$U7leHfJ8hef*o{;@o6@ ziBMzq#aDJLoV>ZvNB;t!tSh$YM%&q9lW&-r)^jpMr+-HAppFkEUv|2%bbsS+Df{c@ zKExBn;a51(7QY9>`*^O$GZz{;rOMIi#Y!T&^lh9#_@iP2{B5S3GqyYDOF^A7bkhG z5#DY8)^+5EukIe<6E{1Lb1E&p#wC@>dQM%b!8Awkd(8ED#5cuD=TLt?MvySrr=Qi3 z&7@zR&Cg|q;DCtE=Ae&8l*rPSrd@C@^0|bD2Oj-i8SUm|OCjAu1XKKL%hO+Of9)5+ zua27;5eN&nDJr@B^HZFn-(u{aK-k?nB4$C5RIPZt)iB$jzV~(=k5@VRZcwFkO#d`V z^MhlO>Q)YdoNvWr7mc%wl}n_Cz+()Fe&p`tZFXXKiiqIf6(DgV53z0N;+tL1r29Q< z<$r(JR%1o><(9IKEmW&;z4D~O!bjk;k9@_ z3VBeWXbUnBaR|||WJ4?k6P6OaM-RHdzRWH;BD!NKrrrIrJMlJ8L@Lx#F($5IB zi&2GWC~4vQJ7{4fBdJ~X$HTvKC5T)nJ$-+o%QOXdC}L(`(#hK|jipBYmwwj`_`d0> zm9lNzO|f7)n>NRMz{DK!%G+VQ2-2|w_r4CC95{pj0~ZJ2)gK1Vjs*7~T3FvUge zb45SnS3(ni*FuC*xxQ*~^l)3Y;o%NjcZ%QQNQT$sm7(|@`oz;vIY{qHEf)bSEJBoLCEf9zYQmAM`9X!AT^11?{+lW`f}iKTcBu_>~maH8Df-6tf}Lie6;I4!jH zj;I<3@7d8b+!ylVSUzju5DaQ-+%YF?xPTpgYkdlBq%9-I=Da*3BL8PX!*`cgh!gWJ zVK;i-?94s*EdwDHomP2-u?V)#yIbPWvvXW=XdMH>o5eVwil{f$M~O61Cad#tUxoEq zCVR{EMJ?E@_f?-0D_14z3wq5|Ljo&4qIvdjgxi8#5D8pv&o(?)oaRsyz|7nwpvTxO z*&?4p-XF1%?fBIXZ1Aw!KM#jt=El2IaIL~TjO-2t$z0~1HBUrME)viESP-Oqyj7jG z_MIaUa)akn&SsId_NDx+XS;KsgXcoS{jY%A0^ckVsp_uZgSc3dWN{7e*CsSoEs+t6 zoO1WU5e#cyo(b9TvalBr!OcSknaSiA4;q8N6kcly@Oiam=idmmje*~<)x`Yl=fa+L zN4!6EG4Amm>FmK;>raokYf{gW$s%@rZ;QL%y5$(}tY3-1Y)D*0g=P6sO*x!{iR#e$ zSOp*V>yyuI=6^t7XH6n^_n51CQ#j<8qYH5`B2k=8W3?xUW| z3&Yy5aXX~ABCdRSWg=bZgjXy+61g^81wY}WbM!~_5KUBBIG-KA5h-(P0BtOlv{T&N zKCgUecQGayS9D`r<>-OC{u?dVmZaeHMCe$1mi}lCT3Gv8tc=Uh-BFcAtWX#eezvE_ zI2FRSEo2rtxU{>RdT;dtRPAi+I1W%wpY}OmlB;%$M=6yH8OL1 zdb!#kW4!a9Md}vfAuN)KCZjpX!V$fRx6=w*Q(YD)hrH_L#S3!F#rEB;jn8GGLQ$7j zlF=5VV60-Yiru@hhxlBbrH%Xeb-k$%{9Qt$#D0w2uIK9glo)c6ib^AzVVz3XwHM*5 zM;ARFFJYtkh3}o?m@4VU71#{{#~Al*Bs_dH22RO88Bl z=2S=xHPQGSEqW6`PmNxcfa+QY4)u}y(;=mfmOZ5(r%Dz!fwi{g&RpVQG@{iIsn@0D zSR#sTZ4XUFTHejjj!zGb3o*U!PQ;~k-d|dqh41@QSo^)lUX8}?x1xhCKdksaU9#^y zaukBY#kwQhTY3GL7p@iS5Z30OO`Q9Ci=uhG_rM+2U3ewywOKz||D_r-PKm5}ePl?} z93GY58M3q%NWK?c|0>v)XC~)X8EIJM>s9Da^)5F5SGO6Qiu3ObGWAaJy;%wGCPx<`k zw@H5FxN^Wa9d9(BYVptXdEm2Vg_PC?KCeDW)56oZ;*?PSQ<^o)n+UvrH{13Y4ah zcV89OioN3ac0{5p%F5x?Yh%1;wYm#z0;xrFJVB?R!D*RjXRX0+ zpO2M<`FvYh{_`7bACNi7s?|! z*YA^`b@A1156UtAx=W$OdtGG6mZ$i|qfvfMEcFN{!VmAB!I^0lU{QPr&q!;-|(AZ)oC*ED6>2A%>n#es7W?#gPX`{2puhbopY={QE6ka;RryiyMIQl+2fU@0-d01D z8#C#M>A+s#52OJBQ?*U7H_;*DRhOs^y<3pYl28C{K{luXanl63!@q5(1c3-yxdZ=+ zhXFa3vH`t~A^8@4qZbEh`Bhleam@a^+l@O8Upx-``htT%swDq3KxMn~xifQpOCNSUL;>g+qGzM4W&g=O?q5t%K zG3D$gN&9a{J6JwOSl+QIyW+!hTXpqo*rd6slW-gi9P}aq^&aDWC$qL@x5oN06*69= zRJ*`Pc}P|1&V!%r6mFkhYnu;XF}}yKLYs)X0Z7+&v^^`ObB8o^@aWH?PuT(g6DMKw z5Jzi$F5~j4J>SCO)nUhfhSmX@E%{nCh9(wc;qP^76MOH=Gwd#3&v0dV3;*+!+t0_+ z&$fx^WKuo26}(+f`Ei`IYd;`)CNq*XI<)eJTg(bTx%ooCA=v!-5ABaT)K}_ zVMg!h!(`0F(JZrylDK15XQw`>e3A|CLb`W#;{e;N6{3r?yjT ztIOp@L(w@|Kb^V+kA804Z^RKZd$ABU0p31;#>kNowM#X`BJ}9lkO~wU{p1{UWJm_AV*BC2 z7Tcxy-YPBUUjkF&kelkH3S&Up zY&V?s#kV4fo+_eA>?CTn{GHdO2jf{b?SAr9z`f0_MPY6qb-wyRCCh*NA&vAy4(=$a zfci%+t)dyoh2cR#FM7liuU@*~pet4HiNFA}7Kx@2{%mBa^34c36}W`$WP~42ZAg_G zk+1THYUE+Ut@M2o@($Jh)OK&*D7C)Uny>wd_L1&Sx)8F{unNS)M$lo}rOMSkD;7s{ zIu;{Wjym*2ubPV{meYo5^zy%pl`MNsFewSMyf|iOrl|4SYy3^!1zfYMnMa@7=$y;< zJFL71inrNnxKH-;`0l=uFV4NPN|v06grvD_!ZbleY;u-3TMk5hV=n|xJHIOR;gfN@ zyZSl!Qd2AyhZHYWdfwigH%`0_Y4+fKa8gFCDT`0xwQg8RbLWWSq|_k^AQ6^^(E zZMKqx-tR_047x_Ag-W{4*j7b-4o&_I(kKDVS+ zWaPd;^-ZU91%yl3!&`kYVyiQ>5pk{wj~HlO*yq%ld-(>QD)LMTRV6c7Io-ZksBw?> z)barlPu7q%NS`*?&jXCM#;3wPpSN+#q?6y8aqK;r-1*dL1tRZS_wLz=n~jjMvP(MB z&V=l&xgt4INhz)GVzv78=zMjAw)3(6vfC+(4oQS!&AfEh=&b0%+aiii{qp*yj%LNn zt=OFV_DnVQD+R>+oe1HFXhKJ89L8~|G43)YtZGI%#%rXF8^wRw`HsBX=o5NZ0Z<<6 z7Ko`5mZeHTb}a%TNr!wI=~?Q@g9UTWf8+bDAq`wOQvl-5zCT~3)hKqflvpf9BW zMA5;=?=L-jnQ@HlRECY^D9FWBt z`jk&iR7`sFa4IK?VF}uCGs~qKmo$r~U&>q7Uu3Ii`HR*M9$`=(3XZAw1i2u)iC+HawYweq2ONDAhTWP%=F{f4#N+0|Pm6O*x9U?2{ED=io+2c7F7D&{6XA zY)J(aE~h+Scf~lI(cjuoMkc$b{`_6X*FsS~A<_FrUg1rtsJ=wmS0L;kry4X^kJ49m&ENutdzvxN!#a#*f7N$he! zB!(v|QtndR_hxfc_P!_9q*yZ+t+rj)^q_srag^`qpKy+dKE|;R!S%$hAGs1stfQT=Hb=Y+t!cJ5 z2%e939aBm0SkSBX_bc{Oj?QG;5FG#kUl360BPmo;jIanV{Y>0p(4)>x+h+ z+}E#zX2acgqT!5uM}-@A>qXm5u5OXzeH9*?w?|^uB>}$t687F)_buV+qsgBnAi9U_ zvu%^nFd&)M^+)>Des%NvF%WdR`tz)@IU!Jj>%J;HP8$6_-J&GzHrjrE?6A(EqyY_D z$enep*>0()6IHN6-W2Hq2OwWq5LYsbQ^V)C= zKn`o+@9wc9k9|e?Ye#Wohl1r>b#12NtCt4FZxGK3Uao>}?D`bvcg3!MfEXfzXC?v$ zD^Qe|;WwS{Xb&Hof!ekylt-?8QB!DgR?hWTa;M?$k=)qvlzD)E?R0VCmCz27@!6qU zue*qW@>^c;A<6J*+@uGWW#4sk-SrLkm~Xw)+)$jAKv&gw9>N1?*@vKFWa-{4VuHorf zSzNt{AGstt6}SakU_9&;(js^#0UH$?$AoN@dNRIip7i8YI%`Q+#F2%4({!i9*sR7` zlxf*5%TIn>Xtwjr$piHtwa)6DoBeTFl{csXEd%Ptg)y;K0HIgVan?R9>?02jV17l7 zu=DF~uqGuLbc!!iCs7#+OScZS{Z^d$a=Pa5P9L87@C7Dc6vbk%*R!@%jcvsW%34{B zdS3`XKnScI;OMAA_MMy18Dw>_;T8h;Uu^ z;R)VnfbHV0_rH2c6{l~WkA3i9znj#PxAF^~Q0sJ%5D?6eWusEZzSb&=_**vW?=Vzj z!+_L}B-+xoFWg`I^IR1c2vId%Y~;!9YTj)PMw>|^S8sFOxY4Dw^qsTj?%6DsR65BI zX_u>_UzIdT+K;8fWm#l<7!XxaPOXD&$0;u(vmrjQOlA>hDZ*SGM&@4oW6fIn(_QOl z>Pf-RsV4g)Hs6{WI3?*Z(K`@o#=;gUw`}5A{?T{-`~8|<_p{8GK7zWBlHBNWgK)40 zlz@(v{>OUDT`r0xO2mdWZn=_=mtJr^-(TC)6ScmZi#zGvvM84qr#P1nH*oZt;RnNQ zRQ6LQBlio5zMWVO!Q$(0?(*S59s*^S`$u`+)La#tItG#e$GxA$s5+52u`)>5b|w=0 zQwF=MpPrKtw9K}!!FKK1Dm(7R@C7igDED|e#O71F&J>kMF|SSG%?U2T=Dxo_THK#K zV(TLzP zSs}(H^|9+nZ)=vR_TE2&MM$ks0BqE{ro)09q}#N@?>JdM9B{4j(FG2}*OCsIW+~nK z685?DWu3?Vq}jVSGwB@x=M{sOs~F}hCy#8Zn0@y?49&(}fihR9Q6)A{h|+C$qPWga zeNRT+!-ygUIy(?+^?QR)Z@3XdOiHk5xoyH)*m^TkW1OUm?APYjv~$`&KRxbnN3gQc zvA}{ak=)~C#8qk^G2(6WWe>%}ZP6O_VE>66CXQL_H&n;>DXi>o;+c3u# z5Pu3(R3p+gKT-VGz!J6-JNFC7e0{ERt@`!~U?7dEhoc&Q4O4Phe)s!K#>u*kX5v`~ zg5ceZUXMg2H{sw(%xtGgI+P-#bH3h$2(`wEzKV_#Yr5l08!}=0S%vxi5sHq7m zs*WJBZ4#Y+CBsK=MZEj&6Uh_zyuNO#5z$QDpnO5Ba_aq+UWy99NX}x;s(OQ3iJU~g zH|M$lo6vQEj>&mpm%Wvgi3)Rzb$jJKzfj6p#GFIF4Qp5ZKEp)k(KT2q=r+4A4H&&8 z;8*OFe52e!adx}qanoX|RbcW*Pw~wRe-SQ1!@8uO43*Q(a$eQn>SeAbNiVpAi!F=D z^vCN}{+bGW!bRE^PRS8V$4NCYQ#cuyy-;Ev_}crNEj=R6DQ6!`;88RTF3&!K-K>iI z>cQmKJ#7MQ5z^x0JuSb|k09oYU+X_v>V9Afx0z;H^Q zNjIJJ4_QAnzp{|_2mG*?Z-7(3+wHNl5kdILZl`{k2m8HH=5 z$Ynye17qlQ1H~~v5XgD&>H|}5^mS0u?I;ogl6y3j)&r+@XKmoJUYlOim3cF5UpMK*@fnAdnuH#%>RT5L;dcQ42OLXj5VnOjt;U;CioL(U{> z#XO2WFdRjdTTou5G1bFnew3CroZ+Q4Tw)}@C8dGqVF-Zn#h$oOAn7w_hoNTus}}Gp zfZ0Y;&V5|3=i)OEpR#8OJus>HC6b0DyVJ7LxUpvN{jKos-h>nH=st{+SNkMp93kW&Y13(`uYFH5}n3IVpmd-|XCS1eB!kxZ}h zweuhrcY(=5nQYmbs$mx&wLgqDX!ror?<=%Q1*hub^5{^UV&9gb%bC8lB!)92Bhg#a z!KDDwo2{(-cxGoDuna3Sej}ErsF%5?CszH_a!eO9xxi8&aN8?7r|KaA^3z_|2Sr`~ ze!PBr8A+_SUq`?LD>3VIi3NMGnVHKpyz}+Df|d$*;)OBxb!#6vM4&83rgxeF8}#LC zv$?RA^^*U>-Y@;`F~F%k0~sZIHi%(ufn1&*HsVkWDlFCCxcfNP;FYnV=r&Il zoqLu1O&w4-=w`y*$FC=Ma?!P?zcR%5r8ifDo(bsjM$jF3?P0W#nXxYMwBLv|p6<_i zR*$m(=h@BNZhqGsDJ%}nx zpjVh#hB?uTJx3!WmUuk}7&tb@LXUeb493u=cPop9iy9Z{p|;rq-b$g1{RTcg1S$>W%MOyI%Ia3f zCPs^zJcB^Z+H=zK{oMesB@5NFhkfC|8p7iT@62$$j^W(4HuYqz&kSfGtQPBvz~9C1 zO%sXyx*=+W^|i~ej!V;tuFS#OyK9HkjL&!K)RdQLY~8Z>HEAMo<2QX10R1^wtQQSpN6VY#N%Dc=Sc=2h?x zEhS0C$Z#kwO`V?o&#y{O7kC zi!&qJd>1{)>WZ-B4J8`9uprZ=eiD-x^TGq9sP1AEnf{&TmC5?!`6 z-&fz3YUX!T)Sa7wuu;!05Xf zwDwKSTPT(F(*J1j;cAr-crN}?`+opQG3vV>BG@0LUT@tW4{q!+E4opD@I2h5R?*@; z`oC5K$W2Vs9Sw@#C_$bPF3G@c+X5h}DYr}#w^aUXx!GZd9FVyEL-kLjw|KxM!wS~2 z)aJ0GD{htz3djS7bu611uWE&2p06W&ZISxNC;=c#8@8qP$A>%qpNtZlVNxWZ7&``) zfFb3<^++qxI@3^$pWs4rnd`EyztC5YgKsR2d*`4NcE8#k^Ts%1=;P3oDJdL1x zfWCF1}*jzh;BnEax$C9P9;#!)mt)zfs^O=#bTu{kytg?H2?n zqA_|x8K{&-x*LRa0L%q{y`0JsAkNWKRJ3fv%SUfT6n+D@`XX9wTW`U(yyfQv9eP3xwV3$*kIfvulB?JH>RsK$#E|tJhJyrJ(l6)M%lsJGo znbl`VYyE}qS!;mZv^qqDz_L0b#5r))Y>1SvIo!~2hE|Md%8o0vD^LT{|1Z0BwgMq` z==*&>DUg;nJQX;7FBU|Zp1mh;DLY6Ogscu^UK+LROV{f4Gjw=5mKQF9nV(+1J)>Jw zfw?qtkSn;q_OKhszXLxkitP+L0KQauxlDiB;}i?7(riCv_qDNC3+pD@?>=Fdic@$5 zvCr|((fzllFJf+)#OHUzs-my<&sCrQopZ2-ejNdr48!>QaHWXY2s(HpNNz95tZ{1& zGJotDdXCG+)ao?fH^+F#fzX@jr((N0b;{&=5jSE_sq}E7PoCYR3V+QYSW2aos54PG z>C~{*&$7ltID4M7WSDQ>XX-WhbckNl;fE;g0PWhq@u2@gg}GQe*t$oNw&YUmq|Udx zpxC_0DoS(rST&ql`h6wa@9%?#z~f@aqX=&mpYP50nCwr7>}}Wc5FBjB=6!!dVPwan zzGj`nCMKblyJv_!(q*1FqG>AJ?-_z>_V^#u^KdIf_JGZLxSJc+ACkP*yph~s&igO( zDLvC%%cp_Eqlqq|i^g+;&48T+DJVVm6^`(ZyUae9Q`gMg*7xO&W9F9A{HF0xd_s56 z_57~R{jtT;z!s~@*z{nFkn?i3AY=sk{Rc3wxU1(cwDMA84jVOVJv)BTHEQC><$@2U zKEz@>pKZlA+CCjIE;?XPleT%yVc6(^E2@7YZed>RAh>Yk);~_)bHvRDs7&xZJLLyo z$NY0>lx+zn3s7ptEdHtnUELNqhZod5bMHm4VZCkVxC1O~BAx6X*&NHTdZfY?%XfVi zas^)tnb?(|)J>=M6t!#{V=W;KW|t^MFeT7+d7Fs6Yb|)a_^MDe|Bghdh4Fm7{-g#T z{~PO=H2CgA9dVDWady=t$Aq4FF7G7~J6`A5q6#w3F>%aHq>SsYaAUoco!QhHkx~WXVx#Vzg#RHJRCCK_4{XC zN5MbQNxcuO9)W5UcN5&sBbW4Ug*6P;e_An)KiYGY9x(rH92erk0)(|FP9cWMO&o=o zXwbkFK)-Ha>Ri9yE@^AMFKbV!lTpJ<>(vm%e*!9AzoVI9eWAf1q}zj0g#b*YjLT>*L1&Wmml`y2XiQQ!pAU6BSo`y+oZ4`RI9 zKYfmwq|&{wND??p4{E6C$Tyxy#jDN#iEve@}-4_iG$9&b0derFCLnvpxJK!XvR}{?#Odc*Go50v7Y0Nl4v~ds9&HGl8*ia|MT@Gcnx4WlXE9nCwaV<^z!> z8?&|k%E{6`catN$J`HA6V1(!rxPjchNbYqu`)V?)IO@S`_4o6?5Uxto8P153KJ#GW zMYyLF5I-)m4J5z67hHrbHer)BDmToFu6|9jA1M6kU_{P-wY5GWPwSbL_#QWBOAj}; z?QrF3)2A)qX_H)ic*=y8WA9O2g;|3sPrPG}VCzq_^bb$xevnD$Me^mUmPqIRzAOM!)^w+dQ=CYWZ>v1qg!B@cq}oL8sYuw`0eG0s^Vq=^ zA{IGs;EoL|zR{5|SUmG_Cu357{s0kjmv36cWazWMbEL1h4*f`)Hrr(pPp43RX^H&& z1$Mm#s6J0PnL<#{HaNKJw>X|F$~V%3(yCDaAC&C}w{^D0aesCEX_{?P>i9+EI|#{A zfF;(aUvDg7KQp_Ejx9-HHMAI!iq0^Pzb@IeA9#yFrt>iVPViNo4kJl{E0STI!?wN9w!_QU6>w*i)O)pkMCzU z~dd&+{vC&~BV2Pu^rJ+OgwF$@%UnM;Dz z)vzZb6IA}8$a7-rW!d_j8$`XNK@}*38=l4D^G|)Z{H|f;%XDvu8Oy5>V{^z`-Vct4 z^^92q8(k^r5(eoimnxR~@bh z1Er-EBWBj_TxF1NZsxEqd_UZpW_CWc>)6%1-M|vO4Hs~`&&ekRAiWfv>(A_NBA3%P zAQ$$9-57i`1+IZyvFqwh0QEHeSULG`0)@Vt@bx!cCSl%GA201>J|(NR82b&%+Oa>s zd+7!oZ#?YwEcQLUQcnGQXFSDU6^fv9;oAER@YW?RfZfq9bg#@yv0De?in`zm0=+OZ zh-aJv@2_<-@KZw|hNdVfNBRR%luTpdLh%nn*DM4>WngmWj~~^rKvCVYlKL3_v{3K2 zqTej1-y$`enmSxQ9YZdkde1&G!%DbMR}koRJ}B+S%f57TDJ|(9buHZZa`n>s<<{KG zl7*JFEGp&D1 - - - - - - - - - - \ No newline at end of file + android:viewportWidth="512" + android:viewportHeight="512"> + + + + + + + + + + + + + + diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml index 6f3b755..7353dbd 100644 --- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -1,6 +1,5 @@ - - - + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml index 6f3b755..7353dbd 100644 --- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -1,6 +1,5 @@ - - - + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/app/src/main/res/mipmap-hdpi/ic_launcher.webp index c209e78ecd372343283f4157dcfd918ec5165bb3..90a0579932487c6ebfaae37cbb3b3c79933990b8 100644 GIT binary patch literal 938 zcmV;b16BM|Nk&GZ0{{S5MM6+kP&iDM0{{RoN5ByfRZ!cu$&h5dQ@yrr+gA75w#|>W zZQC}EZQJLJt^5@ST~k%lar7}FG6HUG+p3j&@59|a{Sn%Mg?|tdlhXnzkxS(EEdpw= z0<@j?-k%e||Gy)mgQNz4#6-oHFr$hCoZ$qgF)db6MPMW!!kEB2kYtO1>gey9DMDk{FO$Sfb2W|o@@3@yl-_byDc=m%^ zUvY}jq8!C&30vebT+b+{7lR+_b|`H{5hrGQ&OB3vS}U#=D7!in)&R z8E;uCANyQvE#{^E$$n5%e*^GBsoXXjfVqKlh;TgB-^_H7%WJ~E6i+o5mFSQ3^EHDx z+d$74?sV({bA9G>yxBro$Z?WsMhF|FpRXA7I8U94t)igtCw-P1*kyZGD zB0$0pA(X4VQ9jlH6nCAGt{jom-shlmO-ZjY=95}dFt;Z)ee5^99f>ODn9H^_PL^En zs;Gx-8+~pq7$8@^>6)91xwS%ybl6_EVc}f-5B0IP%=M#f9{C2WbWRaB8T{BFVF5;R z=~6>Sq{GYa9rm|dBR}UL!{R)}ya5c6YL9pejb;RD@;vX?(D8eR7i-JjmRCM6FUUBs zfRSzh2}xw6=_I7QK+EUlm9C+~dc!Rs2k-TrrK*#Obdc&x#eqC!ys9ja$<9(8Kzt$x z)C#MkdI1fbjFqoGjBkK(TA`$8OqE}jFB2wJj8lmUDTycLFWeBa~ zLJEp@97QL?EG;$XT9>1N=31I59Uab*BAM&2*7%a7$Yu}pUWFTPh#> zTx_j>vPN#YWtU<2mb;P^kmn^LLrids3=?y4iqWD%R7=<-kK08R6r&+l)y7=G0r;I(+eUp%;Fo6lLY)>0U4qfMurir3y{P= zwk$djyrTzmSabH#!W&-j8q=(33Dz)&UId?r=)(*uSj1AyD=L^qKcXLIbYcV(n2LFe M5k&|Tks$w{3Vw;ZF8}}l literal 1404 zcmV-?1%vuhNk&F=1pok7MM6+kP&il$0000G0000-002h-06|PpNX!5L00Dqw+t%{r zzW2vH!KF=w&cMnnN@{whkTw+#mAh0SV?YL=)3MimFYCWp#fpdtz~8$hD5VPuQgtcN zXl<@<#Cme5f5yr2h%@8TWh?)bSK`O z^Z@d={gn7J{iyxL_y_%J|L>ep{dUxUP8a{byupH&!UNR*OutO~0{*T4q5R6@ApLF! z5{w?Z150gC7#>(VHFJZ-^6O@PYp{t!jH(_Z*nzTK4 zkc{fLE4Q3|mA2`CWQ3{8;gxGizgM!zccbdQoOLZc8hThi-IhN90RFT|zlxh3Ty&VG z?Fe{#9RrRnxzsu|Lg2ddugg7k%>0JeD+{XZ7>Z~{=|M+sh1MF7~ zz>To~`~LVQe1nNoR-gEzkpe{Ak^7{{ZBk2i_<+`Bq<^GB!RYG+z)h;Y3+<{zlMUYd zrd*W4w&jZ0%kBuDZ1EW&KLpyR7r2=}fF2%0VwHM4pUs}ZI2egi#DRMYZPek*^H9YK zay4Iy3WXFG(F14xYsoDA|KXgGc5%2DhmQ1gFCkrgHBm!lXG8I5h*uf{rn48Z!_@ z4Bk6TJAB2CKYqPjiX&mWoW>OPFGd$wqroa($ne7EUK;#3VYkXaew%Kh^3OrMhtjYN?XEoY`tRPQsAkH-DSL^QqyN0>^ zmC>{#F14jz4GeW{pJoRpLFa_*GI{?T93^rX7SPQgT@LbLqpNA}<@2wH;q493)G=1Y z#-sCiRNX~qf3KgiFzB3I>4Z%AfS(3$`-aMIBU+6?gbgDb!)L~A)je+;fR0jWLL-Fu z4)P{c7{B4Hp91&%??2$v9iRSFnuckHUm}or9seH6 z>%NbT+5*@L5(I9j@06@(!{ZI?U0=pKn8uwIg&L{JV14+8s2hnvbRrU|hZCd}IJu7*;;ECgO%8_*W Kmw_-CKmY()leWbG diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp index b2dfe3d1ba5cf3ee31b3ecc1ced89044a1f3b7a9..8407439a7f03b87751f21e443c588ef82b50eb29 100644 GIT binary patch literal 1780 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 diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/app/src/main/res/mipmap-mdpi/ic_launcher.webp index 4f0f1d64e58ba64d180ce43ee13bf9a17835fbca..463df80e59c2a3b0507ed1383ab104c2ffd5bc4c 100644 GIT binary patch literal 658 zcmV;D0&V?LNk&GB0ssJ4MM6+kP&iC}0ssInFTe{BRY zDdDBV>KLMroKgf$iJGzu(KyY4M{^hR&5Jh52b$UKl=br1Z>Fl8;4%nO2-v_>rcPmoy&yyqc|3!Nct-(A20;qv_`)13 sES-^b7T>r;Dxxr>fL&bS##mfq*G3k8WKlxZSRxzqFM%{N#yX}G2~fc_V*mgE literal 982 zcmV;{11bDcNk&G_0{{S5MM6+kP&il$0000G0000l001ul06|PpNU8t;00Dqo+t#w^ z^1csucXz7-Qrhzl9HuHB%l>&>1tG2^vb*E&k^T3$FG1eQZ51g$uv4V+kI`0<^1Z@N zk?Jjh$olyC%l>)Xq;7!>{iBj&BjJ`P&$fsCfpve_epJOBkTF?nu-B7D!hO=2ZR}

C%4 zc_9eOXvPbC4kzU8YowIA8cW~Uv|eB&yYwAObSwL2vY~UYI7NXPvf3b+c^?wcs~_t{ ze_m66-0)^{JdOMKPwjpQ@Sna!*?$wTZ~su*tNv7o!gXT!GRgivP}ec?5>l1!7<(rT zds|8x(qGc673zrvYIz;J23FG{9nHMnAuP}NpAED^laz3mAN1sy+NXK)!6v1FxQ;lh zOBLA>$~P3r4b*NcqR;y6pwyhZ3_PiDb|%n1gGjl3ZU}ujInlP{eks-#oA6>rh&g+!f`hv#_%JrgYPu z(U^&XLW^QX7F9Z*SRPpQl{B%x)_AMp^}_v~?j7 zapvHMKxSf*Mtyx8I}-<*UGn3)oHd(nn=)BZ`d$lDBwq_GL($_TPaS{UeevT(AJ`p0 z9%+hQb6z)U9qjbuXjg|dExCLjpS8$VKQ55VsIC%@{N5t{NsW)=hNGI`J=x97_kbz@ E0Of=7!T@xO7k_?hJ)Cii4XYNs_NT@@I1in6r6GjropxBI2Dd@oswJ zsQGHPP0)!Xv)%OrO8GI(mWV=+L2=QvOb8N|6k?MISw z+>x}70m49|T`bxGG-3!a*8Pw5bQmIFq2UOx@D9ma+yh#%0*Iq)!9j5e$V_hW0* ze$$x5^H5(0HQzhz0YYY6rplO!BTeQx{$Xzf(_$wTzTgbdgkuz%Jvi1bIZhikAY8m} zy%->Ad0w0jfnRU^X-Q3wU*HiEMn6RN8)*drbdXr=1%QVIKQREW2tYdraoS~;0%2=@ zWgq?goO6WfSQ4ucZh8qqz(5V0W%|SnVTLduEM@6%%m$J*;B)XCW(tXg2!~4C1wqYO zUMd$MX_U%wd1rJ2@PM;?-KzaqY5o)=0I)$M3V$=29~u#tMiv5_0y2i^7bY@(QiY8b zh~^?EfD9nN<%vLEhK0o0B#|{9tDgCzFuWj%wi*+D zu!u=VQRr=n-8RHm{a!-L_a2%kem*x}->w&<@=h7bRpssHTii-Y89Zt__CNHSaV-Ax z&Ox3Yx00JL7j-BftABs~i{Fx3U6NyXUnK2{i>_W}#Ba&BFE1NWPD(D{UExa7OU;>c zQbF#x4Y|GkEhDDwQf;{8!2igb~I14(|eM&gH@C}Iy2lY_$dVgSKX6$EpY z=nk|>KzKSc@|}qA1Gq)rg^M(%DXV~;1K%Nlurw1pV9D2lqg_5=f3N+ zIIJX(R{x`dI1+jeaRtox$Wy6^3Z7O2I}2Drl=FBlKA>0_m^Cia6XQmi7!Xm`7!4L@ z6R>MOle7EzUFI?CJ2PXuINbV}e4QYuS?<9A4~m%IG2MzfE5>}|`~#-rWl1^&ej(q< zrrYo2GA{=FGnr5KJ}k{%V7K}(8waJ~s-!7!X}iz6s-UdV99p?+5T>gm+{}MoQ~o0i z#0?d6U3j>Bl`fZc>ODRq`GiN5b;?y${9;*m0A;%e{ROt0*uy@Oxr1(Mg1@_ml&xmM z;VCd(=Wg0;2H;!CrDRPym*J7|C(_k%8 aCRYlL`>F8_FfX)tiIQi;)4Vw_00{tw!3Z4y literal 1772 zcmVQj4N+cqN`nQhxvX7dAV-`K|Ub$-q+H-5I?Tx0g9jWxd@A|?POE8`3b8fO$T))xP* z(X?&brZw({`)WU&rdAs1iTa0x6F@PIxJ&&L|dpySV!ID|iUhjCcKz(@mE z!x@~W#3H<)4Ae(4eQJRk`Iz3<1)6^m)0b_4_TRZ+cz#eD3f8V;2r-1fE!F}W zEi0MEkTTx}8i1{`l_6vo0(Vuh0HD$I4SjZ=?^?k82R51bC)2D_{y8mi_?X^=U?2|F{Vr7s!k(AZC$O#ZMyavHhlQ7 zUR~QXuH~#o#>(b$u4?s~HLF*3IcF7023AlwAYudn0FV~|odGH^05AYPEfR)8p`i{n zwg3zPVp{+wOsxKc>)(pMupKF!Y2HoUqQ3|Yu|8lwR=?5zZuhG6J?H`bSNk_wPoM{u zSL{c@pY7+c2kck>`^q1^^gR0QB7Y?KUD{vz-uVX~;V-rW)PDcI)$_UjgVV?S?=oLR zf4}zz{#*R_{LkiJ#0RdQLNC^2Vp%JPEUvG9ra2BVZ92(p9h7Ka@!yf9(lj#}>+|u* z;^_?KWdzkM`6gqPo9;;r6&JEa)}R3X{(CWv?NvgLeOTq$cZXqf7|sPImi-7cS8DCN zGf;DVt3Am`>hH3{4-WzH43Ftx)SofNe^-#|0HdCo<+8Qs!}TZP{HH8~z5n`ExcHuT zDL1m&|DVpIy=xsLO>8k92HcmfSKhflQ0H~9=^-{#!I1g(;+44xw~=* zxvNz35vfsQE)@)Zsp*6_GjYD};Squ83<_?^SbALb{a`j<0Gn%6JY!zhp=Fg}Ga2|8 z52e1WU%^L1}15Ex0fF$e@eCT(()_P zvV?CA%#Sy08_U6VPt4EtmVQraWJX` zh=N|WQ>LgrvF~R&qOfB$!%D3cGv?;Xh_z$z7k&s4N)$WYf*k=|*jCEkO19{h_(%W4 zPuOqbCw`SeAX*R}UUsbVsgtuG?xs(#Ikx9`JZoQFz0n*7ZG@Fv@kZk`gzO$HoA9kN z8U5{-yY zvV{`&WKU2$mZeoBmiJrEdzUZAv1sRxpePdg1)F*X^Y)zp^Y*R;;z~vOv-z&)&G)JQ{m!C9cmziu1^nHA z`#`0c>@PnQ9CJKgC5NjJD8HM3|KC(g5nnCq$n0Gsu_DXk36@ql%npEye|?%RmG)

FJ$wK}0tWNB{uH;AM~i diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp index 948a3070fe34c611c42c0d3ad3013a0dce358be0..6bd253a4c87abaff5d15ee522cb2bf48286b95f6 100644 GIT binary patch literal 1182 zcmV;P1Y!G9Nk&GN1ONb6MM6+kP&iD91ONapU%(d-RfdALZ5Wq7>|F>EF#%+BPaTvy z8hw*Ax-&H5H^8l}J&tl`sfd(_hzf>moS91Kgr+EAD`E%zMq-VASrKq-+ghHy@4In# z_XKy3l`JOUGMX$(dTJ^djk{Iu8kdj9#%&u()XZMpc8kpqWJ{85+Sc4!``Em;ZQJIP zZQHhO+cx$(w$HV-F@Iy}YMlE###|BocjUH_BT18Y&lUWH{AY~>0QMyyQ4>Li(PYw! zNJ&;MS6Y-K`o4%m$(}dAen!=Dc^Non4r$n!tRoBnU@m>`Eo=zF@Q;ToWgRmskj|O}&6%!oE96!V0Wvfz(;bYzU ze-4clitWEZLFd!*w{={qn!Z>_SUe#HxVNTQcK*96n_n#7CPyAyv z;4>_KOms*>*yp;zf?ty^N!!ft9HG7-;!2v}LzooFs;=Iw+!I-25ine}oRX@AkqOQU-U!wrgMA}8|H1#jw9mhaU*`;NlkoFImyBSC69pX zk-PF8->Qt~Ezbt}IKHb&(x>MOUS*cr!T-ct^}sikt1LXCd^jJO%QMfa$LnHYRSGyJ zSs2NPxQmsU@F&jdlBddP_W2NeyeP{}j*}SCB86Un=Jt*WNs4k^Ml_m#XvT;Jx_MD` zMpRceP4h{8NOiB#hh&L}!i)J0E6lp;xV@OEKIhAz*6msCG)(iFPzeaumJ(MfxP3=M z)Oss$$Ry|xK*T@;fX+qes^^sSTFD^^r-w*jFAsNH_!ft-IvRkg)x|`n@4a*48 wN6wif1HYs$efq1{bx96}=o6o6Gy zRq6Ap5(_{XLdXcL-MzlN`ugSdZY_`jXhcENAu)N_0?GhF))9R;E`!bo9p?g?SRgw_ zEXHhFG$0{qYOqhdX<(wE4N@es3VIo$%il%6xP9gjiBri+2pI6aY4 zJbgh-Ud|V%3O!IcHKQx1FQH(_*TK;1>FQWbt^$K1zNn^cczkBs=QHCYZ8b&l!UV{K z{L0$KCf_&KR^}&2Fe|L&?1I7~pBENnCtCuH3sjcx6$c zwqkNkru);ie``q+_QI;IYLD9OV0ZxkuyBz|5<$1BH|vtey$> z5oto4=l-R-Aaq`Dk0}o9N0VrkqW_#;!u{!bJLDq%0092{Ghe=F;(kn} z+sQ@1=UlX30+2nWjkL$B^b!H2^QYO@iFc0{(-~yXj2TWz?VG{v`Jg zg}WyYnwGgn>{HFaG7E~pt=)sOO}*yd(UU-D(E&x{xKEl6OcU?pl)K%#U$dn1mDF19 zSw@l8G!GNFB3c3VVK0?uyqN&utT-D5%NM4g-3@Sii9tSXKtwce~uF zS&Jn746EW^wV~8zdQ1XC28~kXu8+Yo9p!<8h&(Q({J*4DBglPdpe4M_mD8AguZFn~ ztiuO~{6Bx?SfO~_ZV(GIboeR9~hAym{{fV|VM=77MxDrbW6`ujX z<3HF(>Zr;#*uCvC*bpoSr~C$h?_%nXps@A)=l_;({Fo#6Y1+Zv`!T5HB+)#^-Ud_; zBwftPN=d8Vx)*O1Mj+0oO=mZ+NVH*ptNDC-&zZ7Hwho6UQ#l-yNvc0Cm+2$$6YUk2D2t#vdZX-u3>-Be1u9gtTBiMB^xwWQ_rgvGpZ6(C@e23c!^K=>ai-Rqu zhqT`ZQof;9Bu!AD(i^PCbYV%yha9zuoKMp`U^z;3!+&d@Hud&_iy!O-$b9ZLcSRh? z)R|826w}TU!J#X6P%@Zh=La$I6zXa#h!B;{qfug}O%z@K{EZECu6zl)7CiNi%xti0 zB{OKfAj83~iJvmpTU|&q1^?^cIMn2RQ?jeSB95l}{DrEPTW{_gmU_pqTc)h@4T>~& zluq3)GM=xa(#^VU5}@FNqpc$?#SbVsX!~RH*5p0p@w z;~v{QMX0^bFT1!cXGM8K9FP+=9~-d~#TK#ZE{4umGT=;dfvWi?rYj;^l_Zxywze`W z^Cr{55U@*BalS}K%Czii_80e0#0#Zkhlij4-~I@}`-JFJ7$5{>LnoJSs??J8kWVl6|8A}RCGAu9^rAsfCE=2}tHwl93t0C?#+jMpvr7O3`2=tr{Hg$=HlnjVG^ewm|Js0J*kfPa6*GhtB>`fN!m#9J(sU!?(OSfzY*zS(FJ<-Vb zfAIg+`U)YaXv#sY(c--|X zEB+TVyZ%Ie4L$gi#Fc++`h6%vzsS$pjz9aLt+ZL(g;n$Dzy5=m=_TV(3H8^C{r0xd zp#a%}ht55dOq?yhwYPrtp-m1xXp;4X;)NhxxUpgP%XTLmO zcjaFva^}dP3$&sfFTIR_jC=2pHh9kpI@2(6V*GQo7Ws)`j)hd+tr@P~gR*2gO@+1? zG<`_tB+LJuF|SZ9tIec;h%}}6WClT`L>HSW?E{Hp1h^+mlbf_$9zA>!ug>NALJsO{ mU%z=YwVD?}XMya)Bp;vlyE5&E_6!fzx9pwrdz474!~g(M6R?N? diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp index 1b9a6956b3acdc11f40ce2bb3f6efbd845cc243f..8f6a4bad6ada3a6e6aaee3ad9e6eb056b86e98d8 100644 GIT binary patch literal 2420 zcmV-)35)hpNk&F&2><|BMM6+kP&iCr2><{uU%(d-m4<<|ZCd}bxBV6(Vgdl4u&BeJ z$l~}>PjqW>f`4h(wil9AJ)X5~+qP}nwr$(CZF_9nMpgZds&qQZ;&Z>V?~Z3SqW=)w zNKzy@77v#joU!J5ZYB)m)toZ4IEd~6rWJg7ma4Lf9@*%O6DL{$7#Lh@F zj*v#%+VMu;_r7`4Huje~w#K$?+qP}nwr%rQZMz-Yo)3=$Ns`)T78T*pA!l=^7nuJX zp`jI?XkF`TV#KRb3No>gFo@^mt?i>cCMKkyFHhNP&Y757cMBc$NUj$? z`|0=dRy}WH^TXPYk6cUmR>K@sC&+=cK8xO@c)#J* zwP!@$QDsfFTF%;g(T{?p!h-PpE$5QTheiD;ArfnQMp#^vxY%=XeC}w!{l+zA{-P6m zf`Vjn_oB`7*)3f)&aXP-pKReiWv+|cRFY!mXQA3#l|nvf%laMxl4v*lutgNhq zl8J-+$bxmCoEdGo2Gw=JX{UyVO-i zECldX2#t)$1e%X%PZ$HFbs~E4UHxVdcnBE0IHi z3qD)1VFvMEXQhMEQCIHmVM|_t3#Slt9cB`{zm%ZXctq`@DLeoGb98;wfa(HxN`iuV zDw(XM>@jo7sCf6;rs5fHa8JzUwH zQ6oR52UW4XqYa(QF&rlY!1GdQ^BHFD z0f*|#b5-6R|G{5BeyI!pk5TzgTz>L?{_-1q<~Pn-O4v471!cQI!aE=r!a=Nk{0eWM z`QPE!uiw>#|HL_$!ZA$Y&+@4XN&0(wI%stkvRDr91>rIvM&*1*1BMf|N3F#G0a=>H z0FYQ&_m8+yEqMMA+Yr-%R+`~+E#Pytz5@GDQ}Kri7l==(4F2~u<8T^!rSKpPB|j;| zTg>th$SY6`Pidfah|rKZk_N-NPvuZ=L3(T#L7XKYMRO1mi-WL<7bzY(D( z6@xQN|5tCTVnfSHGGuGz?c|+TyNbEx2@fiRm+lKvf4rb|w?e%D2q20r1wSO~{Nk=1-kH){30OR=xtBXC{sgJ#si&qQs=#n`l%#LV{L#`_Wi*!}iA z7r7y(;Zp>#uP3geRCSu(ey$R2Jc#x>;zBLL4AB(zeB6nK)g^$Ng?yQPUu)5O0?WA@ ztTZzMXJ`^ewNwD?90Iua<&H3XBB@fZlFi8xK-A(4#lDWXBG#DO2G5IIGO%^)LA11S z(J%%rqTBjpKxSr3L@;tW8;x*TwiYgNF_O&9pghM3%! zCgku~Ks3et{0U5b6Oz!Ed2#idCJy_l`|2i+lGW%ZElGyaGpQkQZrY*Za~k=CJ>yu} zLWDuwr0V&4Y+}rArz`KuzYEmd9$|%fj?kCC0D_I)Aymj7vh-{ejMTVWGeC zcN_f=96l1O<#={m%YB<3i;T2)-j|UoE|C{g*^R09oFunb5lSZRLqk_VG+a7zi*WtW znEQ4{ZfF@AMyVO`q7lVptmgv357b70;%xz@Rqq6eFoB_hTJ?PL`dN zs9xk|3*?GnV&^>u56!yi-AU;_%vhs&fA3B{-zzWs+kPGIFD}y{p%ufz$#)DMYrS;p zrgWQ}^F*dHue|fI@_MU$oGfB!NoKSXFDHzQUK}eM7oWf}bgXf#b7*{M z_0s93%f7Zlqqlkt3Gi)ftT%cxVVA$Pqe4VCQffxAOf0PIoLn1s#MbWb*SANdCcW*` m8(?06iCDyyl7;C3fBY6Q8_YSjb2%a=fc}4E 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 M85_<9#-B<70Bv10#Q*>R literal 2884 zcmV-K3%m4ENk&FI3jhFDMM6+kP&il$0000G0001w0055w06|PpNY()W00EFA*|uso z=UmW3;Ri7@GcyiBW{ey$jes55b5S`|ZVZ{(x$xch{z?D+^{yErVgleVwa9qvGt40r z42;MG=7<0QySlzE=Ig6%01!FBK^$Fsxe@Hfe6aCy?Wh2r0~}@_lQAF90oTUi0FhEr z#(*;kTC(r!tQk6;gxj4h%FdHAt(^M3YvYj(!tOeN)+Hvj6+< zzyJRG?^lZfWuR#t!tUKP&(?%3v&Zd$R2YN>lB(Lq`OInY48%4%yTv2 zYe1{G`3)(PDEio5Y@-I5tUf`c%%OCJMtSW56g3iEg%3`$7XSJJHyA z<|7&N)5Xrlgv~%BO24eFd;Hd;uiK%D`EdK|quUeRZDqbh9l)%j%J#0lfrZumvA<_w zu&=AVvdChf6}eqh(bUz`(`Ue*p01{fBAcTgKyDYLs_I+YyJEk+rM@avU~>fB$n)HS zM7pfJydu`i%gfS<{PF94kZDv$t>06sAkheDzu40NJ$5CMW%n^Lls?8^p^QGWURbKu3ZduZQZ((s2? zzE`}<{;Zt7<$C|9R8A~DJ~@%x>TfP zF>TX8)@v|t)q4GjRt<}5s6hLHwRel7>V@&r-O|Av(yh;Q1A{E>Ir>p+%dHD|=l+lT zpr(Dg&>#Nu=!)6bCLr-ZS%|;h)Ij$+e@r8_{qO19QvDe=&1tmpY*0lcA^Cc-#{9fQ z<~$*<&P$Q<_jy#<$40PMofM7aQ}C=jphI`4kLg}Z7CIN#26D{-4v-_CA-LiE@(%{y!BzsU%gG`Q?sjLUf%qFSl0y)2#ae*+EI>s|i`d^V$Dn)qmzqRq6VJRY|{4ujsIU%#bnqU6MR&-1I_43=|5(6Jr;Jvert) zE?S|Tmn}Tv<-??sxV5@9t}3D=>YZ0JrQe$CO~|EY=Lj9RM&4svQHPQL6%pV5fPFiH zfXDx;l@~et{*{U*#c#Dvzu)|znDO7$#CRx)Z&yp-}SrD{&|(MQtfUz~n35@RLfUy=aqrhCX0M}J_r5QsK~NmRCR|Nm&L z41UdsLjWxSUlL41r^0K&nCCK>fdR-!MYjFg(z9_mF^C|#ZQw?`)f6uVzF^`bRnVY& zo}@M06J&_+>w9@jpaO4snmU;0t-(zYW1qVBHtuD!d?%?AtN7Plp><-1Y8Rqb20ZaP zTCgn*-Sri4Q8Xn>=gNaWQ57%!D35UkA@ksOlPB*Dvw}t02ENAqw|kFhn%ZyyW%+t{ zNdM!uqEM^;2}f+tECHbwLmH*!nZVrb$-az%t50Y2pg(HqhvY-^-lb}>^6l{$jOI6} zo_kBzj%8aX|6H5M0Y<)7pzz_wLkIpRm!;PzY)9+24wk2&TT{w--phDGDCOz{cN_ca zpnm7`$oDy=HX%0i-`769*0M6(e5j-?(?24%)<)&46y0e&6@HCDZAm9W6Ib#Y#BF6- z=30crHGg+RRTe%VBC>T00OV6F+gQDAK38Ne3N9bm|62tPccBJi)5{B z4zc^Db72XiBd}v$CF|yU{Z=M|DZ%-(XarYNclODlb1Kz1_EKLy(NSLCN`eUl(rBCL zT*jx@wNvze0|TSqgE(QArOZU)_?qH(sj#TwzElLs9q)(0u!_P|R%Cy_0JFQxgGV>1 zz4?_uq<8_gM0`c*Hh|;UMz~vrg1gQXp{ufg`hM_qU;U>+zmvc5blCLSq@PrEBSGR# z&8=2Z4uXN`F3p73ueD1l{s{k$WipAvSh5W7ABe?4)t;r@V?y`bNB5FvBuE|0VRTb< zM1Hn^?DSsJY+sX@T5xW=#>T9VEV|?<(=6|ge$X6Sb05!LFdjDcoq*gM(Zq=t;_)Le&jyt(&9jzR73noru`a# zN*<`KwGa^gZU3-)MSLF0aFag#f0<>E(bYTeHmtdbns#|I)-$)mJ`q9ctQ8g0=ET?| zdO}eZ*b_p>ygRTtR^5Ggdam=Zb5wmd{}np+Jn1d_=M`~P=M67jj})fH4ztb5yQqQW z^C|C&^LHAK-u+ooIK)yM)QM?t;|<{P;;{`p=BclzAN#JzL4jCwXkQB1Dy{=^KR`=~ zTrr)y7eiYBzSNs_DvO=4A6#EgGS-zY%Vi)N*Yb`U;6o}KR}dq{r9pT5wqZ@3NOE8- z9-(}D|Nc5732CSYQbL)!gPQ#RbD8BhK3dl{sUuPvei0tkvnJBxDEAYTesU8H$)g(Plra{VH(v3u^CO1~(+ zU0O7#)jaS4{NcwA+LuSm&VBcX2#Im3xg)W}ySNw%->orn1taZ&+d)}8gJTqA!u|5P z{yv?zol_3|(1(%M(EVU=cp?L`{Pi|ixk{U)*guFML3P!OSlz;zGA#T+E@8@cgQ_mv1o7RSU=Zo_82F?&&2r;WE z@wk}JHYEZ9nYUc(Vv~iTCa3u8e4q(yq<29VoNbKk|`mq%I6u)My=gPIDuUb&lzf4`MEA9^g8u z)vp8|$$HE9m_BTV?lOosIGa4jud=jIbw)O2eCMfyw2*S8?hjWw^nqws$O*M$3I1)x zR0PWFb3$ySOcGTe1dz%N0l;RPc`x%05FtT^f^j{YVNPjaJER_`xQJe^4 zx~seXRfF&%EYP-fOp^58`@aAG!x0%yTag*@N0hot(>=Cr+n#CLwr$(CZ6iC|Mpng# zI{_56Z7(EgZ)!ZV-GjAl+cswBH`cao+qP}nwnl3z0wClJy92|)k9V?wAy zLhwVwgraE*qm!7TX^P;-g9%NceJrbH*o@dR%fx03tI1-tPtgQF5KK|%Ud1XuvYlgH zTO< z;bNX+85RJ*Qx5WR8X=0pi=r?>hHc?N0N@#xhv7WS@BrXJTNp+NQ!An{Le@IL2LQlu zkd|Wrzz0sS79mWnhN2MiDOUgh!$NYF0RUI{G(r@$5=?6;(=t4O6O154uL08t`;T`4SeTjx@UH)0MB@ce2wCGU0EUOjc?Q7U)*wVp zCqT#!01O8U?!Op*)?E!aIOf^QwWfvTY=$WuxDo=X#YxK*MMihZOTY6 z0ga4!6m(&<7#O6(oW;U50A28yaYQReBXbr(7lyaUrelA1;U4IMMJ+@p6Ce$*gDwou z_km%|sHyyuLWAkjGGPE+uxK37i72GU z!=MZ2i%_~2EqqjQB!*JjDjc8-9#uh98iIsw1zlJh=z=>W5lMm(b{6Qu>OdEqPY}*Q zVLJl@>jMVtg{c$*#8io;eJuon4OJwr6 z$?|FG&ipecTb2>y+yVyNLLoeg#U5a$8|**H$r|6-Ab)Sv1B z11_b=MHKdN;341pCdlKFiy|+n0r22Un7)P(7Xmv0Pv%XqOj=4USa2mpE}^hh0Q?Mm zcI?=(k#)BoJ9Z~m4<5kBF?|Vf1h6m#$$O>HC%~|~1}r$4P!|xa-&?>-Px&ruys#mGgiE#%CyE9RQyyd*YvI z)?EOgNSfV0CuAwb1Na=F&j=`c0SlS#7lzA)?x_SUIE)gUz*xq^z-99F6mE&0K9xA| zxFW`V6!CFLXe%o}9Of*{~`;@))0uKC!686vn`v5ytw;xbDFX<>)Z|A0$&#X#)Yi*Xhk zO!p%!#xtX;1{~N`*dY`-7}#9?c0db$YcESaXUeKAi{Hm!FAZ35f~bR*bbhv@d60OL zJRxi5Gvy4B)%r!wGIVgI6x|`^cHmOwHU>KR$ehBG|0tbfNJf%OPe9~b#W--EERFXF zE$7i>hoTYvgd{gdn$F7JC76txBtwOPE6RCFg(iCxt?tEKpIZa}`b_UC0LYUrVzo3kAgi~WYX)v!IbU4nJ#%vk*j2*p(h94 z6pI9Vl*phLW=Xzd+8|6Amum{u*2bb~5q+fOksO|hHiyPYu*K96(y~8HuZzo~hEB9B zrHZHYil{vzq3G5eMQAhKBWo5S-{g}a)3nr(#i!GI1nv z%3>SIoT1Q!1y?F|NGUjEnuz5L^n&F-B!3ju$H@v(_&`$L7Qjt{9>KTqR?R+8cA4Hu$Wp#1SoR*t5XR z(z3FhG`^^;3kMcVOA8JVC9UR4U}uRi>=fZ~p$iLc)+3Ku*8w*AON2ai<|UK?kB_ z)$O(h+sZjYO^!mHL|WOGS>Q8e<##VBQzaf)KU9_>Jy@h*#Qnf!^7RmIiT`~nao{nN z3L@M^>(OtYT)bbRrPrrfzQP334f$I?v@{@mumiFTqafGLDzRx z%ar+ zhX#}c_yS5wuLn{T3M_=WP{k@iai7F=dRuM~9wB0@z&KJP=|vpq`jo~Gr)^3jLY zifXS^sQZ~VA}1i|a!g4m8r5(Wu!A;5B#oUc zpZboD{WB)ZCzTLmTw|f!s9{u5g2LA!OiSxEZnXtq$E-zDA64v!p>DBI7JbaPK4}`G zkwhqO#Ps%a4!A)r)EMj<2gTJtm^LaeBxoq6B$Z5P)IEXc*T?fv57aQBBuU{h2-DK4 zCd@kn*!8hcE--IGRhq_VG!e?`GpXrXU{}XNxk+qNzpRj;;h2aN44c+=H!y2spgdr? zX~PO4g2y3DNh%sM(^h)|m~$`ijNR@iGi#poOn4s5lQ%-+dD3#>V4ytR)|oM;C`nRNxho8~&a+VNZ|gNos>%om%1Z>YYNpJ^ zGS>jljq>cy$+dr@C|u`wC^y6kbEec}1q3I;L@29nN+a6N1YifjQ^a8kTxS8wxzW)u zr7kNZ7-hypD63||Y{YeM7T7_`vrue3AY!v7)MSMOquiJXWmJrs)7pOJ1>m@Ka2%9{ z`um8@8B>uF5|p0^q?HYs(`@Tp4Zy5|0VvnC%{0v$QkE7FJOL&GDJ6p%=A&KrC?5dN zt$^d9d{Fm^ws~^~m81j&qbV^hsYjnF&4{u?x;Jrb-+4`z-+g za4g61*YF(2asU99%Ki25?FX?u+FDv>jObOA5zz#rgsf2l{fwmjusxY)a%a&0}L{Grky=em16-oSzby=6O0y(2_=w_kyqAh(5PwiT3T8WM>{&PESAOcSlL#$kGIclU);XD zeR=z0`&|2YTOF%nMJ$VD(TR4%T3TA>O&c|+S6N<0LO>CW7LGBYg%UFIN^1HH88vCv zyq30DERHzhh@&0tXh%EZh$9w@#oAit&6+f7NS~ULyo`j9CK#jDV@znFgp{m;qKdkH zgNBV7Gj7tfS+g1%X3d&5Y227m!v^)Mt0*eSN=XQ5f-!oG7!!&Xh$N+DCP}*Q=lvp4$ZXrTZQHhO+w%wJn3c8j%+5C3UAFD&%8dBl_qi9D5g8fry}6Ev z2_Q~)5^N$!IU`BPh1O|=BxQ#*C5*}`lluC515$lxc-vNC)IgW=K|=z7o%cWFpndn= zX}f{`!VK02_kU+Q5a3m37J;c} zTzbxteE{GNf?yLt5X=Bzc-mio^Up0nunMCgp*ZJ;%MJvPM3QK)BryP(_v@ei4UvHr z6+sbCifQaOkL6-;5fL8$W($zZ_;CZp305C;~$hhRquZr-r)jjd1z z31%ZK{-(`P#|Um_Sivn@p$-vz46uqT>QG0B1w9znfS9A8PB2LaHdzA|_)yjXVR*l{ zkcu3@vEf7bxH0nkh`q?8FmoO_Ucui*>_a~P?qQrlZ9@+D7%MTpSnztpylXrt5!-k8_QPB?YL8Kx_On8WD zgT+111d(Op$^$&KLAN5+@?>f7F4~wFi(8TL8+szgVmcMDTp5l&k6~=rA{Dt}!gb^r zSWY<)M7D|Z2P0cEodj6E42PV>&>DFmQpgt)E-|#sSUU@uKed+F680H@<;-x{p|nuH4!_mn85rx>wz;0mPi2ZkL#k6;sznu?cXh!T0S>{w6 zL^gvR05NY64l*<+_L>On$rjx9!US;l;LX6@z}yi#2XHh)F@Oo+l)h%fq$v}DNmF2> zfs^_t0)3N-W<9-N?uedVv{)-J0W5mh#29QM5R5h&KuiRM=0Zvnf#lF=K#WlCgc#9c zS;qvh(P$!_a8JwyhI^ZJV2k+B6Z^64?w|1?5gyo6y{}923CRZfYVe1#?F% z7h2SUiNO3;T#JUOyovSs@@C1GtwipycA=*x5{BpIZ_#GCMuV8XK=x;qCNy{d7?wA~ zC+=vjls;ci&zW=6$H~4^K%v{p}Ab?U%C6Z4p%eC<3ExqU$XR<}LLF67A$Sr20DR_pJ3yeBa~ z^sw{V0FI5;UpwXsScYuhbqGQ`YQ25;6p6W^+tgL&;Ml;>S3CGpSZ>VrTn0m1$y$HU z&65)I!c?oREz};c=nLCliriqQX->4uivHTgd${GqeAlf*!P^B|jkU|*IdNP(&6C>4 zqOW$)Nw9nvjy^&`?E|gotDV{JmJ9Q~vuhy<`^C4XIUDt|j4o6rK^e8_(=YqC zuaR6TRVf@tUFHB079o4MBIh{M~4>WwnGgesQH*3?w(RA%hCZ*7)b!aNV=yOQ%o_Y=Lt0Sl*(9^jfRnC210Om$=y>*o|3z} zAR&vAdrB#mWoaB0fJSw9xw|Am$fzK>rx-~R#7IFSAwdu_EI|SRfB*yl0w8oX09H^q zAjl2?0I)v*odGJ40FVGaF&2qJq9Gv`>V>2r0|c`GX8h>CX8eHcOy>S0@<;M3<_6UM z7yCEpug5NZL!H_0>Hg_HasQGxR`rY&Z{geOy?N92Z z{lER^um|$*?*G63*njwc(R?NT)Bei*3jVzR>FWUDb^gKhtL4A=kE_1p-%Fo2`!8M} z(0AjuCiS;G{?*^1tB-uY%=)SRx&D)pK4u@>f6@KPe3}2j_har$>HqzH;UCR^ssFD0 z7h+VLO4o@_Yt>>AeaZKUxqyvxWCAjKB>qjQ30UA)#w z&=RmdwlT`7a8J8Yae=7*c8XL|{@%wA8uvCqfsNX^?UZsS>wX}QD{K}ad4y~iO*p%4 z_cS{u7Ek%?WV6em2(U9#d8(&JDirb^u~7wK4+xP$iiI6IlD|a&S)6o=kG;59N|>K1 zn(0mUqbG3YIY7dQd+*4~)`!S9m7H6HP6YcKHhBc#b%1L}VIisp%;TckEkcu0>lo@u995$<*Em;XNodjTiCdC%R+TX|_ZR#|1`RR|`^@Teh zl#w@8fI1FTx2Dy+{blUT{`^kY*V-AZUd?ZZqCS4gW(kY5?retkLbF=>p=59Nl|=sf zo1Pc|{{N4>5nt#627ylGF`3n>X%`w%bw-Y~zWM_{Si$dc82|=YhISal{N7OY?O`C4 zD|qb}6nLWJ`hUyL+E>-;ricg9J@ZNYP(x(Sct&OI$Y!QWr*=^VN;G3#i>^1n4e#Je zOVhbFbLpXVu*16enDM+ic;97@R~u&kh__kgP#!R`*rQEnA+_dLkNP~L`0alC|J;c; zeiK=s8;BsLE)KbG3BD&Br@(Ha@SBT&$?xX`=$;eeel=|R_dIr6-Ro?=HEjnsJ_b`1 zK6Yg^-6;^2aW!xeTK)A~3Rm|L^FCHB_I>jIju7ZGo&N_1*QHkxH2!!%@o4iZ?vntS;&zJdPe1dH#04YD93A44o-MpfD zP{rn_aq>U%RDvC2+bp;xPlsOzauIi3*Lf42`jVKKZCRuKdYhi>FDuL2l=v{$BCN#Q6796s%r-AG$Q^t(3c@ zD?w0UhYr11@feiyl9kY_@H8~|xlmO<8PfQmj1!$@WieW@VxR@Psxfe-v9WCi1+f>F4VL?0O~K7T?m4-u|pSkBpUJZZe*16_wAp zSYZ@;k`3;W3UHKUWc8QeI}0jH5Ly=cGWQPw(Kr2fm=-5L(d`lcXofy8tJY3@Tuadz zYWXR{mW7XT!RF#RVCe%}=tM*O6!AD3^(!8un~opNI%Uko7$5t@<8+?; zTxDys(MyyGsUjtSu9$+|_-t!U3fVb1dkK?l`17<+jfl=hrBHnDSV>^R1=TnQeyqbW z>ov#l%!1|S!1>8UUxIdhQq`_klcHVx0{?#>K3#$4GlXncwldt!g17TcvKq-jo_996 z>oA=tH9CqRl6Yw?Uc`am!V?lHJbizOJaVaScf1UP5e7Dbgabq=b!B~T&_F6?ooU>w%x0A zH~&MHJ=q`fCH{U<7MDXE4SD32cDZA)WJeWkllJ`UspWaS#eDe^kg^oU_A14UE9zG-a^g{xaXf$})Wik>gT zl#dkzGr(;h0JZDuFn(+k8wNq?PZ5grQ<+sM?wBGt@JnH6v0#or-5wBQWKU~(S_> zkE!tc*ZJ1Y&*p(xX84POb3cClRMd!^qJ#CAZfIepEj-<`VURS_yCz0(?*Ixcj4 z-!zV1_QZhpm=0<;*(nm+F>T=)o?ep@CK5I%g^VAA+RB25ab?7)A~z~egru=I1S|@v zH7tXV!0wmGS^qj#e+MY;C5eUjEAp$Y?LDkS^QPZ}8WN85?r$u<-Epi;yZ1|J2J`se z$D6DpH~2F=eI0B&=UFAUnJvZAmClJlK)sutJ?M>xpZiWV&0=G4MZP+x+p>EX=HbCz zxls%Mw?*u^;LbHWIWCyq+yi)`GmFn9J112CZda_u@YIP%i;srFg_paU02Ifij*7}l z&CF-(3|>*a|+vbNR`^RP=9G?ymEJ0Z~)d&c*UE$UMepZ zcITr{0WqhxkjUnM15js_gW=e3Uh|y6ZReaXHIz-=p`x5VvB&rH9y>Amv@^WmXFEw) zQXYrk3feir=a{jMQ+wDIkkFnZ$k{sJakHn*?u za%4b!00ev8NVLM1TY=cl?KB&55BY_MU-sg?c>=Dbz_W{(Z~c?HJi*XpYL)C6Bd8WH zt+v-#0&o~@t4qESi*)+eW%@VD0|o^yF)n0hME$UtXF$*Lvh}7sso{`|pn*JDIy5^Fm3s$5*zEE=?u5<=l8FJc3r%+H} zdfoNl2J0^~!-*mOL5o-x32|e0Im*E!yY7F7E5N)W3>+v_LBydlEx?4$RL5f2oYRD# zaR0wv(-p~wO0eLDl3K=%`{5+0Gd$ktO=W)gWlGZJ0`K z$_RNA=ckrfa;H0KA~dR^p�(p-{x$&=IACIfoAR!za)F-^da-t3#0Dycnp zwO~NVXwXCl;jE<}>%@xz|=8fIJAB?>+E{7)|4l${4ngA3G|=r z2Dyv;VVWSgZx9Wj>qUjleGl3Ei9K4>h!(lPS%8VOG>Xu0%6VDz^O=bjJmuP7>DeUv zrbI}MlHB^^d?{zv6d=@_ZD2lg1&G7UjnVN{1}9WkaM3H~btX0GtSzB+tZ^qRgWo4m z!GmimlG$=wgXCnr6j@m<1gAL46#T~5Bnm=2{^@>|t&`9mkEPddj zAvG~@Tv~TAm2i%VW}R-g(Z0)z-Y|szHr@rk>4MAyG*Ma*7Yh#H7(!-5>DZ@8r;_dx z{prSe<>~099F8vsYd2xff7uAS%7{S)f(|@me3t2$iy&NEc7OUEchp@9A|X;;IA>8!oX+y(BKJ$EzV* znR$z;!L$s7uy@{OT~nG#B!NRraT8(X##Ho!0r_o@gg0CA-9H^;-uE&?$2$nHv_00o z%cbuUc-tCx$Uh&EZ4Nf4Zgqv)Y6>usG3>GeQnxx_Z6+PcbX-+ysbt1hQ`K1LDpOE? zrAhIZhSN9yVIAOa22gn577tbc&i3|3V8NWy&!tw##`}9*x}gtI^h1DzZRA>UuaJG) zaZ7j)dq!O}{?#8Y7~7i6fHh4{`pL?>-18|p!S75Y#^DM>-S3)vuZG+Q7l@ek zQP~#cBpWgg#mApc_sPYjpw8odQuRokmTkzcNl`^CcKB7e&;zViV;{Y{o^Y$%7i0m# z62%#1Lq!RC?}lK>%mp}T!3Xv;L*0v*>USLm``N%>w>@fwC+#T&Tx2bN4w(20JB}oU zuSa6v^kXi0xPs?pbaOHnyiqq6By1EZY9OZ^^QA>{q-Hsd&m`pbQ%8121aWG-F5xf zlZ%;B{;C>X19|`^_?dVyCq>n+41w7|!tUS!{9rHlbhX=SZO5CQ^;!Du_E7*`GiR^Q w)2!4MKjfSAeN9k=)waxqERZTNm2( zWB4nx0pceB5?mtS2mn|}x*gAC)@R$cZKJ-(mfG&7gY=>j0^uwJ6)~*0lR`RZH8TViL<}LA8Evwugg}qkhGc43H#7Q>p(9tb zl}4)}A($$LYIL{Kq&OOpKvO2nf*DEekjjU`ifyKjo3Ra!tV!jGGpqVg&B$utLL~a{ zSJS5wL+KD|8k`-i-~^@=nLHLeX$7x1rzQbJ!SpHsf#GFvaoiHTSkjX}0ze;zc1;ufvryRM4+bSB^8K$;JagJC2p=NmO1 zMT>=$t~l)ioiE1a$iI3u#joUsyt zWzA2O-wXbZ6rh@RXyM)A*lP-ENxdsIHFpt`RRU}%4tmUh2K zh2t;M(i}3#5D{k(6}2KF*jx>X=q==Kg?r3{+&;;py&qOD4(%6JPmzE77hxm9=53F! zLqkLvh=_E->B80wUn*pp=_wKNH3!UoQjtb#Isli!Dws>Ck_nf-DpVM#NE`$hylhVu?-B%7%9kQRqXiP>diJh3ue=+TiU@W~9-`%6 z$heiqvk@2b<|AVgud!3c7Ld6cL{#7{;^XYBpp&fiM$UE_@dRMB4n(vZ5pfi&@Eca< zkKAj&^0*U4)XNQ%Ymr2GJ}^H3F^q=DRhk>bxI8RXh%!Hc#L-3~TF3qbbYkq{jbrUc zrS3%Z2okwZaIK0U%I}kzu9!XkyN2kye#(is)5tuwFiv`mJi(Vc8S|oX6~E+K&!gfe z#G~=X5l;C@HbrkY<4HJR=hlkKi2i38K6U}252CMyM8WUTUzUgr2a#c`mjaw4&fFQE z#puK612W>?nAlA+I%}IkFUa`dYl-L#`-dF&?273i0_{3~KBK~lH;4d-y za;QWUbsD)^Dym!YRUryJhxoF*d9W+`GyjD@`44HZ@4=<>I6@+#MVu$OBq9oWEaS6j zR}gWjjv`NaN3g_`%``)E>Ozd}99t|A z`8=AIj+gC_R}IVA@m2v=HE!oBEfvmlS1XX!ZDljOgzzLCSbs zb{8N)+Y!;86;$MRUy2rKf`~74u*%_>CW#dJh&Wk-72Pc(6mc4zt)bO1f`qNd*KyD? z>BMT{d4TtQ#BzNc0=)18^?*mi%+QM==+UoagA>c4*mFCHr7HH^R0AzBXbllC&pIW9 zs49?%%;{{{`8jOo2Jau(ucq07r1kv5l<^s!bDJW5tZ()`<ja$c^R_yxFbMfLZFN-Nh5ss7&9vx3TXTdKb|w;A5+<{m(Q-J-9W%Iij7f%kS_ zmG`E9eO|Y&ZA{$z1{`uhci)D$s>U(VBtAe3`+YOC0Gbbv-idwRl97KNqSANs27Oe^ z(o=Nby!QnoDe-F^VByl4d<1a6A}AP@lbIMfPvg-L`g0)U~Y z3~B{4bvi^qS;;d-dMupi|B%uPYq7 zUC}9TKL;JXS8E~Hd3=zU*!4h7HDiklp1oJ^nR;&pOeNvf=3J*;?&qHkM$KmhxDUmqryZ{Nus^054l2V*mgE literal 3844 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

UmY2RIYtNPVDpE$%vda+HD#3m z&VuXJ{BK&Qe+rBa7eq}Q(bq|tn(RrJAk|ztj2(i{d>nmQnM?;HF2k&9sA6up5tmjl z7lySlzMbifH17-m-Lwa_F&e7nOH?ESi3#ckR3tsM+jsck3`oG!uMS}|eAwVXv>}qxwq?QY%QJ0}r@^;fhuUA9W z*BVl>TGo&N004@xSiwDUXUvp51sVmqO3m)=B55aPwf@0=e}cN+$-BdKxY`YrT_4)0 z_d10#i44Q*rFr8MC>*)v$EJvz``(pb{e&*6k+b zsMz%($|1+8hn8c2?P(l@;Rb&CsZeYoCI3?2!LqjbwPXW3z4G$Qfj=cT5Yb%vY0(AX oeb?AaKtwrnc|$|zzw9vfvn^aJJ!zd)XFXqqy0000001=f@-~a#s diff --git a/app/src/main/res/values/ic_launcher_background.xml b/app/src/main/res/values/ic_launcher_background.xml new file mode 100644 index 0000000..beab31f --- /dev/null +++ b/app/src/main/res/values/ic_launcher_background.xml @@ -0,0 +1,4 @@ + + + #000000 + \ No newline at end of file From f8ec89933501f962492bb8c674b26d1a44ad6bd2 Mon Sep 17 00:00:00 2001 From: Nacho Date: Sat, 9 Aug 2025 12:59:01 +0200 Subject: [PATCH 10/10] Version up and credits --- app/build.gradle.kts | 2 +- .../nacabaro/vbhelper/screens/settingsScreen/CreditsScreen.kt | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index ed934d9..e1499af 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -15,7 +15,7 @@ android { minSdk = 28 targetSdk = 35 versionCode = 1 - versionName = "Alpha 0.5" + versionName = "Alpha 0.5.1" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/settingsScreen/CreditsScreen.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/settingsScreen/CreditsScreen.kt index c57a0a1..1b8f41a 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/settingsScreen/CreditsScreen.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/settingsScreen/CreditsScreen.kt @@ -35,6 +35,7 @@ fun CreditsScreen( SettingsEntry(title = "cfogrady", description = "Developed vb-lib-nfc and part of 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 = "shvstrz", description = "Designing the app icon in SVG.") { } } } } \ No newline at end of file