diff --git a/app/src/main/assets/items.db b/app/src/main/assets/items.db index 8ab0d21..80a883b 100644 Binary files a/app/src/main/assets/items.db and b/app/src/main/assets/items.db differ 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 039b322..e39db36 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/MainActivity.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/MainActivity.kt @@ -9,9 +9,12 @@ import androidx.compose.runtime.Composable import com.github.nacabaro.vbhelper.navigation.AppNavigation import com.github.nacabaro.vbhelper.di.VBHelper import com.github.nacabaro.vbhelper.navigation.AppNavigationHandlers +import com.github.nacabaro.vbhelper.screens.homeScreens.HomeScreenControllerImpl import com.github.nacabaro.vbhelper.screens.itemsScreen.ItemsScreenControllerImpl 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.storageScreen.StorageScreenControllerImpl import com.github.nacabaro.vbhelper.ui.theme.VBHelperTheme @@ -39,6 +42,9 @@ class MainActivity : ComponentActivity() { ) val settingsScreenController = SettingsScreenControllerImpl(this) val itemsScreenController = ItemsScreenControllerImpl(this) + val adventureScreenController = AdventureScreenControllerImpl(this) + val storageScreenController = StorageScreenControllerImpl(this) + val homeScreenController = HomeScreenControllerImpl(this) super.onCreate(savedInstanceState) @@ -49,7 +55,10 @@ class MainActivity : ComponentActivity() { MainApplication( scanScreenController = scanScreenController, settingsScreenController = settingsScreenController, - itemsScreenController = itemsScreenController + itemsScreenController = itemsScreenController, + adventureScreenController = adventureScreenController, + homeScreenController = homeScreenController, + storageScreenController = storageScreenController ) } } @@ -77,13 +86,20 @@ class MainActivity : ComponentActivity() { private fun MainApplication( scanScreenController: ScanScreenControllerImpl, settingsScreenController: SettingsScreenControllerImpl, - itemsScreenController: ItemsScreenControllerImpl - ) { + itemsScreenController: ItemsScreenControllerImpl, + adventureScreenController: AdventureScreenControllerImpl, + storageScreenController: StorageScreenControllerImpl, + homeScreenController: HomeScreenControllerImpl, + + ) { AppNavigation( applicationNavigationHandlers = AppNavigationHandlers( settingsScreenController, scanScreenController, - itemsScreenController + itemsScreenController, + adventureScreenController, + storageScreenController, + homeScreenController ) ) } 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 d305586..114e946 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 @@ -35,6 +35,7 @@ fun CharacterEntry( icon: BitmapData, modifier: Modifier = Modifier, obscure: Boolean = false, + disabled: Boolean = false, shape: Shape = MaterialTheme.shapes.medium, multiplier: Int = 4, onClick: () -> Unit = { } @@ -48,7 +49,10 @@ fun CharacterEntry( Card( shape = shape, - onClick = onClick, + onClick = when (disabled) { + true -> { {} } + false -> onClick + }, modifier = modifier .aspectRatio(1f) .padding(8.dp) 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 9dfca9d..b184cee 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 @@ -23,7 +23,8 @@ fun TopBanner( modifier: Modifier = Modifier, onGearClick: (() -> Unit)? = null, onBackClick: (() -> Unit)? = null, - onScanClick: (() -> Unit)? = null + onScanClick: (() -> Unit)? = null, + onAdventureClick: (() -> Unit)? = null ) { Box( // Use Box to overlay elements modifier = modifier @@ -49,7 +50,18 @@ fun TopBanner( contentDescription = "Settings" ) } - } + } else if (onAdventureClick != null) { + IconButton( + onClick = onAdventureClick, + modifier = Modifier + .align(Alignment.CenterEnd) // Place gear icon at the end + ) { + Icon( + painter = painterResource(R.drawable.baseline_fort_24), // Use a gear icon + contentDescription = "Adventure" + ) + } + } if (onScanClick != null) { IconButton( diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/daos/AdventureDao.kt b/app/src/main/java/com/github/nacabaro/vbhelper/daos/AdventureDao.kt new file mode 100644 index 0000000..f211e9f --- /dev/null +++ b/app/src/main/java/com/github/nacabaro/vbhelper/daos/AdventureDao.kt @@ -0,0 +1,41 @@ +package com.github.nacabaro.vbhelper.daos + +import androidx.room.Dao +import androidx.room.Query +import com.github.nacabaro.vbhelper.dtos.CharacterDtos + + +@Dao +interface AdventureDao { + @Query(""" + INSERT INTO Adventure (characterId, finishesAdventure) + VALUES (:characterId, strftime('%s', 'now') + :timeInSeconds) + """) + fun insertNewAdventure(characterId: Long, timeInSeconds: Long) + + @Query(""" + SELECT COUNT(*) FROM Adventure + """) + fun getAdventureCount(): Int + + @Query(""" + SELECT + uc.*, + c.sprite1 AS spriteIdle, + c.spritesWidth AS spriteWidth, + c.spritesHeight AS spriteHeight, + d.isBEm as isBemCard, + a.finishesAdventure AS timeLeft + FROM UserCharacter uc + JOIN Character c ON uc.charId = c.id + JOIN Card d ON c.dimId = d.id + JOIN Adventure a ON uc.id = a.characterId + """) + suspend fun getAdventureCharacters(): List + + @Query(""" + DELETE FROM Adventure + WHERE characterId = :characterId + """) + suspend fun deleteAdventure(characterId: Long) +} \ 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 3aad016..a655369 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 @@ -6,40 +6,48 @@ import com.github.nacabaro.vbhelper.dtos.ItemDtos @Dao interface ItemDao { - @Query(""" - SELECT Items.*, UserItems.quantity + @Query( + """ + SELECT * FROM Items - LEFT JOIN UserItems ON Items.id = UserItems.itemId ORDER BY Items.itemIcon ASC - """) + """ + ) suspend fun getAllItems(): List - @Query(""" - SELECT Items.*, UserItems.quantity + @Query( + """ + SELECT * FROM Items - JOIN UserItems ON Items.id = UserItems.itemId - """) + WHERE quantity > 0 + """ + ) suspend fun getAllUserItems(): List - @Query(""" - SELECT Items.*, UserItems.quantity + @Query( + """ + SELECT * FROM Items - JOIN UserItems ON Items.id = UserItems.itemId - WHERE UserItems.itemId = :itemId - """) - fun getUserItem(itemId: Long): ItemDtos.ItemsWithQuantities + WHERE Items.id = :itemId + """ + ) + fun getItem(itemId: Long): ItemDtos.ItemsWithQuantities - @Query(""" - UPDATE UserItems + @Query( + """ + UPDATE Items SET quantity = quantity - 1 - WHERE itemId = :itemId - """) + WHERE id = :itemId + """ + ) fun useItem(itemId: Long) - @Query(""" - UPDATE UserItems - SET quantity = quantity - :itemAmount - WHERE itemId = :itemId - """) + @Query( + """ + UPDATE Items + SET quantity = quantity + :itemAmount + WHERE id = :itemId + """ + ) suspend fun purchaseItem(itemId: Long, itemAmount: Int) } \ 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 0dbea43..6e8e33f 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 @@ -48,14 +48,39 @@ interface UserCharacterDao { c.sprite1 AS spriteIdle, c.spritesWidth AS spriteWidth, c.spritesHeight AS spriteHeight, - d.isBEm as isBemCard + 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 c.dimId = d.id + LEFT JOIN Adventure a ON a.characterId = uc.id """ ) suspend fun getAllCharacters(): List + @Query( + """ + SELECT + uc.*, + c.sprite1 AS spriteIdle, + c.spritesWidth AS spriteWidth, + c.spritesHeight 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 c.dimId = d.id + LEFT JOIN Adventure a ON a.characterId = uc.id + WHERE uc.id = :id + """) + suspend fun getCharacterWithSprites(id: Long): CharacterDtos.CharacterWithSprites + @Query("SELECT * FROM UserCharacter WHERE id = :id") suspend fun getCharacter(id: Long): UserCharacter @@ -69,14 +94,18 @@ interface UserCharacterDao { c.sprite1 AS spriteIdle, c.spritesWidth AS spriteWidth, c.spritesHeight AS spriteHeight, - d.isBEm as isBemCard + c.name as nameSprite, + c.nameWidth as nameSpriteWidth, + c.nameHeight as nameSpriteHeight, + d.isBEm as isBemCard, + a.characterId as isInAdventure FROM UserCharacter uc JOIN Character c ON uc.charId = c.id JOIN Card d ON c.dimId = d.id + LEFT JOIN Adventure a ON a.characterId = uc.id WHERE uc.isActive = 1 LIMIT 1 - """ - ) + """) suspend fun getActiveCharacter(): CharacterDtos.CharacterWithSprites? @Query("DELETE FROM UserCharacter WHERE id = :id") 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 7bc9f56..6b5bf5f 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 @@ -2,6 +2,7 @@ package com.github.nacabaro.vbhelper.database import androidx.room.Database import androidx.room.RoomDatabase +import com.github.nacabaro.vbhelper.daos.AdventureDao import com.github.nacabaro.vbhelper.daos.CharacterDao import com.github.nacabaro.vbhelper.daos.DexDao import com.github.nacabaro.vbhelper.daos.DiMDao @@ -10,12 +11,12 @@ import com.github.nacabaro.vbhelper.daos.UserCharacterDao import com.github.nacabaro.vbhelper.domain.characters.Character import com.github.nacabaro.vbhelper.domain.characters.Card import com.github.nacabaro.vbhelper.domain.Sprites +import com.github.nacabaro.vbhelper.domain.characters.Adventure import com.github.nacabaro.vbhelper.domain.characters.Dex import com.github.nacabaro.vbhelper.domain.device_data.BECharacterData import com.github.nacabaro.vbhelper.domain.device_data.TransformationHistory import com.github.nacabaro.vbhelper.domain.device_data.UserCharacter import com.github.nacabaro.vbhelper.domain.items.Items -import com.github.nacabaro.vbhelper.domain.items.UserItems @Database( version = 1, @@ -28,7 +29,7 @@ import com.github.nacabaro.vbhelper.domain.items.UserItems TransformationHistory::class, Dex::class, Items::class, - UserItems::class + Adventure::class ] ) abstract class AppDatabase : RoomDatabase() { @@ -37,4 +38,5 @@ abstract class AppDatabase : RoomDatabase() { abstract fun userCharacterDao(): UserCharacterDao abstract fun dexDao(): DexDao abstract fun itemDao(): ItemDao + abstract fun adventureDao(): AdventureDao } \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/domain/characters/Adventure.kt b/app/src/main/java/com/github/nacabaro/vbhelper/domain/characters/Adventure.kt new file mode 100644 index 0000000..66dcfe1 --- /dev/null +++ b/app/src/main/java/com/github/nacabaro/vbhelper/domain/characters/Adventure.kt @@ -0,0 +1,21 @@ +package com.github.nacabaro.vbhelper.domain.characters + +import androidx.room.Entity +import androidx.room.ForeignKey +import androidx.room.PrimaryKey +import com.github.nacabaro.vbhelper.domain.device_data.UserCharacter + +@Entity( + foreignKeys = [ + ForeignKey( + entity = UserCharacter::class, + parentColumns = ["id"], + childColumns = ["characterId"], + onDelete = ForeignKey.CASCADE + ) + ] +) +data class Adventure( + @PrimaryKey val characterId: Long, + val finishesAdventure: Long +) 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 934d303..18dfc68 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 @@ -10,5 +10,6 @@ data class Items( val description: String, val itemIcon: Int, val itemLength: Int, - val price: Int + val price: Int, + val quantity: Int ) diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/domain/items/UserItems.kt b/app/src/main/java/com/github/nacabaro/vbhelper/domain/items/UserItems.kt deleted file mode 100644 index 5c98774..0000000 --- a/app/src/main/java/com/github/nacabaro/vbhelper/domain/items/UserItems.kt +++ /dev/null @@ -1,20 +0,0 @@ -package com.github.nacabaro.vbhelper.domain.items - -import androidx.room.Entity -import androidx.room.ForeignKey -import androidx.room.PrimaryKey - -@Entity( - foreignKeys = [ - ForeignKey( - entity = Items::class, - parentColumns = ["id"], - childColumns = ["itemId"], - onDelete = ForeignKey.CASCADE - ) - ] -) -data class UserItems( - @PrimaryKey val itemId: Long, - val quantity: Int, -) diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/dtos/CharacterDtos.kt b/app/src/main/java/com/github/nacabaro/vbhelper/dtos/CharacterDtos.kt index 1c91f6d..f54b299 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/dtos/CharacterDtos.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/dtos/CharacterDtos.kt @@ -27,7 +27,11 @@ object CharacterDtos { val spriteIdle: ByteArray, val spriteWidth: Int, val spriteHeight: Int, - val isBemCard: Boolean + val nameSprite: ByteArray, + val nameSpriteWidth: Int, + val nameSpriteHeight: Int, + val isBemCard: Boolean, + val isInAdventure: Boolean ) data class DiMInfo( @@ -51,4 +55,30 @@ object CharacterDtos { val spriteHeight: Int, val discoveredOn: Long? ) + + data class AdventureCharacterWithSprites( + var id: Long = 0, + var charId: Long, + var stage: Int, + var attribute: NfcCharacter.Attribute, + var ageInDays: Int, + var nextAdventureMissionStage: Int, // next adventure mission stage on the character's dim + var mood: Int, + var vitalPoints: Int, + var transformationCountdown: Int, + var injuryStatus: NfcCharacter.InjuryStatus, + var trophies: Int, + var currentPhaseBattlesWon: Int, + var currentPhaseBattlesLost: Int, + var totalBattlesWon: Int, + var totalBattlesLost: Int, + var activityLevel: Int, + var heartRateCurrent: Int, + var characterType: DeviceType, + val spriteIdle: ByteArray, + val spriteWidth: Int, + val spriteHeight: Int, + val isBemCard: Boolean, + val timeLeft: Long + ) } \ No newline at end of file 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 8e296e8..e2e7df6 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 @@ -2,7 +2,7 @@ package com.github.nacabaro.vbhelper.dtos object ItemDtos { - data class ItemsWithQuantities ( + data class ItemsWithQuantities( val id: Long, val name: String, val description: String, @@ -11,4 +11,13 @@ object ItemDtos { val price: Int, val quantity: Int, ) + + data class PurchasedItem( + val itemId: Long, + val itemName: String, + val itemDescription: String, + val itemIcon: Int, + val itemLength: Int, + val itemAmount: Int + ) } \ 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 541a80c..35b13d4 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 @@ -16,15 +16,22 @@ import com.github.nacabaro.vbhelper.screens.scanScreen.ScanScreen import com.github.nacabaro.vbhelper.screens.scanScreen.ScanScreenControllerImpl import com.github.nacabaro.vbhelper.screens.settingsScreen.SettingsScreen import com.github.nacabaro.vbhelper.screens.SpriteViewer -import com.github.nacabaro.vbhelper.screens.StorageScreen +import com.github.nacabaro.vbhelper.screens.homeScreens.HomeScreenControllerImpl +import com.github.nacabaro.vbhelper.screens.storageScreen.StorageScreen import com.github.nacabaro.vbhelper.screens.itemsScreen.ChooseCharacterScreen import com.github.nacabaro.vbhelper.screens.itemsScreen.ItemsScreenControllerImpl 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.storageScreen.StorageScreenControllerImpl data class AppNavigationHandlers( val settingsScreenController: SettingsScreenControllerImpl, val scanScreenController: ScanScreenControllerImpl, - val itemsScreenController: ItemsScreenControllerImpl + val itemsScreenController: ItemsScreenControllerImpl, + val adventureScreenController: AdventureScreenControllerImpl, + val storageScreenController: StorageScreenControllerImpl, + val homeScreenController: HomeScreenControllerImpl ) @Composable @@ -49,12 +56,15 @@ fun AppNavigation( } composable(NavigationItems.Home.route) { HomeScreen( - navController = navController + navController = navController, + homeScreenController = applicationNavigationHandlers.homeScreenController ) } composable(NavigationItems.Storage.route) { StorageScreen( - navController = navController + navController = navController, + adventureScreenController = applicationNavigationHandlers.adventureScreenController, + storageScreenController = applicationNavigationHandlers.storageScreenController ) } composable(NavigationItems.Scan.route) { @@ -108,6 +118,13 @@ fun AppNavigation( ) } } + composable(NavigationItems.Adventure.route) { + AdventureScreen( + navController = navController, + storageScreenController = applicationNavigationHandlers + .adventureScreenController + ) + } } } } diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/navigation/NavigationItems.kt b/app/src/main/java/com/github/nacabaro/vbhelper/navigation/NavigationItems.kt index 45c654e..59140f1 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/navigation/NavigationItems.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/navigation/NavigationItems.kt @@ -19,4 +19,5 @@ sealed class NavigationItems ( object MyItems : NavigationItems("MyItems", R.drawable.baseline_data_24, "My items") object ItemsStore : NavigationItems("ItemsStore", R.drawable.baseline_data_24, "Items store") object ApplyItem : NavigationItems("ApplyItem/{itemId}", R.drawable.baseline_data_24, "Apply item") + object Adventure : NavigationItems("Adventure", R.drawable.baseline_fort_24, "Adventure") } \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/StorageScreen.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/StorageScreen.kt deleted file mode 100644 index 89a6e52..0000000 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/StorageScreen.kt +++ /dev/null @@ -1,194 +0,0 @@ -package com.github.nacabaro.vbhelper.screens - -import androidx.compose.foundation.gestures.Orientation -import androidx.compose.foundation.gestures.scrollable -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.lazy.grid.GridCells -import androidx.compose.foundation.lazy.grid.LazyVerticalGrid -import androidx.compose.foundation.lazy.grid.items -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.foundation.verticalScroll -import androidx.compose.material3.Button -import androidx.compose.material3.Card -import androidx.compose.material3.Scaffold -import androidx.compose.material3.Text -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.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.unit.dp -import androidx.compose.ui.window.Dialog -import androidx.compose.ui.window.DialogProperties -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.device_data.UserCharacter -import com.github.nacabaro.vbhelper.dtos.CharacterDtos -import com.github.nacabaro.vbhelper.navigation.NavigationItems -import com.github.nacabaro.vbhelper.source.StorageRepository -import com.github.nacabaro.vbhelper.utils.BitmapData -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext - - -@Composable -fun StorageScreen( - navController: NavController -) { - val coroutineScope = rememberCoroutineScope() - val application = LocalContext.current.applicationContext as VBHelper - val storageRepository = StorageRepository(application.container.db) - val monList = remember { mutableStateOf>(emptyList()) } - - var selectedCharacter by remember { mutableStateOf(null) } - - LaunchedEffect(storageRepository) { - coroutineScope.launch { - val characterList = storageRepository.getAllCharacters() - monList.value = characterList - } - } - - Scaffold ( - topBar = { TopBanner(text = "My characters") } - ) { contentPadding -> - if (monList.value.isEmpty()) { - Column ( - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.Center, - modifier = Modifier - .padding(top = contentPadding.calculateTopPadding()) - .fillMaxSize() - ) { - Text( - text = "Nothing to see here", - textAlign = TextAlign.Center, - modifier = Modifier - ) - } - } - - LazyVerticalGrid( - columns = GridCells.Fixed(3), - modifier = Modifier - .scrollable(state = rememberScrollState(), orientation = Orientation.Vertical) - .padding(top = contentPadding.calculateTopPadding()) - ) { - items(monList.value) { index -> - CharacterEntry( - icon = BitmapData( - bitmap = index.spriteIdle, - width = index.spriteWidth, - height = index.spriteHeight - ), - onClick = { - selectedCharacter = index.id - } - ) - } - } - - if (selectedCharacter != null) { - StorageDialog( - characterId = selectedCharacter!!, - onDismissRequest = { selectedCharacter = null }, - onClickSetActive = { - coroutineScope.launch { - withContext(Dispatchers.IO) { - storageRepository.setActiveCharacter(selectedCharacter!!) - selectedCharacter = null - } - navController.navigate(NavigationItems.Home.route) - } - }, - onSendToBracelet = { - navController.navigate( - NavigationItems.Scan.route.replace( - "{characterId}", - selectedCharacter.toString() - ) - ) - } - ) - } - } -} - -@Composable -fun StorageDialog( - characterId: Long, - onDismissRequest: () -> Unit, - onSendToBracelet: () -> Unit, - onClickSetActive: () -> Unit -) { - val coroutineScope = rememberCoroutineScope() - val application = LocalContext.current.applicationContext as VBHelper - val storageRepository = StorageRepository(application.container.db) - val character = remember { mutableStateOf(null) } - - LaunchedEffect(storageRepository) { - coroutineScope.launch { - character.value = storageRepository.getSingleCharacter(characterId) - } - } - - Dialog( - onDismissRequest = onDismissRequest, - properties = DialogProperties( - dismissOnBackPress = true, - dismissOnClickOutside = true - ) - ) { - Card( - shape = RoundedCornerShape(16.dp) - ) { - Column ( - modifier = Modifier - .padding(16.dp) - ) { - if (character.value != null) { - Text( - text = character.value?.toString() ?: "Loading...", - textAlign = TextAlign.Center, - modifier = Modifier - .padding(8.dp) - ) - } - Row ( - modifier = Modifier - .verticalScroll(rememberScrollState()) - ) { - Button( - onClick = onSendToBracelet - ) { - Text(text = "Send to bracelet") - } - Button( - onClick = onClickSetActive - ) { - Text(text = "Set active") - } - Button( - onClick = onDismissRequest - ) { - Text(text = "Close") - } - } - } - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/adventureScreen/AdventureEntry.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/adventureScreen/AdventureEntry.kt new file mode 100644 index 0000000..abab3bf --- /dev/null +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/adventureScreen/AdventureEntry.kt @@ -0,0 +1,68 @@ +package com.github.nacabaro.vbhelper.screens.adventureScreen + +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.material3.Text +import androidx.compose.material3.Card +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +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 +import java.util.Locale + +@Composable +fun AdventureEntry( + icon: BitmapData, + timeLeft: Long, + modifier: Modifier = Modifier, + onClick: () -> Unit +) { + val bitmap = remember (icon.bitmap) { icon.getBitmap() } + val imageBitmap = remember(bitmap) { bitmap.asImageBitmap() } + val density: Float = LocalContext.current.resources.displayMetrics.density + val dpSize = (icon.width * 4 / density).dp + + Card( + onClick = onClick, + modifier = modifier + .padding(8.dp) + .fillMaxWidth() + ) { + Row( + modifier = Modifier + .padding(8.dp) + .height(96.dp) + ) { + Image( + bitmap = imageBitmap, + contentDescription = null, + filterQuality = FilterQuality.None, + modifier = Modifier + .size(dpSize) + ) + Text( + text = when { + timeLeft < 0 -> "Adventure finished" + else -> "Time left: ${formatSeconds(timeLeft)}" + } + ) + } + } +} + +fun formatSeconds(totalSeconds: Long): String { + val hours = totalSeconds / 3600 + val minutes = (totalSeconds % 3600) / 60 + val seconds = totalSeconds % 60 + + return String.format(Locale.getDefault(), "%02d:%02d:%02d", hours, minutes, seconds) +} \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/adventureScreen/AdventureScreen.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/adventureScreen/AdventureScreen.kt new file mode 100644 index 0000000..1113511 --- /dev/null +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/adventureScreen/AdventureScreen.kt @@ -0,0 +1,128 @@ +package com.github.nacabaro.vbhelper.screens.adventureScreen + +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.produceState +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.navigation.NavController +import com.github.nacabaro.vbhelper.screens.itemsScreen.ObtainedItemDialog +import com.github.nacabaro.vbhelper.components.TopBanner +import com.github.nacabaro.vbhelper.di.VBHelper +import com.github.nacabaro.vbhelper.dtos.CharacterDtos +import com.github.nacabaro.vbhelper.dtos.ItemDtos +import com.github.nacabaro.vbhelper.navigation.NavigationItems +import com.github.nacabaro.vbhelper.source.StorageRepository +import com.github.nacabaro.vbhelper.utils.BitmapData +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import java.time.Instant + +@Composable +fun AdventureScreen( + navController: NavController, + storageScreenController: AdventureScreenControllerImpl +) { + val coroutineScope = rememberCoroutineScope() + val application = LocalContext.current.applicationContext as VBHelper + val database = application.container.db + val storageRepository = StorageRepository(database) + val characterList = remember { + mutableStateOf>(emptyList()) + } + var obtainedItem by remember { + mutableStateOf(null) + } + + val currentTime by produceState(initialValue = Instant.now().epochSecond) { + while (true) { + value = Instant.now().epochSecond + delay(1000) + } + } + + var cancelAdventureDialog by remember { + mutableStateOf(null) + } + + LaunchedEffect(storageRepository) { + coroutineScope.launch { + characterList.value = storageRepository + .getAdventureCharacters() + } + } + + Scaffold( + topBar = { + TopBanner( + text = "Adventure", + onBackClick = { + navController.popBackStack() + } + ) + } + ) { contentPadding -> + LazyColumn( + modifier = Modifier + .padding(top = contentPadding.calculateTopPadding()) + ) { + items(characterList.value) { + AdventureEntry( + icon = BitmapData( + bitmap = it.spriteIdle, + width = it.spriteWidth, + height = it.spriteHeight + ), + timeLeft = it.timeLeft - currentTime, + onClick = { + if (it.timeLeft < currentTime) { + storageScreenController + .getItemFromAdventure(it.id) { adventureResult -> + obtainedItem = adventureResult + } + } else { + cancelAdventureDialog = it + } + } + ) + } + } + } + + if (obtainedItem != null) { + ObtainedItemDialog( + obtainedItem = obtainedItem!!, + onClickDismiss = { + obtainedItem = null + } + ) + } + + if (cancelAdventureDialog != null) { + CancelAdventureDialog( + characterSprite = BitmapData( + bitmap = cancelAdventureDialog!!.spriteIdle, + width = cancelAdventureDialog!!.spriteWidth, + height = cancelAdventureDialog!!.spriteHeight + ), + onDismissRequest = { + cancelAdventureDialog = null + }, + onClickConfirm = { + storageScreenController.cancelAdventure(cancelAdventureDialog!!.id) { + navController.navigate(NavigationItems.Storage.route) + } + cancelAdventureDialog = null + } + ) + } +} diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/adventureScreen/AdventureScreenController.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/adventureScreen/AdventureScreenController.kt new file mode 100644 index 0000000..11d70f6 --- /dev/null +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/adventureScreen/AdventureScreenController.kt @@ -0,0 +1,9 @@ +package com.github.nacabaro.vbhelper.screens.adventureScreen + +import com.github.nacabaro.vbhelper.dtos.ItemDtos + +interface AdventureScreenController { + fun sendCharacterToAdventure(characterId: Long, timeInMinutes: Long) + fun getItemFromAdventure(characterId: Long, onResult: (ItemDtos.PurchasedItem) -> Unit) + fun cancelAdventure(characterId: Long, onResult: () -> Unit) +} \ 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 new file mode 100644 index 0000000..1f77db6 --- /dev/null +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/adventureScreen/AdventureScreenControllerImpl.kt @@ -0,0 +1,100 @@ +package com.github.nacabaro.vbhelper.screens.adventureScreen + +import android.widget.Toast +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.Dispatchers +import kotlinx.coroutines.launch +import kotlin.math.roundToInt +import kotlin.random.Random + +class AdventureScreenControllerImpl( + private val componentActivity: ComponentActivity, +) : AdventureScreenController { + private val application = componentActivity.applicationContext as VBHelper + private val database = application.container.db + + override fun sendCharacterToAdventure(characterId: Long, timeInMinutes: Long) { + val timeInSeconds = timeInMinutes * 60 + componentActivity.lifecycleScope.launch(Dispatchers.IO) { + val characterData = database + .userCharacterDao() + .getCharacter(characterId) + + if (characterData.isActive) { + database + .userCharacterDao() + .clearActiveCharacter() + } + + database + .adventureDao() + .insertNewAdventure(characterId, timeInSeconds) + } + } + + override fun getItemFromAdventure( + characterId: Long, + onResult: (ItemDtos.PurchasedItem) -> Unit + ) { + componentActivity.lifecycleScope.launch(Dispatchers.IO) { + database + .adventureDao() + .deleteAdventure(characterId) + + val generatedItem = generateItem(characterId) + + onResult(generatedItem) + } + } + + override fun cancelAdventure(characterId: Long, onResult: () -> Unit) { + componentActivity.lifecycleScope.launch(Dispatchers.IO) { + database + .adventureDao() + .deleteAdventure(characterId) + + componentActivity + .runOnUiThread { + Toast.makeText( + componentActivity, + "Adventure canceled", + Toast.LENGTH_SHORT + ).show() + onResult() + } + + } + } + + private suspend fun generateItem(characterId: Long): ItemDtos.PurchasedItem { + val character = database + .userCharacterDao() + .getCharacter(characterId) + + val randomItem = database + .itemDao() + .getAllItems() + .random() + + val random = ((Random.nextFloat() * character.stage) + 3).roundToInt() + + database + .itemDao() + .purchaseItem( + itemId = randomItem.id, + itemAmount = random + ) + + return ItemDtos.PurchasedItem( + itemId = randomItem.id, + itemAmount = random, + itemName = randomItem.name, + itemIcon = randomItem.itemIcon, + itemLength = randomItem.itemLength, + itemDescription = randomItem.description + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/adventureScreen/CancelAdventureDialog.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/adventureScreen/CancelAdventureDialog.kt new file mode 100644 index 0000000..350ef5e --- /dev/null +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/adventureScreen/CancelAdventureDialog.kt @@ -0,0 +1,73 @@ +package com.github.nacabaro.vbhelper.screens.adventureScreen + +import androidx.compose.foundation.Image +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.foundation.layout.size +import androidx.compose.material3.Button +import androidx.compose.material3.Card +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +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 androidx.compose.ui.window.Dialog +import com.github.nacabaro.vbhelper.utils.BitmapData +import com.github.nacabaro.vbhelper.utils.getBitmap + +@Composable +fun CancelAdventureDialog( + characterSprite: BitmapData, + onDismissRequest: () -> Unit, + onClickConfirm: () -> Unit +) { + val bitmap = remember (characterSprite) { characterSprite.getBitmap() } + val imageBitmap = remember(bitmap) { bitmap.asImageBitmap() } + val density: Float = LocalContext.current.resources.displayMetrics.density + val dpSize = (characterSprite.width * 4 / density).dp + + Dialog( + onDismissRequest = onDismissRequest + ) { + Card { + Column( + modifier = Modifier + .padding(16.dp) + ) { + Row { + Image( + bitmap = imageBitmap, + contentDescription = null, + filterQuality = FilterQuality.None, + modifier = Modifier + .size(dpSize) + ) + Text( + text = "Are you sure you want to cancel this character's adventure?" + ) + } + Row( + modifier = Modifier + .padding(8.dp) + ) { + Button( + onClick = onClickConfirm + ) { + Text(text = "Confirm") + } + Spacer(modifier = Modifier.padding(4.dp)) + Button( + onClick = onDismissRequest + ) { + Text(text = "Cancel") + } + } + } + } + } +} \ 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/BEBEmHomeScreen.kt index 9b58058..344ecc5 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/BEBEmHomeScreen.kt @@ -15,7 +15,7 @@ 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.components.getIconResource +import com.github.nacabaro.vbhelper.screens.itemsScreen.getIconResource import com.github.nacabaro.vbhelper.domain.device_data.BECharacterData import com.github.nacabaro.vbhelper.dtos.CharacterDtos import com.github.nacabaro.vbhelper.screens.itemsScreen.ItemsScreenControllerImpl 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/BEDiMHomeScreen.kt index 9b4ea06..05b250b 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/BEDiMHomeScreen.kt @@ -16,7 +16,7 @@ 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.components.getIconResource +import com.github.nacabaro.vbhelper.screens.itemsScreen.getIconResource import com.github.nacabaro.vbhelper.domain.device_data.BECharacterData import com.github.nacabaro.vbhelper.dtos.CharacterDtos import com.github.nacabaro.vbhelper.screens.itemsScreen.ItemsScreenControllerImpl 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 2ad2a19..60eee9b 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 @@ -4,15 +4,22 @@ import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Button +import androidx.compose.material3.Card import androidx.compose.material3.Scaffold import androidx.compose.material3.Text 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.saveable.rememberSaveable +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.unit.dp +import androidx.compose.ui.window.Dialog import androidx.navigation.NavController import com.github.nacabaro.vbhelper.components.TopBanner import com.github.nacabaro.vbhelper.di.VBHelper @@ -27,7 +34,8 @@ import kotlinx.coroutines.withContext @Composable fun HomeScreen( - navController: NavController + navController: NavController, + homeScreenController: HomeScreenControllerImpl ) { val application = LocalContext.current.applicationContext as VBHelper val storageRepository = StorageRepository(application.container.db) @@ -35,6 +43,7 @@ fun HomeScreen( val transformationHistory = remember { mutableStateOf?>(null) } val beData = remember { mutableStateOf(null) } val vbData = remember { mutableStateOf(null) } + var adventureMissionsFinished by rememberSaveable { mutableStateOf(false) } LaunchedEffect(storageRepository, activeMon) { withContext(Dispatchers.IO) { @@ -46,6 +55,13 @@ fun HomeScreen( } } + LaunchedEffect(true) { + homeScreenController + .didAdventureMissionsFinish { + adventureMissionsFinished = it + } + } + Scaffold ( topBar = { TopBanner( @@ -94,6 +110,26 @@ fun HomeScreen( } } } + + if (adventureMissionsFinished) { + Dialog( + onDismissRequest = { adventureMissionsFinished = false }, + ) { + Card { + Column( + modifier = Modifier + .padding(16.dp) + ) { + Text(text = "One of your characters has finished their adventure mission!") + Button(onClick = { + adventureMissionsFinished = false + }) { + Text(text = "Dismiss") + } + } + } + } + } } 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 new file mode 100644 index 0000000..b524ba2 --- /dev/null +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/HomeScreenController.kt @@ -0,0 +1,5 @@ +package com.github.nacabaro.vbhelper.screens.homeScreens + +interface HomeScreenController { + fun didAdventureMissionsFinish(onCompletion: (Boolean) -> 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 new file mode 100644 index 0000000..fe29f8e --- /dev/null +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/HomeScreenControllerImpl.kt @@ -0,0 +1,29 @@ +package com.github.nacabaro.vbhelper.screens.homeScreens + +import androidx.activity.ComponentActivity +import androidx.lifecycle.lifecycleScope +import com.github.nacabaro.vbhelper.di.VBHelper +import kotlinx.coroutines.launch +import java.time.Instant + +class HomeScreenControllerImpl( + private val componentActivity: ComponentActivity, +): HomeScreenController { + private val application = componentActivity.applicationContext as VBHelper + private val database = application.container.db + + override fun didAdventureMissionsFinish(onCompletion: (Boolean) -> Unit) { + componentActivity.lifecycleScope.launch { + val currentTime = Instant.now().epochSecond + val adventureCharacters = database + .adventureDao() + .getAdventureCharacters() + + val finishedAdventureCharacters = adventureCharacters.filter { character -> + character.timeLeft <= currentTime + } + + onCompletion(finishedAdventureCharacters.isNotEmpty()) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/components/ItemElement.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/itemsScreen/ItemElement.kt similarity index 98% rename from app/src/main/java/com/github/nacabaro/vbhelper/components/ItemElement.kt rename to app/src/main/java/com/github/nacabaro/vbhelper/screens/itemsScreen/ItemElement.kt index 6493ec6..5956bf3 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/components/ItemElement.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/itemsScreen/ItemElement.kt @@ -1,4 +1,4 @@ -package com.github.nacabaro.vbhelper.components +package com.github.nacabaro.vbhelper.screens.itemsScreen import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box @@ -25,7 +25,6 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.DialogProperties import com.github.nacabaro.vbhelper.R -import com.github.nacabaro.vbhelper.screens.itemsScreen.ItemsScreenControllerImpl import com.github.nacabaro.vbhelper.ui.theme.VBHelperTheme @Composable 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 c604236..f1a6716 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 @@ -107,7 +107,7 @@ class ItemsScreenControllerImpl ( private fun getItem(itemId: Long): ItemDtos.ItemsWithQuantities { return database .itemDao() - .getUserItem(itemId) + .getItem(itemId) } private fun consumeItem(itemId: Long) { diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/itemsScreen/ItemsStore.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/itemsScreen/ItemsStore.kt index 771838c..032303b 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/itemsScreen/ItemsStore.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/itemsScreen/ItemsStore.kt @@ -15,10 +15,6 @@ 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.components.ItemDialog -import com.github.nacabaro.vbhelper.components.ItemElement -import com.github.nacabaro.vbhelper.components.getIconResource -import com.github.nacabaro.vbhelper.components.getLengthResource import com.github.nacabaro.vbhelper.di.VBHelper import com.github.nacabaro.vbhelper.dtos.ItemDtos import com.github.nacabaro.vbhelper.source.ItemsRepository diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/itemsScreen/MyItems.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/itemsScreen/MyItems.kt index 098c2fd..55b835f 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/itemsScreen/MyItems.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/itemsScreen/MyItems.kt @@ -19,10 +19,6 @@ 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.components.ItemDialog -import com.github.nacabaro.vbhelper.components.ItemElement -import com.github.nacabaro.vbhelper.components.getIconResource -import com.github.nacabaro.vbhelper.components.getLengthResource import com.github.nacabaro.vbhelper.di.VBHelper import com.github.nacabaro.vbhelper.dtos.ItemDtos import com.github.nacabaro.vbhelper.navigation.NavigationItems diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/itemsScreen/ObtainedItemDialog.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/itemsScreen/ObtainedItemDialog.kt new file mode 100644 index 0000000..420c11e --- /dev/null +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/itemsScreen/ObtainedItemDialog.kt @@ -0,0 +1,89 @@ +package com.github.nacabaro.vbhelper.screens.itemsScreen + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.material3.Card +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.window.Dialog +import com.github.nacabaro.vbhelper.dtos.ItemDtos + +@Composable +fun ObtainedItemDialog( + obtainedItem: ItemDtos.PurchasedItem, + onClickDismiss: () -> Unit +) { + Dialog( + onDismissRequest = onClickDismiss + ) { + Card { + Column( + modifier = Modifier + .padding(16.dp) + ) { + Column ( + modifier = Modifier + .padding(16.dp) + ) { + Row { + Box(modifier = Modifier) { + Icon( + painter = painterResource(id = getIconResource(obtainedItem.itemIcon)), + contentDescription = null, + modifier = Modifier + .size(96.dp) + .align(Alignment.Center) + ) + Icon( + painter = painterResource(id = getLengthResource(obtainedItem.itemLength)), + contentDescription = null, + tint = MaterialTheme.colorScheme.outline, + modifier = Modifier + .size(64.dp) + .align(Alignment.BottomEnd) + ) + } + Column ( + modifier = Modifier + .padding(16.dp) + ) { + Text( + fontSize = MaterialTheme.typography.titleLarge.fontSize, + text = obtainedItem.itemName, + modifier = Modifier + .fillMaxWidth() + ) + } + } + Text( + textAlign = TextAlign.Center, + fontSize = MaterialTheme.typography.bodyMedium.fontSize, + fontFamily = MaterialTheme.typography.bodyMedium.fontFamily, + text = obtainedItem.itemDescription + ) + Text( + textAlign = TextAlign.Center, + fontSize = MaterialTheme.typography.bodySmall.fontSize, + fontFamily = MaterialTheme.typography.bodySmall.fontFamily, + text = "You have obtained ${obtainedItem.itemAmount} of this item", + modifier = Modifier + .fillMaxWidth() + .padding(16.dp) + ) + } + } + } + } + +} \ No newline at end of file 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 94be077..a9c87cc 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 @@ -131,7 +131,7 @@ class SettingsScreenControllerImpl( val characters = card.characterStats.characterEntries var spriteCounter = when (card is BemCard) { - true -> 55 + true -> 54 false -> 10 } diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/storageScreen/StorageAdventureTimeDialog.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/storageScreen/StorageAdventureTimeDialog.kt new file mode 100644 index 0000000..cb14cf7 --- /dev/null +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/storageScreen/StorageAdventureTimeDialog.kt @@ -0,0 +1,107 @@ +package com.github.nacabaro.vbhelper.screens.storageScreen + +import androidx.compose.foundation.clickable +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.padding +import androidx.compose.foundation.layout.width +import androidx.compose.material3.Button +import androidx.compose.material3.Card +import androidx.compose.material3.DropdownMenu +import androidx.compose.material3.DropdownMenuItem +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.unit.dp +import androidx.compose.ui.window.Dialog +import com.github.nacabaro.vbhelper.R + +fun getAdventureTime(time: Int): String { + return when (time) { + 360 -> "6 hours" + 720 -> "12 hours" + 1440 -> "24 hours" + else -> "Unknown" + } +} + +@Composable +fun StorageAdventureTimeDialog( + onClickSendToAdventure: (time: Long) -> Unit, + onDismissRequest: () -> Unit +) { + val times = arrayOf(360, 720, 1440) + var expanded by remember { mutableStateOf(false) } + var itemPosition by remember { mutableIntStateOf(-1) } + + Dialog( + onDismissRequest = onDismissRequest + ) { + Card { + Column( + modifier = Modifier + .padding(16.dp) + ) { + Box( + modifier = Modifier + .padding(16.dp) + ) { + Row ( + horizontalArrangement = Arrangement.SpaceBetween, + modifier = Modifier + .width(256.dp) + .clickable(true) { + expanded = true + } + ) { + Text( + text = when (itemPosition) { + -1 -> "Choose time" + else -> getAdventureTime(times[itemPosition]) + } + ) + Icon( + painter = painterResource(R.drawable.baseline_single_arrow_down), + contentDescription = "Show more" + ) + } + DropdownMenu( + expanded = expanded, + onDismissRequest = { expanded = false }, + modifier = Modifier + .width(256.dp) + ) { + times.forEach { time -> + DropdownMenuItem( + text = { Text(getAdventureTime(time)) }, + onClick = { + itemPosition = times.indexOf(time) + expanded = false + } + ) + } + } + } + Button( + onClick = { + if (itemPosition != -1) { + onClickSendToAdventure(times[itemPosition].toLong()) + onDismissRequest() + } + } + ) { + Text(text = "Send on adventure") + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/storageScreen/StorageDialog.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/storageScreen/StorageDialog.kt new file mode 100644 index 0000000..2259f47 --- /dev/null +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/storageScreen/StorageDialog.kt @@ -0,0 +1,159 @@ +package com.github.nacabaro.vbhelper.screens.storageScreen + +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Button +import androidx.compose.material3.Card +import androidx.compose.material3.Text +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.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 androidx.compose.ui.window.Dialog +import androidx.compose.ui.window.DialogProperties +import com.github.nacabaro.vbhelper.di.VBHelper +import com.github.nacabaro.vbhelper.dtos.CharacterDtos +import com.github.nacabaro.vbhelper.source.StorageRepository +import com.github.nacabaro.vbhelper.utils.BitmapData +import com.github.nacabaro.vbhelper.utils.getBitmap +import kotlinx.coroutines.launch + +@Composable +fun StorageDialog( + characterId: Long, + onDismissRequest: () -> Unit, + onSendToBracelet: () -> Unit, + onClickSetActive: () -> Unit, + onClickSendToAdventure: (time: Long) -> Unit +) { + val coroutineScope = rememberCoroutineScope() + val application = LocalContext.current.applicationContext as VBHelper + val storageRepository = StorageRepository(application.container.db) + val character = remember { mutableStateOf(null) } + val characterSprite = remember { mutableStateOf(null) } + val characterName = remember { mutableStateOf(null) } + var onSendToAdventureClicked by remember { mutableStateOf(false) } + + LaunchedEffect(storageRepository) { + coroutineScope.launch { + character.value = storageRepository.getSingleCharacter(characterId) + characterSprite.value = BitmapData( + bitmap = character.value!!.spriteIdle, + width = character.value!!.spriteWidth, + height = character.value!!.spriteHeight + ) + characterName.value = BitmapData( + bitmap = character.value!!.nameSprite, + width = character.value!!.nameSpriteWidth, + height = character.value!!.nameSpriteHeight + ) + } + } + + Dialog( + onDismissRequest = onDismissRequest, + properties = DialogProperties( + dismissOnBackPress = true, + dismissOnClickOutside = true + ) + ) { + Card( + shape = RoundedCornerShape(16.dp) + ) { + Column ( + modifier = Modifier + .padding(16.dp) + ) { + if (character.value != null && + characterSprite.value != null && + characterName.value != null + ) { + Row( + verticalAlignment = Alignment.CenterVertically, + ) { + val bitmap = remember (characterSprite.value!!) { characterSprite.value!!.getBitmap() } + val imageBitmap = remember(bitmap) { bitmap.asImageBitmap() } + val density: Float = LocalContext.current.resources.displayMetrics.density + val dpSize = (characterSprite.value!!.width * 4 / density).dp + Image( + bitmap = imageBitmap, + contentDescription = "Character image", + filterQuality = FilterQuality.None, + modifier = Modifier + .size(dpSize) + ) + val nameBitmap = remember (characterName.value!!) { characterName.value!!.getBitmap() } + val nameImageBitmap = remember(nameBitmap) { nameBitmap.asImageBitmap() } + val nameDpSize = (characterName.value!!.width * 4 / density).dp + Image( + bitmap = nameImageBitmap, + contentDescription = "Character image", + filterQuality = FilterQuality.None, + modifier = Modifier + .size(nameDpSize) + ) + } + } + Row( + horizontalArrangement = Arrangement.Center, + modifier = Modifier + .fillMaxWidth() + ) { + Button( + onClick = onSendToBracelet, + ) { + Text(text = "Send to bracelet") + } + Spacer( + modifier = Modifier + .padding(4.dp) + ) + Button( + onClick = onClickSetActive, + ) { + Text(text = "Set active") + } + } + Button( + onClick = { + onSendToAdventureClicked = true + }, + ) { + Text(text = "Send to adventure") + } + Button( + modifier = Modifier + .fillMaxWidth(), + onClick = onDismissRequest + ) { + Text(text = "Close") + } + } + } + } + + if (onSendToAdventureClicked) { + StorageAdventureTimeDialog( + onClickSendToAdventure = { time -> + onClickSendToAdventure(time) + }, + onDismissRequest = { onSendToAdventureClicked = false } + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/storageScreen/StorageScreen.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/storageScreen/StorageScreen.kt new file mode 100644 index 0000000..b5736bb --- /dev/null +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/storageScreen/StorageScreen.kt @@ -0,0 +1,145 @@ +package com.github.nacabaro.vbhelper.screens.storageScreen + +import android.widget.Toast +import androidx.compose.foundation.gestures.Orientation +import androidx.compose.foundation.gestures.scrollable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.grid.GridCells +import androidx.compose.foundation.lazy.grid.LazyVerticalGrid +import androidx.compose.foundation.lazy.grid.items +import androidx.compose.foundation.rememberScrollState +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +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.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.text.style.TextAlign +import androidx.navigation.NavController +import com.github.nacabaro.vbhelper.components.CharacterEntry +import com.github.nacabaro.vbhelper.components.TopBanner +import com.github.nacabaro.vbhelper.di.VBHelper +import com.github.nacabaro.vbhelper.dtos.CharacterDtos +import com.github.nacabaro.vbhelper.navigation.NavigationItems +import com.github.nacabaro.vbhelper.screens.adventureScreen.AdventureScreenControllerImpl +import com.github.nacabaro.vbhelper.source.StorageRepository +import com.github.nacabaro.vbhelper.utils.BitmapData +import kotlinx.coroutines.launch + + +@Composable +fun StorageScreen( + navController: NavController, + storageScreenController: StorageScreenControllerImpl, + adventureScreenController: AdventureScreenControllerImpl +) { + val coroutineScope = rememberCoroutineScope() + val application = LocalContext.current.applicationContext as VBHelper + val storageRepository = StorageRepository(application.container.db) + val monList = remember { mutableStateOf>(emptyList()) } + var selectedCharacter by remember { mutableStateOf(null) } + + LaunchedEffect(storageRepository, selectedCharacter) { + coroutineScope.launch { + val characterList = storageRepository.getAllCharacters() + monList.value = characterList + } + } + + Scaffold ( + topBar = { + TopBanner( + text = "My characters", + onAdventureClick = { + navController.navigate(NavigationItems.Adventure.route) + } + ) + } + ) { contentPadding -> + if (monList.value.isEmpty()) { + Column ( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center, + modifier = Modifier + .padding(top = contentPadding.calculateTopPadding()) + .fillMaxSize() + ) { + Text( + text = "Nothing to see here", + textAlign = TextAlign.Center, + modifier = Modifier + ) + } + } else { + LazyVerticalGrid( + columns = GridCells.Fixed(3), + modifier = Modifier + .scrollable(state = rememberScrollState(), orientation = Orientation.Vertical) + .padding(top = contentPadding.calculateTopPadding()) + ) { + items(monList.value) { index -> + CharacterEntry( + icon = BitmapData( + bitmap = index.spriteIdle, + width = index.spriteWidth, + height = index.spriteHeight + ), + onClick = { + if (!index.isInAdventure) { + selectedCharacter = index.id + } else { + Toast.makeText( + application, + "This character is in an adventure", + Toast.LENGTH_SHORT + ).show() + navController.navigate( + NavigationItems.Adventure.route + ) + } + }, + ) + } + } + } + + if (selectedCharacter != null) { + StorageDialog( + characterId = selectedCharacter!!, + onDismissRequest = { selectedCharacter = null }, + onClickSetActive = { + storageScreenController + .setActive(selectedCharacter!!) { + selectedCharacter = null + navController.navigate(NavigationItems.Home.route) + } + }, + onSendToBracelet = { + navController.navigate( + NavigationItems.Scan.route.replace( + "{characterId}", + selectedCharacter.toString() + ) + ) + }, + onClickSendToAdventure = { time -> + adventureScreenController + .sendCharacterToAdventure( + characterId = selectedCharacter!!, + timeInMinutes = time + ) + selectedCharacter = null + } + ) + } + } +} diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/storageScreen/StorageScreenController.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/storageScreen/StorageScreenController.kt new file mode 100644 index 0000000..b958c18 --- /dev/null +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/storageScreen/StorageScreenController.kt @@ -0,0 +1,5 @@ +package com.github.nacabaro.vbhelper.screens.storageScreen + +interface StorageScreenController { + fun setActive(characterId: Long, onCompletion: () -> Unit) +} \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/storageScreen/StorageScreenControllerImpl.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/storageScreen/StorageScreenControllerImpl.kt new file mode 100644 index 0000000..1f27b23 --- /dev/null +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/storageScreen/StorageScreenControllerImpl.kt @@ -0,0 +1,28 @@ +package com.github.nacabaro.vbhelper.screens.storageScreen + +import android.widget.Toast +import androidx.activity.ComponentActivity +import androidx.lifecycle.lifecycleScope +import com.github.nacabaro.vbhelper.di.VBHelper +import kotlinx.coroutines.launch + +class StorageScreenControllerImpl( + private val componentActivity: ComponentActivity +): StorageScreenController { + private val application = componentActivity.applicationContext as VBHelper + private val database = application.container.db + + override fun setActive(characterId: Long, onCompletion: () -> Unit) { + componentActivity.lifecycleScope.launch { + database.userCharacterDao().setActiveCharacter(characterId) + componentActivity.runOnUiThread { + Toast.makeText( + componentActivity, + "Active character updated!", + Toast.LENGTH_SHORT + ).show() + onCompletion() + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/source/StorageRepository.kt b/app/src/main/java/com/github/nacabaro/vbhelper/source/StorageRepository.kt index 46eda24..d463d06 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,6 @@ 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.TransformationHistory -import com.github.nacabaro.vbhelper.domain.device_data.UserCharacter import com.github.nacabaro.vbhelper.dtos.CharacterDtos class StorageRepository ( @@ -13,8 +11,8 @@ class StorageRepository ( return db.userCharacterDao().getAllCharacters() } - suspend fun getSingleCharacter(id: Long): UserCharacter { - return db.userCharacterDao().getCharacter(id) + suspend fun getSingleCharacter(id: Long): CharacterDtos.CharacterWithSprites { + return db.userCharacterDao().getCharacterWithSprites(id) } suspend fun getCharacterBeData(id: Long): BECharacterData { @@ -37,8 +35,7 @@ class StorageRepository ( return db.userCharacterDao().deleteCharacterById(id) } - fun setActiveCharacter(id: Long) { - db.userCharacterDao().clearActiveCharacter() - return db.userCharacterDao().setActiveCharacter(id) + suspend fun getAdventureCharacters(): List { + return db.adventureDao().getAdventureCharacters() } } \ No newline at end of file diff --git a/app/src/main/res/drawable/baseline_fort_24.xml b/app/src/main/res/drawable/baseline_fort_24.xml new file mode 100644 index 0000000..e5efa75 --- /dev/null +++ b/app/src/main/res/drawable/baseline_fort_24.xml @@ -0,0 +1,9 @@ + + +