~cytrogen/vbhelper

09394871e5bc0d5f19348b2bfcb6847795cdbc09 — nacabaro 1 year, 3 months ago bc7a05c + 972a640
Merge pull request #22 from nacabaro/database/items

Add items functionality
42 files changed, 1134 insertions(+), 69 deletions(-)

A app/src/main/assets/items.db
M app/src/main/java/com/github/nacabaro/vbhelper/MainActivity.kt
M app/src/main/java/com/github/nacabaro/vbhelper/components/CharacterEntry.kt
A app/src/main/java/com/github/nacabaro/vbhelper/components/ItemElement.kt
A app/src/main/java/com/github/nacabaro/vbhelper/daos/ItemDao.kt
M app/src/main/java/com/github/nacabaro/vbhelper/daos/UserCharacterDao.kt
M app/src/main/java/com/github/nacabaro/vbhelper/database/AppDatabase.kt
M app/src/main/java/com/github/nacabaro/vbhelper/di/DefaultAppContainer.kt
M app/src/main/java/com/github/nacabaro/vbhelper/domain/device_data/BECharacterData.kt
A app/src/main/java/com/github/nacabaro/vbhelper/domain/items/Items.kt
A app/src/main/java/com/github/nacabaro/vbhelper/domain/items/UserItems.kt
A app/src/main/java/com/github/nacabaro/vbhelper/dtos/ItemDtos.kt
M app/src/main/java/com/github/nacabaro/vbhelper/navigation/AppNavigation.kt
M app/src/main/java/com/github/nacabaro/vbhelper/navigation/NavigationItems.kt
D app/src/main/java/com/github/nacabaro/vbhelper/screens/ItemsScreen.kt
M app/src/main/java/com/github/nacabaro/vbhelper/screens/StorageScreen.kt
M app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/BEBEmHomeScreen.kt
M app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/BEDiMHomeScreen.kt
A app/src/main/java/com/github/nacabaro/vbhelper/screens/itemsScreen/ChooseCharacterScreen.kt
A app/src/main/java/com/github/nacabaro/vbhelper/screens/itemsScreen/ItemsScreen.kt
A app/src/main/java/com/github/nacabaro/vbhelper/screens/itemsScreen/ItemsScreenController.kt
A app/src/main/java/com/github/nacabaro/vbhelper/screens/itemsScreen/ItemsScreenControllerImpl.kt
A app/src/main/java/com/github/nacabaro/vbhelper/screens/itemsScreen/ItemsStore.kt
A app/src/main/java/com/github/nacabaro/vbhelper/screens/itemsScreen/MyItems.kt
A app/src/main/java/com/github/nacabaro/vbhelper/source/ItemsRepository.kt
A app/src/main/res/drawable/baseline_10_hour_timer.xml
A app/src/main/res/drawable/baseline_12_hour_timer.xml
A app/src/main/res/drawable/baseline_15_min_timer.xml
A app/src/main/res/drawable/baseline_24_hour_timer.xml
A app/src/main/res/drawable/baseline_30_min_timer.xml
A app/src/main/res/drawable/baseline_5_hour_timer.xml
A app/src/main/res/drawable/baseline_60_min_timer.xml
A app/src/main/res/drawable/baseline_arrow_up_24.xml
A app/src/main/res/drawable/baseline_double_arrow_down.xml
A app/src/main/res/drawable/baseline_double_arrow_up.xml
A app/src/main/res/drawable/baseline_reset_24.xml
A app/src/main/res/drawable/baseline_shield_24.xml
A app/src/main/res/drawable/baseline_single_arrow_down.xml
A app/src/main/res/drawable/baseline_single_arrow_up.xml
A app/src/main/res/drawable/baseline_triple_arrow_down.xml
A app/src/main/res/drawable/baseline_triple_arrow_up.xml
M gradle/libs.versions.toml
A app/src/main/assets/items.db => app/src/main/assets/items.db +0 -0
M app/src/main/java/com/github/nacabaro/vbhelper/MainActivity.kt => app/src/main/java/com/github/nacabaro/vbhelper/MainActivity.kt +10 -2
@@ 24,6 24,7 @@ import com.github.nacabaro.vbhelper.domain.characters.Character
import com.github.nacabaro.vbhelper.domain.device_data.BECharacterData
import com.github.nacabaro.vbhelper.domain.device_data.UserCharacter
import com.github.nacabaro.vbhelper.navigation.AppNavigationHandlers
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.ui.theme.VBHelperTheme


@@ 63,12 64,17 @@ class MainActivity : ComponentActivity() {
            this::unregisterActivityLifecycleListener
        )
        val settingsScreenController = SettingsScreenControllerImpl(this)
        val itemsScreenController = ItemsScreenControllerImpl(this)

        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        setContent {
            VBHelperTheme {
                MainApplication(scanScreenController, settingsScreenController)
                MainApplication(
                    scanScreenController = scanScreenController,
                    settingsScreenController = settingsScreenController,
                    itemsScreenController = itemsScreenController
                )
            }
        }
        Log.i("MainActivity", "Activity onCreated")


@@ 189,13 195,15 @@ class MainActivity : ComponentActivity() {
    @Composable
    private fun MainApplication(
        scanScreenController: ScanScreenControllerImpl,
        settingsScreenController: SettingsScreenControllerImpl
        settingsScreenController: SettingsScreenControllerImpl,
        itemsScreenController: ItemsScreenControllerImpl
    ) {

        AppNavigation(
            applicationNavigationHandlers = AppNavigationHandlers(
                settingsScreenController,
                scanScreenController,
                itemsScreenController
            ),
            onClickImportCard = {
                val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {

M app/src/main/java/com/github/nacabaro/vbhelper/components/CharacterEntry.kt => app/src/main/java/com/github/nacabaro/vbhelper/components/CharacterEntry.kt +3 -11
@@ 1,6 1,5 @@
package com.github.nacabaro.vbhelper.components

import android.util.Log
import android.widget.Toast
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Arrangement


@@ 23,22 22,18 @@ import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.github.nacabaro.vbhelper.utils.BitmapData
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 androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.TextUnit
import com.github.nacabaro.vbhelper.utils.getObscuredBitmap
import java.nio.ByteBuffer

@Composable
fun CharacterEntry(
    icon: BitmapData,
    obscure: Boolean = false,
    modifier: Modifier = Modifier,
    obscure: Boolean = false,
    shape: Shape = MaterialTheme.shapes.medium,
    multiplier: Int = 3,
    onClick: () -> Unit = {  }


@@ 79,8 74,6 @@ fun ItemDisplay(
    icon: Int,
    textValue: String,
    modifier: Modifier = Modifier,
    iconSize: Dp = 48.dp,
    textSize: TextUnit = 24.sp,
    definition: String = "",
) {
    val context = LocalContext.current


@@ 101,15 94,14 @@ fun ItemDisplay(
                painter = painterResource(icon),
                contentDescription = "Vitals",
                modifier = Modifier
                    .fillMaxSize(0.5f)
                    .padding(8.dp)
                    .size(iconSize)
            )
            Text(
                text = textValue,
                textAlign = TextAlign.Center,
                fontSize = textSize,
                fontFamily = MaterialTheme.typography.titleLarge.fontFamily,
                fontWeight = FontWeight.Bold
                fontWeight = FontWeight.Bold,
            )
        }
    }

A app/src/main/java/com/github/nacabaro/vbhelper/components/ItemElement.kt => app/src/main/java/com/github/nacabaro/vbhelper/components/ItemElement.kt +211 -0
@@ 0,0 1,211 @@
package com.github.nacabaro.vbhelper.components

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.Spacer
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.Button
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.tooling.preview.Preview
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
fun ItemElement(
    itemIcon: Int,
    lengthIcon: Int,
    modifier: Modifier = Modifier,
    onClick: (() -> Unit) = {  }
) {
    Card (
        onClick = onClick,
        modifier = modifier
            .aspectRatio(1f)
    ) {
        Box(modifier = Modifier.fillMaxSize()) {
            // Background image (full size)
            Icon(
                painter = painterResource(id = itemIcon),
                contentDescription = null,
                modifier = Modifier
                    .size(96.dp)
                    .align(Alignment.Center)
            )
            Icon(
                painter = painterResource(id = lengthIcon),
                contentDescription = null,
                tint = MaterialTheme.colorScheme.surfaceTint,
                modifier = Modifier
                    .size(48.dp) // Set the size of the overlay image
                    .padding(4.dp
                    )
                    .align(Alignment.TopStart) // Align to the top end (top-right corner)
            )
        }
    }
}

@Composable
fun ItemDialog(
    name: String,
    description: String,
    itemIcon: Int,
    lengthIcon: Int,
    amount: Int,
    onClickUse: () -> Unit,
    onClickCancel: () -> Unit
) {
    Dialog(
        onDismissRequest = onClickCancel,
        properties = DialogProperties(
            dismissOnBackPress = true,
            dismissOnClickOutside = true
        )
    ) {
        Card (
            modifier = Modifier
                .fillMaxWidth()
                .padding(16.dp)
        ) {
            Column (
                modifier = Modifier
                    .padding(16.dp)
            ) {
                Row {
                    Box(modifier = Modifier) {
                        // Background image (full size)
                        Icon(
                            painter = painterResource(id = itemIcon),
                            contentDescription = null,
                            modifier = Modifier
                                .size(96.dp)
                                .align(Alignment.Center)
                        )
                        Icon(
                            painter = painterResource(id = lengthIcon),
                            contentDescription = null,
                            tint = MaterialTheme.colorScheme.outline,
                            modifier = Modifier
                                .size(64.dp) // Set the size of the overlay image
                                .align(Alignment.BottomEnd) // Align to the top end (top-right corner)
                        )
                    }
                    Column (
                        modifier = Modifier
                            .padding(16.dp)
                    ) {
                        Text(
                            fontSize = MaterialTheme.typography.titleLarge.fontSize,
                            text = name,
                            modifier = Modifier
                                .fillMaxWidth()
                        )
                    }
                }
                Text(
                    textAlign = TextAlign.Center,
                    fontSize = MaterialTheme.typography.bodyMedium.fontSize,
                    fontFamily = MaterialTheme.typography.bodyMedium.fontFamily,
                    text = description
                )
                Text(
                    textAlign = TextAlign.Center,
                    fontSize = MaterialTheme.typography.bodySmall.fontSize,
                    fontFamily = MaterialTheme.typography.bodySmall.fontFamily,
                    text = "You have $amount of this item",
                    modifier = Modifier
                        .fillMaxWidth()
                        .padding(16.dp)
                )
                Row (
                    horizontalArrangement = Arrangement.Center,
                    modifier = Modifier
                        .fillMaxWidth()
                ) {
                    Button(
                        onClick = onClickUse
                    ) {
                        Text("Use item")
                    }
                    Spacer(modifier = Modifier.size(8.dp))
                    Button(
                        onClick = onClickCancel
                    ) {
                        Text("Cancel")
                    }
                }
            }
        }
    }
}

fun getIconResource(index: Int): Int {
    return when (index) {
        ItemsScreenControllerImpl.ItemTypes.PPTraining.id -> R.drawable.baseline_agility_24
        ItemsScreenControllerImpl.ItemTypes.APTraining.id -> R.drawable.baseline_attack_24
        ItemsScreenControllerImpl.ItemTypes.HPTraining.id -> R.drawable.baseline_shield_24
        ItemsScreenControllerImpl.ItemTypes.BPTraining.id -> R.drawable.baseline_trophy_24
        ItemsScreenControllerImpl.ItemTypes.AllTraining.id -> R.drawable.baseline_arrow_up_24
        6 -> R.drawable.baseline_timer_24
        7 -> R.drawable.baseline_rank_24
        8 -> R.drawable.baseline_vitals_24
        else -> R.drawable.baseline_question_mark_24
    }
}

fun getLengthResource(index: Int): Int {
    return when (index) {
        15 -> R.drawable.baseline_15_min_timer
        30 -> R.drawable.baseline_30_min_timer
        60 -> R.drawable.baseline_60_min_timer
        -60 -> R.drawable.baseline_60_min_timer
        300 -> R.drawable.baseline_5_hour_timer
        600 -> R.drawable.baseline_10_hour_timer
        -720 -> R.drawable.baseline_12_hour_timer
        -1440 -> R.drawable.baseline_24_hour_timer
        6000 -> R.drawable.baseline_reset_24
        1000 -> R.drawable.baseline_single_arrow_up
        2500 -> R.drawable.baseline_double_arrow_up
        5000 -> R.drawable.baseline_triple_arrow_up
        9999 -> R.drawable.baseline_health_24
        -500 -> R.drawable.baseline_single_arrow_down
        -1000 -> R.drawable.baseline_double_arrow_down
        -2500 -> R.drawable.baseline_triple_arrow_down
        -9999 -> R.drawable.baseline_reset_24
        else -> R.drawable.baseline_question_mark_24
    }
}

@Composable
@Preview(showBackground = true)
fun PreviewItemDialog() {
    VBHelperTheme {
        ItemDialog(
            name = "AP Training x3 (60 min)",
            description = "Boosts AP during training (for 60 minutes)",
            itemIcon = R.drawable.baseline_attack_24,
            lengthIcon = R.drawable.baseline_60_min_timer,
            onClickUse = {  },
            onClickCancel = {  },
            amount = 19
        )
    }
}
\ No newline at end of file

A app/src/main/java/com/github/nacabaro/vbhelper/daos/ItemDao.kt => app/src/main/java/com/github/nacabaro/vbhelper/daos/ItemDao.kt +45 -0
@@ 0,0 1,45 @@
package com.github.nacabaro.vbhelper.daos

import androidx.room.Dao
import androidx.room.Query
import com.github.nacabaro.vbhelper.dtos.ItemDtos

@Dao
interface ItemDao {
    @Query("""
        SELECT Items.*, UserItems.quantity
        FROM Items
        LEFT JOIN UserItems ON Items.id = UserItems.itemId
        ORDER BY Items.itemIcon ASC
    """)
    suspend fun getAllItems(): List<ItemDtos.ItemsWithQuantities>

    @Query("""
        SELECT Items.*, UserItems.quantity
        FROM Items
        JOIN UserItems ON Items.id = UserItems.itemId
    """)
    suspend fun getAllUserItems(): List<ItemDtos.ItemsWithQuantities>

    @Query("""
        SELECT Items.*, UserItems.quantity
        FROM Items
        JOIN UserItems ON Items.id = UserItems.itemId
        WHERE UserItems.itemId = :itemId
    """)
    fun getUserItem(itemId: Long): ItemDtos.ItemsWithQuantities

    @Query("""
        UPDATE UserItems
        SET quantity = quantity - 1
        WHERE itemId = :itemId
    """)
    fun useItem(itemId: Long)

    @Query("""
        UPDATE UserItems
        SET quantity = quantity - :itemAmount
        WHERE itemId = :itemId
    """)
    suspend fun purchaseItem(itemId: Long, itemAmount: Int)
}
\ No newline at end of file

M app/src/main/java/com/github/nacabaro/vbhelper/daos/UserCharacterDao.kt => app/src/main/java/com/github/nacabaro/vbhelper/daos/UserCharacterDao.kt +7 -0
@@ 4,6 4,7 @@ import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import androidx.room.Upsert
import com.github.nacabaro.vbhelper.domain.device_data.UserCharacter
import com.github.nacabaro.vbhelper.domain.device_data.BECharacterData
import com.github.nacabaro.vbhelper.domain.device_data.TransformationHistory


@@ 17,6 18,12 @@ interface UserCharacterDao {
    @Insert
    fun insertBECharacterData(characterData: BECharacterData)

    @Upsert
    fun updateCharacter(character: UserCharacter)

    @Upsert
    fun updateBECharacterData(characterData: BECharacterData)

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    fun insertTransformationHistory(vararg transformationHistory: TransformationHistory)


M app/src/main/java/com/github/nacabaro/vbhelper/database/AppDatabase.kt => app/src/main/java/com/github/nacabaro/vbhelper/database/AppDatabase.kt +7 -1
@@ 5,6 5,7 @@ import androidx.room.RoomDatabase
import com.github.nacabaro.vbhelper.daos.CharacterDao
import com.github.nacabaro.vbhelper.daos.DexDao
import com.github.nacabaro.vbhelper.daos.DiMDao
import com.github.nacabaro.vbhelper.daos.ItemDao
import com.github.nacabaro.vbhelper.daos.UserCharacterDao
import com.github.nacabaro.vbhelper.domain.characters.Character
import com.github.nacabaro.vbhelper.domain.characters.Card


@@ 13,6 14,8 @@ 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,


@@ 23,7 26,9 @@ import com.github.nacabaro.vbhelper.domain.device_data.UserCharacter
        UserCharacter::class,
        BECharacterData::class,
        TransformationHistory::class,
        Dex::class
        Dex::class,
        Items::class,
        UserItems::class
    ]
)
abstract class AppDatabase : RoomDatabase() {


@@ 31,4 36,5 @@ abstract class AppDatabase : RoomDatabase() {
    abstract fun characterDao(): CharacterDao
    abstract fun userCharacterDao(): UserCharacterDao
    abstract fun dexDao(): DexDao
    abstract fun itemDao(): ItemDao
}
\ No newline at end of file

M app/src/main/java/com/github/nacabaro/vbhelper/di/DefaultAppContainer.kt => app/src/main/java/com/github/nacabaro/vbhelper/di/DefaultAppContainer.kt +3 -1
@@ 22,7 22,9 @@ class DefaultAppContainer(private val context: Context) : AppContainer {
            context = context,
            klass = AppDatabase::class.java,
            "internalDb"
        ).build()
        )
            .createFromAsset("items.db")
            .build()
    }

    override val dataStoreSecretsRepository = DataStoreSecretsRepository(context.secretsStore)

M app/src/main/java/com/github/nacabaro/vbhelper/domain/device_data/BECharacterData.kt => app/src/main/java/com/github/nacabaro/vbhelper/domain/device_data/BECharacterData.kt +4 -4
@@ 20,7 20,7 @@ data class BECharacterData (
    val trainingHp: Int,
    val trainingAp: Int,
    val trainingBp: Int,
    val remainingTrainingTimeInMinutes: Int,
    var remainingTrainingTimeInMinutes: Int,
    val itemEffectMentalStateValue: Int,
    val itemEffectMentalStateMinutesRemaining: Int,
    val itemEffectActivityLevelValue: Int,


@@ 32,9 32,9 @@ data class BECharacterData (
    val abilityBranch: Int,
    val abilityReset: Int,
    val rank: Int,
    val itemType: Int,
    val itemMultiplier: Int,
    val itemRemainingTime: Int,
    var itemType: Int,
    var itemMultiplier: Int,
    var itemRemainingTime: Int,
    val otp0: String,
    val otp1: String,
    val minorVersion: Int,

A app/src/main/java/com/github/nacabaro/vbhelper/domain/items/Items.kt => app/src/main/java/com/github/nacabaro/vbhelper/domain/items/Items.kt +14 -0
@@ 0,0 1,14 @@
package com.github.nacabaro.vbhelper.domain.items

import androidx.room.Entity
import androidx.room.PrimaryKey

@Entity
data class Items(
    @PrimaryKey val id: Long,
    val name: String,
    val description: String,
    val itemIcon: Int,
    val itemLength: Int,
    val price: Int
)

A app/src/main/java/com/github/nacabaro/vbhelper/domain/items/UserItems.kt => app/src/main/java/com/github/nacabaro/vbhelper/domain/items/UserItems.kt +20 -0
@@ 0,0 1,20 @@
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,
)

A app/src/main/java/com/github/nacabaro/vbhelper/dtos/ItemDtos.kt => app/src/main/java/com/github/nacabaro/vbhelper/dtos/ItemDtos.kt +14 -0
@@ 0,0 1,14 @@
package com.github.nacabaro.vbhelper.dtos


object ItemDtos {
    data class ItemsWithQuantities (
        val id: Long,
        val name: String,
        val description: String,
        val itemIcon: Int,
        val itemLength: Int,
        val price: Int,
        val quantity: Int,
    )
}
\ No newline at end of file

M app/src/main/java/com/github/nacabaro/vbhelper/navigation/AppNavigation.kt => app/src/main/java/com/github/nacabaro/vbhelper/navigation/AppNavigation.kt +15 -1
@@ 11,17 11,20 @@ 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.homeScreens.HomeScreen
import com.github.nacabaro.vbhelper.screens.ItemsScreen
import com.github.nacabaro.vbhelper.screens.itemsScreen.ItemsScreen
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.itemsScreen.ChooseCharacterScreen
import com.github.nacabaro.vbhelper.screens.itemsScreen.ItemsScreenControllerImpl
import com.github.nacabaro.vbhelper.screens.settingsScreen.SettingsScreenControllerImpl

data class AppNavigationHandlers(
    val settingsScreenController: SettingsScreenControllerImpl,
    val scanScreenController: ScanScreenControllerImpl,
    val itemsScreenController: ItemsScreenControllerImpl
)

@Composable


@@ 96,6 99,17 @@ fun AppNavigation(
                    navController = navController
                )
            }
            composable(NavigationItems.ApplyItem.route) {
                val itemId = it.arguments?.getString("itemId")
                if (itemId != null) {
                    ChooseCharacterScreen(
                        itemsScreenController = applicationNavigationHandlers
                            .itemsScreenController,
                        navController = navController,
                        itemId = itemId.toLong()
                    )
                }
            }
        }
    }
}

M app/src/main/java/com/github/nacabaro/vbhelper/navigation/NavigationItems.kt => app/src/main/java/com/github/nacabaro/vbhelper/navigation/NavigationItems.kt +3 -0
@@ 16,4 16,7 @@ sealed class NavigationItems (
    object Viewer : NavigationItems("Viewer", R.drawable.baseline_image_24, "Viewer")
    object CardView : NavigationItems("Card/{cardId}", R.drawable.baseline_image_24, "Card")
    object Items : NavigationItems("Items", R.drawable.baseline_data_24, "Items")
    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")
}
\ No newline at end of file

D app/src/main/java/com/github/nacabaro/vbhelper/screens/ItemsScreen.kt => app/src/main/java/com/github/nacabaro/vbhelper/screens/ItemsScreen.kt +0 -12
@@ 1,12 0,0 @@
package com.github.nacabaro.vbhelper.screens

import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.navigation.NavController

@Composable
fun ItemsScreen(
    navController: NavController
) {
    Text(text = "Items")
}
\ No newline at end of file

M app/src/main/java/com/github/nacabaro/vbhelper/screens/StorageScreen.kt => app/src/main/java/com/github/nacabaro/vbhelper/screens/StorageScreen.kt +22 -24
@@ 1,6 1,5 @@
package com.github.nacabaro.vbhelper.screens

import android.util.Log
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.gestures.scrollable
import androidx.compose.foundation.layout.Arrangement


@@ 8,7 7,6 @@ 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.layout.size
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.items


@@ 66,7 64,7 @@ fun StorageScreen(
    }

    Scaffold (
        topBar = { TopBanner(text = "My Digimon") }
        topBar = { TopBanner(text = "My characters") }
    ) { contentPadding ->
        if (monList.value.isEmpty()) {
            Column (


@@ 101,31 99,31 @@ fun StorageScreen(
                        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()
                                )
                            )
        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()
                        )
                    )
                }
            }
            )
        }
    }
}

M app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/BEBEmHomeScreen.kt => app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/BEBEmHomeScreen.kt +20 -0
@@ 15,8 15,10 @@ 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.domain.device_data.BECharacterData
import com.github.nacabaro.vbhelper.dtos.CharacterDtos
import com.github.nacabaro.vbhelper.screens.itemsScreen.ItemsScreenControllerImpl
import com.github.nacabaro.vbhelper.utils.BitmapData
import java.util.Locale



@@ 153,6 155,24 @@ fun BEBEmHomeScreen(
                    .aspectRatio(1f)
                    .padding(8.dp)
            )
            if (beData.itemRemainingTime != 0) {
                ItemDisplay(
                    icon = getIconResource(beData.itemType),
                    textValue = "${beData.itemRemainingTime} m",
                    definition = when (beData.itemType) {
                        ItemsScreenControllerImpl.ItemTypes.PPTraining.id -> "PP Training"
                        ItemsScreenControllerImpl.ItemTypes.HPTraining.id -> "HP Training"
                        ItemsScreenControllerImpl.ItemTypes.APTraining.id -> "AP Training"
                        ItemsScreenControllerImpl.ItemTypes.BPTraining.id -> "BP Training"
                        ItemsScreenControllerImpl.ItemTypes.AllTraining.id -> "All Training"
                        else -> ""
                    },
                    modifier = Modifier
                        .weight(1f)
                        .aspectRatio(1f)
                        .padding(8.dp)
                )
            }
        }
        Row (
            modifier = Modifier

M app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/BEDiMHomeScreen.kt => app/src/main/java/com/github/nacabaro/vbhelper/screens/homeScreens/BEDiMHomeScreen.kt +39 -9
@@ 16,8 16,10 @@ 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.domain.device_data.BECharacterData
import com.github.nacabaro.vbhelper.dtos.CharacterDtos
import com.github.nacabaro.vbhelper.screens.itemsScreen.ItemsScreenControllerImpl
import com.github.nacabaro.vbhelper.utils.BitmapData
import kotlin.text.format



@@ 33,7 35,7 @@ fun BEDiMHomeScreen(
            .padding(top = contentPadding.calculateTopPadding())
            .verticalScroll(state = rememberScrollState())
    ) {
        Row (
        Row(
            modifier = Modifier
                .fillMaxWidth()
        ) {


@@ 49,7 51,7 @@ fun BEDiMHomeScreen(
                    .weight(1f)
                    .aspectRatio(1f)
            )
            Column (
            Column(
                modifier = Modifier
                    .weight(0.5f)
                    .aspectRatio(0.5f)


@@ 74,7 76,7 @@ fun BEDiMHomeScreen(
                )
            }
        }
        Row (
        Row(
            modifier = Modifier
                .fillMaxWidth()
        ) {


@@ 108,7 110,7 @@ fun BEDiMHomeScreen(
                    .padding(8.dp)
            )
        }
        Row (
        Row(
            modifier = Modifier
                .fillMaxWidth()
        ) {


@@ 130,8 132,13 @@ fun BEDiMHomeScreen(
                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
                        val battleWinPercentage =
                            activeMon.totalBattlesWon.toFloat() / (activeMon.totalBattlesWon + activeMon.totalBattlesLost).toFloat()
                        String.format(
                            Locale.getDefault(),
                            "%.2f",
                            battleWinPercentage * 100
                        ) + " %" // Specify locale
                    }
                },
                definition = "Total battle win %",


@@ 145,8 152,13 @@ fun BEDiMHomeScreen(
                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
                        val battleWinPercentage =
                            activeMon.currentPhaseBattlesWon.toFloat() / (activeMon.currentPhaseBattlesWon + activeMon.currentPhaseBattlesLost).toFloat()
                        String.format(
                            Locale.getDefault(),
                            "%.2f",
                            battleWinPercentage * 100
                        ) + " %" // Specify locale
                    }
                },
                definition = "Current phase win %",


@@ 155,8 167,26 @@ fun BEDiMHomeScreen(
                    .aspectRatio(1f)
                    .padding(8.dp)
            )
            if (beData.itemRemainingTime != 0) {
                ItemDisplay(
                    icon = getIconResource(beData.itemType),
                    textValue = "${beData.itemRemainingTime} m",
                    definition = when (beData.itemType) {
                        ItemsScreenControllerImpl.ItemTypes.PPTraining.id -> "PP Training"
                        ItemsScreenControllerImpl.ItemTypes.HPTraining.id -> "HP Training"
                        ItemsScreenControllerImpl.ItemTypes.APTraining.id -> "AP Training"
                        ItemsScreenControllerImpl.ItemTypes.BPTraining.id -> "BP Training"
                        ItemsScreenControllerImpl.ItemTypes.AllTraining.id -> "All Training"
                        else -> ""
                    },
                    modifier = Modifier
                        .weight(1f)
                        .aspectRatio(1f)
                        .padding(8.dp)
                )
            }
        }
        Row (
        Row(
            modifier = Modifier
                .fillMaxWidth()
        ) {

A app/src/main/java/com/github/nacabaro/vbhelper/screens/itemsScreen/ChooseCharacterScreen.kt => app/src/main/java/com/github/nacabaro/vbhelper/screens/itemsScreen/ChooseCharacterScreen.kt +90 -0
@@ 0,0 1,90 @@
package com.github.nacabaro.vbhelper.screens.itemsScreen

import android.widget.Toast
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.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.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.source.StorageRepository
import com.github.nacabaro.vbhelper.utils.BitmapData
import kotlinx.coroutines.launch


@Composable
fun ChooseCharacterScreen(
    navController: NavController,
    itemsScreenController: ItemsScreenControllerImpl,
    itemId: Long
) {
    val coroutineScope = rememberCoroutineScope()
    val application = LocalContext.current.applicationContext as VBHelper
    val storageRepository = StorageRepository(application.container.db)
    val characterList = remember {
        mutableStateOf<List<CharacterDtos.CharacterWithSprites>>(emptyList())
    }

    var selectedCharacter by remember { mutableStateOf<Long?>(null) }

    LaunchedEffect(storageRepository) {
        coroutineScope.launch {
            characterList.value = storageRepository.getAllCharacters()
        }
    }

    LaunchedEffect (selectedCharacter) {
        if (selectedCharacter != null) {
            itemsScreenController.applyItem(itemId, selectedCharacter!!) {
                Toast.makeText(
                    application.applicationContext,
                    "Item applied!",
                    Toast.LENGTH_SHORT
                ).show()
                navController.popBackStack()
            }
        }
    }

    Scaffold(
        topBar = {
            TopBanner(
                text = "Choose character",
                onBackClick = {
                    navController.popBackStack()
                }
            )
        }
    ) { contentPadding ->
        LazyVerticalGrid(
            columns = GridCells.Fixed(3),
            modifier = Modifier
                .padding(top = contentPadding.calculateTopPadding())
        ) {
            items(characterList.value) {
                CharacterEntry(
                    icon = BitmapData(
                        bitmap = it.spriteIdle,
                        width = it.spriteWidth,
                        height = it.spriteHeight
                    )
                ) {
                    selectedCharacter = it.id
                }
            }
        }
    }
}
\ No newline at end of file

A app/src/main/java/com/github/nacabaro/vbhelper/screens/itemsScreen/ItemsScreen.kt => app/src/main/java/com/github/nacabaro/vbhelper/screens/itemsScreen/ItemsScreen.kt +60 -0
@@ 0,0 1,60 @@
package com.github.nacabaro.vbhelper.screens.itemsScreen

import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Tab
import androidx.compose.material3.TabRow
import androidx.compose.material3.Text
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.navigation.NavController
import com.github.nacabaro.vbhelper.components.TopBanner
import com.github.nacabaro.vbhelper.navigation.NavigationItems

@Composable
fun ItemsScreen(
    navController: NavController,
) {
    var selectedTabItem by remember { mutableStateOf(0) }
    val items = listOf(
        NavigationItems.MyItems,
        NavigationItems.ItemsStore
    )
    Scaffold(
        topBar = {
            Column {
                TopBanner("Items")
                TabRow(
                    selectedTabIndex = selectedTabItem,
                    modifier = Modifier
                ) {
                    items.forEachIndexed { index, item ->
                        Tab(
                            text = { Text(item.label) },
                            selected = selectedTabItem == index,
                            onClick = { selectedTabItem = index }
                        )
                    }
                }
            }
        }
    ) { contentPadding ->
        Box(
            modifier = Modifier
                .fillMaxWidth()
                .padding(top = contentPadding.calculateTopPadding())
        ) {
            when (selectedTabItem) {
                0 -> MyItems(navController)
                1 -> ItemsStore(navController)
            }
        }
    }
}
\ No newline at end of file

A app/src/main/java/com/github/nacabaro/vbhelper/screens/itemsScreen/ItemsScreenController.kt => app/src/main/java/com/github/nacabaro/vbhelper/screens/itemsScreen/ItemsScreenController.kt +6 -0
@@ 0,0 1,6 @@
package com.github.nacabaro.vbhelper.screens.itemsScreen


interface ItemsScreenController {
    fun applyItem(itemId: Long, characterId: Long, onCompletion: () -> Unit)
}
\ No newline at end of file

A app/src/main/java/com/github/nacabaro/vbhelper/screens/itemsScreen/ItemsScreenControllerImpl.kt => app/src/main/java/com/github/nacabaro/vbhelper/screens/itemsScreen/ItemsScreenControllerImpl.kt +118 -0
@@ 0,0 1,118 @@
package com.github.nacabaro.vbhelper.screens.itemsScreen

import androidx.activity.ComponentActivity
import androidx.lifecycle.lifecycleScope
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.dtos.ItemDtos
import com.github.nacabaro.vbhelper.utils.DeviceType
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext

class ItemsScreenControllerImpl (
    private val context: ComponentActivity,
): ItemsScreenController {
    private var database: AppDatabase

    enum class ItemTypes(val id: Int) {
        PPTraining(1),
        HPTraining(2),
        APTraining(3),
        BPTraining(4),
        AllTraining(5),
        EvoTimer(6),
        LimitTimer(7),
        Vitals(8)
    }

    init {
        val application = context.applicationContext as VBHelper
        database = application.container.db
    }

    override fun applyItem(itemId: Long, characterId: Long, onCompletion: () -> Unit) {
        context.lifecycleScope.launch {
            withContext(Dispatchers.IO) {
                val item = getItem(itemId)
                val characterData = database.userCharacterDao().getCharacter(characterId)
                val beCharacterData: BECharacterData
                //var vbCharacterData: VBCharacterData

                if (characterData.characterType == DeviceType.BEDevice) {
                    beCharacterData = database.userCharacterDao().getBeData(characterId)
                } else {
                    TODO("Not implemented")
                    //vbCharacterData = database.userCharacterDao().getVbData(characterId)
                }

                if (item.itemIcon in 1 .. 5 && characterData.characterType == DeviceType.BEDevice) {
                    beCharacterData.itemType = item.itemIcon
                    beCharacterData.itemMultiplier = 3
                    beCharacterData.itemRemainingTime = item.itemLength

                    database
                        .userCharacterDao()
                        .updateBECharacterData(beCharacterData)

                } else if (item.itemIcon == ItemTypes.EvoTimer.id) {
                    characterData.transformationCountdown += item.itemLength
                    if (characterData.transformationCountdown < 0) {
                        characterData.transformationCountdown = 0
                    }

                    // VB does not like it when the transformationCountdown is 0
                    if (characterData.characterType == DeviceType.VBDevice &&
                        characterData.transformationCountdown <= 0 ) {
                        characterData.transformationCountdown = 1
                    }

                    database
                        .userCharacterDao()
                        .updateCharacter(characterData)

                } else if (item.itemIcon == ItemTypes.LimitTimer.id) {
                    beCharacterData.remainingTrainingTimeInMinutes += item.itemLength
                    if (beCharacterData.remainingTrainingTimeInMinutes > 6000) {
                        beCharacterData.remainingTrainingTimeInMinutes = 6000
                    }

                    database
                        .userCharacterDao()
                        .updateBECharacterData(beCharacterData)

                } else if (item.itemIcon == ItemTypes.Vitals.id) {
                    characterData.vitalPoints += item.itemLength
                    if (characterData.vitalPoints < 0) {
                        characterData.vitalPoints = 0
                    } else if (characterData.vitalPoints > 9999) {
                        characterData.vitalPoints = 9999
                    }

                    database
                        .userCharacterDao()
                        .updateCharacter(characterData)
                }

                consumeItem(item.id)

                context.runOnUiThread {
                    onCompletion()
                }
            }
        }
    }

    private fun getItem(itemId: Long): ItemDtos.ItemsWithQuantities {
        return database
            .itemDao()
            .getUserItem(itemId)
    }

    private fun consumeItem(itemId: Long) {
        database
            .itemDao()
            .useItem(itemId)
    }
}
\ No newline at end of file

A app/src/main/java/com/github/nacabaro/vbhelper/screens/itemsScreen/ItemsStore.kt => app/src/main/java/com/github/nacabaro/vbhelper/screens/itemsScreen/ItemsStore.kt +75 -0
@@ 0,0 1,75 @@
package com.github.nacabaro.vbhelper.screens.itemsScreen

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.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.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.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
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext

@Composable
fun ItemsStore(
    navController: NavController
) {
    val application = LocalContext.current.applicationContext as VBHelper
    val itemsRepository = ItemsRepository(application.container.db)
    val myItems = remember { mutableStateOf(emptyList<ItemDtos.ItemsWithQuantities>()) }
    var selectedElementIndex by remember { mutableStateOf<Int?>(null) }

    LaunchedEffect(itemsRepository) {
        withContext(Dispatchers.IO) {
            myItems.value = itemsRepository.getAllItems()
        }
    }

    if (myItems.value.isEmpty()) {
        Text("No items")
    } else {
        LazyVerticalGrid(
            columns = GridCells.Fixed(3),
            modifier = Modifier
        ) {
            items(myItems.value) { index ->
                ItemElement(
                    itemIcon = getIconResource(index.itemIcon),
                    lengthIcon = getLengthResource(index.itemLength),
                    modifier = Modifier
                        .padding(8.dp),
                    onClick = {
                        selectedElementIndex = myItems.value.indexOf(index)
                    }
                )
            }
        }

        if (selectedElementIndex != null) {
            ItemDialog(
                name = myItems.value[selectedElementIndex!!].name,
                description = myItems.value[selectedElementIndex!!].description,
                itemIcon = getIconResource(myItems.value[selectedElementIndex!!].itemIcon),
                lengthIcon = getLengthResource(myItems.value[selectedElementIndex!!].itemLength),
                amount = myItems.value[selectedElementIndex!!].quantity,
                onClickUse = { },
                onClickCancel = { selectedElementIndex = null }
            )
        }
    }
}
\ No newline at end of file

A app/src/main/java/com/github/nacabaro/vbhelper/screens/itemsScreen/MyItems.kt => app/src/main/java/com/github/nacabaro/vbhelper/screens/itemsScreen/MyItems.kt +97 -0
@@ 0,0 1,97 @@
package com.github.nacabaro.vbhelper.screens.itemsScreen

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.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.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.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
import com.github.nacabaro.vbhelper.source.ItemsRepository
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext

@Composable
fun MyItems(
    navController: NavController,
) {
    val application = LocalContext.current.applicationContext as VBHelper
    val itemsRepository = ItemsRepository(application.container.db)
    val myItems = remember { mutableStateOf(emptyList<ItemDtos.ItemsWithQuantities>()) }
    var selectedElementIndex by remember { mutableStateOf<Int?>(null) }

    LaunchedEffect(itemsRepository) {
        withContext(Dispatchers.IO) {
            myItems.value = itemsRepository.getUserItems()
        }
    }

    if (myItems.value.isEmpty()) {
        Column(
            verticalArrangement = Arrangement.Center,
            horizontalAlignment = Alignment.CenterHorizontally,
            modifier = Modifier.fillMaxSize()
        ) {
            Text("No items")
        }
    } else {
        LazyVerticalGrid(
            columns = GridCells.Fixed(3),
            modifier = Modifier
        ) {
            items(myItems.value) { index ->
                ItemElement(
                    itemIcon = getIconResource(index.itemIcon),
                    lengthIcon = getLengthResource(index.itemLength),
                    modifier = Modifier
                        .padding(8.dp),
                    onClick = {
                        selectedElementIndex = myItems.value.indexOf(index)
                    }
                )
            }
        }

        if (selectedElementIndex != null) {
            ItemDialog(
                name = myItems.value[selectedElementIndex!!].name,
                description = myItems.value[selectedElementIndex!!].description,
                itemIcon = getIconResource(myItems.value[selectedElementIndex!!].itemIcon),
                lengthIcon = getLengthResource(myItems.value[selectedElementIndex!!].itemLength),
                amount = myItems.value[selectedElementIndex!!].quantity,
                onClickUse = {
                    navController
                        .navigate(
                            NavigationItems
                                .ApplyItem.route
                                .replace(
                                    "{itemId}",
                                    myItems.value[selectedElementIndex!!].id.toString()
                                )
                        )
                    selectedElementIndex = null
                },
                onClickCancel = { selectedElementIndex = null }
            )
        }
    }
}
\ No newline at end of file

A app/src/main/java/com/github/nacabaro/vbhelper/source/ItemsRepository.kt => app/src/main/java/com/github/nacabaro/vbhelper/source/ItemsRepository.kt +17 -0
@@ 0,0 1,17 @@
package com.github.nacabaro.vbhelper.source

import com.github.nacabaro.vbhelper.database.AppDatabase
import com.github.nacabaro.vbhelper.domain.items.Items
import com.github.nacabaro.vbhelper.dtos.ItemDtos

class ItemsRepository(
    private val db: AppDatabase
) {
    suspend fun getAllItems(): List<ItemDtos.ItemsWithQuantities> {
        return db.itemDao().getAllItems()
    }

    suspend fun getUserItems(): List<ItemDtos.ItemsWithQuantities> {
        return db.itemDao().getAllUserItems()
    }
}
\ No newline at end of file

A app/src/main/res/drawable/baseline_10_hour_timer.xml => app/src/main/res/drawable/baseline_10_hour_timer.xml +21 -0
@@ 0,0 1,21 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="960"
    android:viewportHeight="960">
  <path
      android:pathData="m300.57,506.26c0,-5.08 0.1,-11.73 0.29,-19.95 0.39,-8.21 0.68,-15.45 0.88,-21.71 -0.98,1.17 -3.13,3.32 -6.45,6.45 -3.13,2.93 -6.06,5.57 -8.8,7.92l-24.05,19.36L241.02,471.65 308.49,417.97l36.37,0l0,209.44L300.57,627.41Z"
      android:strokeWidth="0"
      android:fillColor="#000000"/>
  <path
      android:pathData="m518.57,522.69q0,33.73 -7.33,57.79 -7.04,24.05 -23.17,36.96 -15.84,12.91 -42.83,12.91 -37.84,0 -55.44,-28.45 -17.6,-28.75 -17.6,-79.2 0,-34.03 7.04,-58.08 7.04,-24.05 23.17,-36.96 16.13,-12.91 42.83,-12.91 37.55,0 55.44,28.45 17.89,28.45 17.89,79.49zM416.2,522.69q0,35.79 6.16,53.97 6.16,17.89 22.88,17.89 16.43,0 22.88,-17.89 6.45,-17.89 6.45,-53.97 0,-35.79 -6.45,-53.97 -6.45,-18.19 -22.88,-18.19 -16.72,0 -22.88,18.19 -6.16,18.19 -6.16,53.97z"
      android:strokeWidth="0"
      android:fillColor="#000000"/>
  <path
      android:pathData="m600.71,449.94q0,11.73 -0.88,22.59 -0.59,10.85 -1.17,15.25l2.35,0q7.63,-12.32 19.65,-17.89 12.03,-5.57 26.69,-5.57 26.11,0 41.65,14.08 15.84,13.79 15.84,44.59l0,104.43l-43.71,0l0,-93.57q0,-34.61 -25.81,-34.61 -19.65,0 -27.28,13.79 -7.33,13.49 -7.33,39.01l0,75.39l-43.71,0l0,-222.93l43.71,0z"
      android:strokeWidth="0"
      android:fillColor="#000000"/>
  <path
      android:pathData="m360,120v-80h240v80zM480,880c-49.33,0 -95.83,-9.5 -139.5,-28.5 -43.67,-19 -81.83,-44.83 -114.5,-77.5 -32.67,-32.67 -58.5,-70.83 -77.5,-114.5 -19,-43.67 -28.5,-90.17 -28.5,-139.5 0,-49.33 9.5,-95.83 28.5,-139.5 19,-43.67 44.83,-81.83 77.5,-114.5 32.67,-32.67 70.83,-58.5 114.5,-77.5 43.67,-19 90.17,-28.5 139.5,-28.5 41.33,0 81,6.67 119,20 38,13.33 73.67,32.67 107,58l56,-56 56,56 -56,56c25.33,33.33 44.67,69 58,107 13.33,38 20,77.67 20,119 0,49.33 -9.5,95.83 -28.5,139.5 -19,43.67 -44.83,81.83 -77.5,114.5 -32.67,32.67 -70.83,58.5 -114.5,77.5 -43.67,19 -90.17,28.5 -139.5,28.5zM480,800c77.33,0 143.33,-27.33 198,-82 54.67,-54.67 82,-120.67 82,-198 0,-77.33 -27.33,-143.33 -82,-198 -54.67,-54.67 -120.67,-82 -198,-82 -77.33,0 -143.33,27.33 -198,82 -54.67,54.67 -82,120.67 -82,198 0,77.33 27.33,143.33 82,198 54.67,54.67 120.67,82 198,82z"
      android:fillColor="#000000"/>
</vector>

A app/src/main/res/drawable/baseline_12_hour_timer.xml => app/src/main/res/drawable/baseline_12_hour_timer.xml +24 -0
@@ 0,0 1,24 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="960"
    android:viewportHeight="960">
  <path
      android:pathData="m360,120v-80h240v80zM480,880c-49.33,0 -95.83,-9.5 -139.5,-28.5 -43.67,-19 -81.83,-44.83 -114.5,-77.5 -32.67,-32.67 -58.5,-70.83 -77.5,-114.5 -19,-43.67 -28.5,-90.17 -28.5,-139.5 0,-49.33 9.5,-95.83 28.5,-139.5 19,-43.67 44.83,-81.83 77.5,-114.5 32.67,-32.67 70.83,-58.5 114.5,-77.5 43.67,-19 90.17,-28.5 139.5,-28.5 41.33,0 81,6.67 119,20 38,13.33 73.67,32.67 107,58l56,-56 56,56 -56,56c25.33,33.33 44.67,69 58,107 13.33,38 20,77.67 20,119 0,49.33 -9.5,95.83 -28.5,139.5 -19,43.67 -44.83,81.83 -77.5,114.5 -32.67,32.67 -70.83,58.5 -114.5,77.5 -43.67,19 -90.17,28.5 -139.5,28.5zM480,800c77.33,0 143.33,-27.33 198,-82 54.67,-54.67 82,-120.67 82,-198 0,-77.33 -27.33,-143.33 -82,-198 -54.67,-54.67 -120.67,-82 -198,-82 -77.33,0 -143.33,27.33 -198,82 -54.67,54.67 -82,120.67 -82,198 0,77.33 27.33,143.33 82,198 54.67,54.67 120.67,82 198,82z"
      android:fillColor="#000000"/>
  <path
      android:pathData="m287.83,506.91q0,-7.63 0.29,-19.95 0.59,-12.32 0.88,-21.71 -1.47,1.76 -6.45,6.45 -4.69,4.4 -8.8,7.92L249.7,498.99 228.29,472.3 295.75,418.62l36.37,0l0,209.44l-44.29,0z"
      android:strokeWidth="0"
      android:fillColor="#000000"
      android:strokeColor="#00000000"/>
  <path
      android:pathData="m386.69,628.06l0,-30.8l52.51,-53.09q15.84,-16.43 25.52,-27.28 9.68,-11.15 14.08,-19.95 4.4,-9.09 4.4,-19.36 0,-12.61 -7.04,-18.77 -6.75,-6.16 -18.48,-6.16 -12.03,0 -23.47,5.57Q422.77,463.79 410.15,474.06L386.1,445.61Q395.19,437.69 405.17,430.94 415.43,424.19 428.63,420.09 442.13,415.69 460.9,415.69q20.53,0 35.2,7.63 14.96,7.33 22.88,20.24 8.21,12.61 8.21,28.75 0,17.31 -7.04,31.68 -6.75,14.37 -19.95,28.45 -12.91,14.08 -31.39,31.09l-26.99,25.23l0,2.05l91.23,0l0,37.25z"
      android:strokeWidth="0"
      android:fillColor="#000000"
      android:strokeColor="#00000000"/>
  <path
      android:pathData="m606.98,450.59c0,7.82 -0.29,15.35 -0.88,22.59 -0.39,7.24 -0.78,12.32 -1.17,15.25L607.27,488.43C612.36,480.22 618.91,474.25 626.93,470.54 634.94,466.82 643.84,464.97 653.62,464.97c17.4,0 31.29,4.69 41.65,14.08 10.56,9.19 15.84,24.05 15.84,44.59L711.11,628.06L667.41,628.06l0,-93.57c0,-23.08 -8.6,-34.61 -25.81,-34.61 -13.1,0 -22.2,4.6 -27.28,13.79 -4.89,9 -7.33,22 -7.33,39.01L606.98,628.06L563.27,628.06L563.27,413.88l43.71,0z"
      android:strokeWidth="0"
      android:fillColor="#000000"
      android:strokeColor="#00000000"/>
</vector>

A app/src/main/res/drawable/baseline_15_min_timer.xml => app/src/main/res/drawable/baseline_15_min_timer.xml +15 -0
@@ 0,0 1,15 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="960"
    android:viewportHeight="960">
  <path
      android:pathData="m360,120v-80h240v80zM480,880c-49.33,0 -95.83,-9.5 -139.5,-28.5 -43.67,-19 -81.83,-44.83 -114.5,-77.5 -32.67,-32.67 -58.5,-70.83 -77.5,-114.5 -19,-43.67 -28.5,-90.17 -28.5,-139.5 0,-49.33 9.5,-95.83 28.5,-139.5 19,-43.67 44.83,-81.83 77.5,-114.5 32.67,-32.67 70.83,-58.5 114.5,-77.5 43.67,-19 90.17,-28.5 139.5,-28.5 41.33,0 81,6.67 119,20 38,13.33 73.67,32.67 107,58l56,-56 56,56 -56,56c25.33,33.33 44.67,69 58,107 13.33,38 20,77.67 20,119 0,49.33 -9.5,95.83 -28.5,139.5 -19,43.67 -44.83,81.83 -77.5,114.5 -32.67,32.67 -70.83,58.5 -114.5,77.5 -43.67,19 -90.17,28.5 -139.5,28.5zM480,800c77.33,0 143.33,-27.33 198,-82 54.67,-54.67 82,-120.67 82,-198 0,-77.33 -27.33,-143.33 -82,-198 -54.67,-54.67 -120.67,-82 -198,-82 -77.33,0 -143.33,27.33 -198,82 -54.67,54.67 -82,120.67 -82,198 0,77.33 27.33,143.33 82,198 54.67,54.67 120.67,82 198,82z"
      android:fillColor="#000000"/>
  <path
      android:pathData="m337.48,503.79q0,-11.09 0.43,-29.01 0.85,-17.92 1.28,-31.57 -2.13,2.56 -9.39,9.39 -6.83,6.4 -12.8,11.52L282.01,492.27 250.87,453.44 349,375.36L401.91,375.36L401.91,680L337.48,680Z"
      android:fillColor="#000000"/>
  <path
      android:pathData="m594.76,485.44q27.73,0 49.49,10.67 21.76,10.67 34.13,31.15 12.8,20.48 12.8,50.77 0,49.49 -30.72,78.08Q629.75,684.27 569.59,684.27 545.7,684.27 524.36,680 503.46,675.73 487.67,667.63L487.67,612.16q15.79,8.11 37.97,14.08 22.19,5.55 41.81,5.55 28.59,0 43.52,-11.52 15.36,-11.95 15.36,-36.69 0,-46.08 -61.01,-46.08 -11.95,0 -24.75,2.56 -12.8,2.13 -21.33,4.27l-25.6,-13.65 11.52,-155.31l165.12,0l0,54.61L561.48,429.97l-5.55,59.73q7.25,-1.28 15.36,-2.56 8.53,-1.71 23.47,-1.71z"
      android:fillColor="#000000"/>
</vector>

A app/src/main/res/drawable/baseline_24_hour_timer.xml => app/src/main/res/drawable/baseline_24_hour_timer.xml +24 -0
@@ 0,0 1,24 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="960"
    android:viewportHeight="960">
  <path
      android:pathData="m360,120v-80h240v80zM480,880c-49.33,0 -95.83,-9.5 -139.5,-28.5 -43.67,-19 -81.83,-44.83 -114.5,-77.5 -32.67,-32.67 -58.5,-70.83 -77.5,-114.5 -19,-43.67 -28.5,-90.17 -28.5,-139.5 0,-49.33 9.5,-95.83 28.5,-139.5 19,-43.67 44.83,-81.83 77.5,-114.5 32.67,-32.67 70.83,-58.5 114.5,-77.5 43.67,-19 90.17,-28.5 139.5,-28.5 41.33,0 81,6.67 119,20 38,13.33 73.67,32.67 107,58l56,-56 56,56 -56,56c25.33,33.33 44.67,69 58,107 13.33,38 20,77.67 20,119 0,49.33 -9.5,95.83 -28.5,139.5 -19,43.67 -44.83,81.83 -77.5,114.5 -32.67,32.67 -70.83,58.5 -114.5,77.5 -43.67,19 -90.17,28.5 -139.5,28.5zM480,800c77.33,0 143.33,-27.33 198,-82 54.67,-54.67 82,-120.67 82,-198 0,-77.33 -27.33,-143.33 -82,-198 -54.67,-54.67 -120.67,-82 -198,-82 -77.33,0 -143.33,27.33 -198,82 -54.67,54.67 -82,120.67 -82,198 0,77.33 27.33,143.33 82,198 54.67,54.67 120.67,82 198,82z"
      android:fillColor="#000000"/>
  <path
      android:pathData="M239.19,624.91L239.19,594.11l52.51,-53.09q15.84,-16.43 25.52,-27.28 9.68,-11.15 14.08,-19.95 4.4,-9.09 4.4,-19.36 0,-12.61 -7.04,-18.77 -6.75,-6.16 -18.48,-6.16 -12.03,0 -23.47,5.57 -11.44,5.57 -24.05,15.84L238.6,442.46Q247.7,434.54 257.67,427.79 267.94,421.05 281.14,416.94 294.63,412.54 313.4,412.54q20.53,0 35.2,7.63 14.96,7.33 22.88,20.24 8.21,12.61 8.21,28.75 0,17.31 -7.04,31.68 -6.75,14.37 -19.95,28.45 -12.91,14.08 -31.39,31.09l-26.99,25.23l0,2.05l91.23,0L385.56,624.91Z"
      android:strokeWidth="0"
      android:fillColor="#000000"
      android:strokeColor="#00000000"/>
  <path
      android:pathData="M487.35,624.91L487.35,581.5L397.88,581.5L397.88,550.7L489.7,415.47L530.47,415.47L530.47,547.18L555.7,547.18L555.7,581.5L530.47,581.5L530.47,624.91ZM487.35,497.02q0,-10.27 0.29,-17.01 0.59,-6.75 0.88,-12.03l-1.17,0Q482.36,477.95 474.74,489.39L436.9,547.18L487.35,547.18Z"
      android:strokeWidth="0"
      android:fillColor="#000000"
      android:strokeColor="#00000000"/>
  <path
      android:pathData="m609.48,447.47c0,7.82 -0.29,15.35 -0.88,22.59 -0.39,7.24 -0.78,12.32 -1.17,15.25L609.77,485.31C614.86,477.09 621.41,471.13 629.43,467.41 637.44,463.7 646.34,461.84 656.12,461.84c17.4,0 31.29,4.69 41.65,14.08 10.56,9.19 15.84,24.05 15.84,44.59l0,104.43L669.91,624.93l0,-93.57c0,-23.08 -8.6,-34.61 -25.81,-34.61 -13.1,0 -22.2,4.6 -27.28,13.79 -4.89,9 -7.33,22 -7.33,39.01l0,75.39L565.77,624.93L565.77,414.5l43.71,0z"
      android:strokeWidth="0"
      android:fillColor="#000000"
      android:strokeColor="#00000000"/>
</vector>

A app/src/main/res/drawable/baseline_30_min_timer.xml => app/src/main/res/drawable/baseline_30_min_timer.xml +15 -0
@@ 0,0 1,15 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="960"
    android:viewportHeight="960">
  <path
      android:pathData="m360,120v-80h240v80zM480,880c-49.33,0 -95.83,-9.5 -139.5,-28.5 -43.67,-19 -81.83,-44.83 -114.5,-77.5 -32.67,-32.67 -58.5,-70.83 -77.5,-114.5 -19,-43.67 -28.5,-90.17 -28.5,-139.5 0,-49.33 9.5,-95.83 28.5,-139.5 19,-43.67 44.83,-81.83 77.5,-114.5 32.67,-32.67 70.83,-58.5 114.5,-77.5 43.67,-19 90.17,-28.5 139.5,-28.5 41.33,0 81,6.67 119,20 38,13.33 73.67,32.67 107,58l56,-56 56,56 -56,56c25.33,33.33 44.67,69 58,107 13.33,38 20,77.67 20,119 0,49.33 -9.5,95.83 -28.5,139.5 -19,43.67 -44.83,81.83 -77.5,114.5 -32.67,32.67 -70.83,58.5 -114.5,77.5 -43.67,19 -90.17,28.5 -139.5,28.5zM480,800c77.33,0 143.33,-27.33 198,-82 54.67,-54.67 82,-120.67 82,-198 0,-77.33 -27.33,-143.33 -82,-198 -54.67,-54.67 -120.67,-82 -198,-82 -77.33,0 -143.33,27.33 -198,82 -54.67,54.67 -82,120.67 -82,198 0,77.33 27.33,143.33 82,198 54.67,54.67 120.67,82 198,82z"
      android:fillColor="#000000"/>
  <path
      android:pathData="m453.77,437.38q0,31.57 -19.2,50.35 -18.77,18.77 -46.51,25.6l0,1.28q36.69,4.27 55.47,22.19 19.2,17.92 19.2,48.21 0,26.45 -13.23,47.79Q436.71,653.7 409.4,666.07 382.52,678.02 339.85,678.02q-49.49,0 -87.89,-16.64L251.96,606.76q19.63,9.81 40.96,14.93 21.76,5.12 40.11,5.12 34.56,0 48.21,-11.95 14.08,-11.95 14.08,-33.71 0,-12.8 -6.4,-21.33Q382.52,550.87 366.31,546.6 350.52,541.91 321.93,541.91L298.89,541.91l0,-49.49l23.47,0q28.16,0 42.67,-5.12 14.93,-5.55 20.05,-14.51 5.55,-9.39 5.55,-21.33 0,-16.21 -10.24,-25.17 -9.81,-9.39 -33.28,-9.39 -21.76,0 -37.97,7.68 -15.79,7.25 -26.88,14.51L252.39,394.71q17.92,-12.8 41.81,-21.33 24.32,-8.53 57.6,-8.53 46.93,0 74.24,19.2 27.73,18.77 27.73,53.33z"
      android:fillColor="#000000"/>
  <path
      android:pathData="m715.32,521.43q0,49.07 -10.67,84.05 -10.24,34.99 -33.71,53.76Q647.91,678.02 608.66,678.02q-55.04,0 -80.64,-41.39 -25.6,-41.81 -25.6,-115.2 0,-49.49 10.24,-84.48 10.24,-34.99 33.71,-53.76 23.47,-18.77 62.29,-18.77 54.61,0 80.64,41.39 26.03,41.39 26.03,115.63zM566.42,521.43q0,52.05 8.96,78.51 8.96,26.03 33.28,26.03 23.89,0 33.28,-26.03 9.39,-26.03 9.39,-78.51 0,-52.05 -9.39,-78.51 -9.39,-26.45 -33.28,-26.45 -24.32,0 -33.28,26.45 -8.96,26.45 -8.96,78.51z"
      android:fillColor="#000000"/>
</vector>

A app/src/main/res/drawable/baseline_5_hour_timer.xml => app/src/main/res/drawable/baseline_5_hour_timer.xml +17 -0
@@ 0,0 1,17 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="960"
    android:viewportHeight="960">
  <path
      android:pathData="m381.45,481.69q20.8,0 37.12,8 16.32,8 25.6,23.36 9.6,15.36 9.6,38.08 0,37.12 -23.04,58.56 -23.04,21.12 -68.16,21.12 -17.92,0 -33.92,-3.2 -15.68,-3.2 -27.52,-9.28v-41.6q11.84,6.08 28.48,10.56 16.64,4.16 31.36,4.16 21.44,0 32.64,-8.64 11.52,-8.96 11.52,-27.52 0,-34.56 -45.76,-34.56 -8.96,0 -18.56,1.92 -9.6,1.6 -16,3.2l-19.2,-10.24 8.64,-116.48h123.84v40.96h-81.6l-4.16,44.8q5.44,-0.96 11.52,-1.92 6.4,-1.28 17.6,-1.28z"
      android:strokeWidth="0"
      android:fillColor="#000000"/>
  <path
      android:pathData="m540.81,434.01c0,8.53 -0.32,16.75 -0.96,24.64 -0.43,7.89 -0.85,13.44 -1.28,16.64h2.56c5.55,-8.96 12.69,-15.47 21.44,-19.52 8.75,-4.05 18.45,-6.08 29.12,-6.08 18.99,0 34.13,5.12 45.44,15.36 11.52,10.03 17.28,26.24 17.28,48.64v113.92h-47.68v-102.08c0,-25.17 -9.39,-37.76 -28.16,-37.76 -14.29,0 -24.21,5.01 -29.76,15.04 -5.33,9.81 -8,24 -8,42.56v82.24h-47.68v-228.82h47.68z"
      android:strokeWidth="0"
      android:fillColor="#000000"/>
  <path
      android:pathData="m360,120v-80h240v80zM480,880c-49.33,0 -95.83,-9.5 -139.5,-28.5 -43.67,-19 -81.83,-44.83 -114.5,-77.5 -32.67,-32.67 -58.5,-70.83 -77.5,-114.5 -19,-43.67 -28.5,-90.17 -28.5,-139.5 0,-49.33 9.5,-95.83 28.5,-139.5 19,-43.67 44.83,-81.83 77.5,-114.5 32.67,-32.67 70.83,-58.5 114.5,-77.5 43.67,-19 90.17,-28.5 139.5,-28.5 41.33,0 81,6.67 119,20 38,13.33 73.67,32.67 107,58l56,-56 56,56 -56,56c25.33,33.33 44.67,69 58,107 13.33,38 20,77.67 20,119 0,49.33 -9.5,95.83 -28.5,139.5 -19,43.67 -44.83,81.83 -77.5,114.5 -32.67,32.67 -70.83,58.5 -114.5,77.5 -43.67,19 -90.17,28.5 -139.5,28.5zM480,800c77.33,0 143.33,-27.33 198,-82 54.67,-54.67 82,-120.67 82,-198 0,-77.33 -27.33,-143.33 -82,-198 -54.67,-54.67 -120.67,-82 -198,-82 -77.33,0 -143.33,27.33 -198,82 -54.67,54.67 -82,120.67 -82,198 0,77.33 27.33,143.33 82,198 54.67,54.67 120.67,82 198,82z"
      android:fillColor="#000000"/>
</vector>

A app/src/main/res/drawable/baseline_60_min_timer.xml => app/src/main/res/drawable/baseline_60_min_timer.xml +15 -0
@@ 0,0 1,15 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="960"
    android:viewportHeight="960">
  <path
      android:pathData="m360,120v-80h240v80zM480,880c-49.33,0 -95.83,-9.5 -139.5,-28.5 -43.67,-19 -81.83,-44.83 -114.5,-77.5 -32.67,-32.67 -58.5,-70.83 -77.5,-114.5 -19,-43.67 -28.5,-90.17 -28.5,-139.5 0,-49.33 9.5,-95.83 28.5,-139.5 19,-43.67 44.83,-81.83 77.5,-114.5 32.67,-32.67 70.83,-58.5 114.5,-77.5 43.67,-19 90.17,-28.5 139.5,-28.5 41.33,0 81,6.67 119,20 38,13.33 73.67,32.67 107,58l56,-56 56,56 -56,56c25.33,33.33 44.67,69 58,107 13.33,38 20,77.67 20,119 0,49.33 -9.5,95.83 -28.5,139.5 -19,43.67 -44.83,81.83 -77.5,114.5 -32.67,32.67 -70.83,58.5 -114.5,77.5 -43.67,19 -90.17,28.5 -139.5,28.5zM480,800c77.33,0 143.33,-27.33 198,-82 54.67,-54.67 82,-120.67 82,-198 0,-77.33 -27.33,-143.33 -82,-198 -54.67,-54.67 -120.67,-82 -198,-82 -77.33,0 -143.33,27.33 -198,82 -54.67,54.67 -82,120.67 -82,198 0,77.33 27.33,143.33 82,198 54.67,54.67 120.67,82 198,82z"
      android:fillColor="#000000"/>
  <path
      android:pathData="m240.86,541.97q0,-26.45 3.84,-52.05 3.84,-25.6 13.23,-48.21 9.81,-23.04 26.88,-40.53 17.49,-17.92 43.95,-27.73 26.88,-10.24 64.85,-10.24 8.96,0 20.91,0.85 11.95,0.43 20.05,2.13L434.57,417.81q-8.11,-2.13 -17.92,-2.99 -9.39,-1.28 -18.77,-1.28 -37.97,0 -58.88,11.95 -20.48,11.95 -29.01,33.71 -8.53,21.33 -9.81,49.49l2.56,0Q311.26,493.76 327.05,483.52 343.26,473.28 368.86,473.28q40.11,0 63.57,25.17 23.47,25.17 23.47,71.25 0,49.49 -28.16,77.65Q400.01,675.52 352.22,675.52q-31.15,0 -56.32,-14.08Q270.73,646.93 255.79,617.49 240.86,587.62 240.86,541.97ZM350.94,623.89q18.77,0 30.72,-12.8 11.95,-13.23 11.95,-40.53 0,-22.19 -10.24,-34.99 -10.24,-12.8 -31.15,-12.8 -14.08,0 -24.75,6.4 -10.67,5.97 -16.64,15.79 -5.97,9.81 -5.97,20.05 0,14.08 5.12,27.73 5.12,13.23 15.36,22.19 10.67,8.96 25.6,8.96z"
      android:fillColor="#000000"/>
  <path
      android:pathData="m710.62,518.93q0,49.07 -10.67,84.05 -10.24,34.99 -33.71,53.76Q643.21,675.52 603.96,675.52q-55.04,0 -80.64,-41.39 -25.6,-41.81 -25.6,-115.2 0,-49.49 10.24,-84.48 10.24,-34.99 33.71,-53.76 23.47,-18.77 62.29,-18.77 54.61,0 80.64,41.39 26.03,41.39 26.03,115.63zM561.71,518.93q0,52.05 8.96,78.51 8.96,26.03 33.28,26.03 23.89,0 33.28,-26.03 9.39,-26.03 9.39,-78.51 0,-52.05 -9.39,-78.51 -9.39,-26.45 -33.28,-26.45 -24.32,0 -33.28,26.45 -8.96,26.45 -8.96,78.51z"
      android:fillColor="#000000"/>
</vector>

A app/src/main/res/drawable/baseline_arrow_up_24.xml => app/src/main/res/drawable/baseline_arrow_up_24.xml +9 -0
@@ 0,0 1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="960"
    android:viewportHeight="960">
  <path
      android:pathData="m296,736 l-56,-56 240,-240 240,240 -56,56 -184,-183 -184,183ZM296,496 L240,440 480,200 720,440 664,496 480,313 296,496Z"
      android:fillColor="#000000"/>
</vector>

A app/src/main/res/drawable/baseline_double_arrow_down.xml => app/src/main/res/drawable/baseline_double_arrow_down.xml +12 -0
@@ 0,0 1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="960"
    android:viewportHeight="960">
  <path
      android:pathData="m664,229.06 l56,56 -240,240 -240,-240 56,-56 184,183z"
      android:fillColor="#000000"/>
  <path
      android:pathData="m664,434.94 l56,56 -240,240 -240,-240 56,-56 184,183z"
      android:fillColor="#000000"/>
</vector>

A app/src/main/res/drawable/baseline_double_arrow_up.xml => app/src/main/res/drawable/baseline_double_arrow_up.xml +12 -0
@@ 0,0 1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="960"
    android:viewportHeight="960">
  <path
      android:pathData="m296,730.94 l-56,-56 240,-240 240,240 -56,56 -184,-183z"
      android:fillColor="#000000"/>
  <path
      android:pathData="m296,525.06 l-56,-56 240,-240 240,240 -56,56 -184,-183z"
      android:fillColor="#000000"/>
</vector>

A app/src/main/res/drawable/baseline_reset_24.xml => app/src/main/res/drawable/baseline_reset_24.xml +9 -0
@@ 0,0 1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="960"
    android:viewportHeight="960">
  <path
      android:pathData="M440,838q-121,-15 -200.5,-105.5T160,520q0,-66 26,-126.5T260,288l57,57q-38,34 -57.5,79T240,520q0,88 56,155.5T440,758v80ZM520,838v-80q87,-16 143.5,-83T720,520q0,-100 -70,-170t-170,-70h-3l44,44 -56,56 -140,-140 140,-140 56,56 -44,44h3q134,0 227,93t93,227q0,121 -79.5,211.5T520,838Z"
      android:fillColor="#000000"/>
</vector>

A app/src/main/res/drawable/baseline_shield_24.xml => app/src/main/res/drawable/baseline_shield_24.xml +9 -0
@@ 0,0 1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="960"
    android:viewportHeight="960">
  <path
      android:pathData="M480,880q-139,-35 -229.5,-159.5T160,444v-244l320,-120 320,120v244q0,152 -90.5,276.5T480,880ZM480,796q104,-33 172,-132t68,-220v-189l-240,-90 -240,90v189q0,121 68,220t172,132ZM480,480Z"
      android:fillColor="#000000"/>
</vector>

A app/src/main/res/drawable/baseline_single_arrow_down.xml => app/src/main/res/drawable/baseline_single_arrow_down.xml +9 -0
@@ 0,0 1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="960"
    android:viewportHeight="960">
  <path
      android:pathData="m664,332 l56,56 -240,240 -240,-240 56,-56 184,183z"
      android:fillColor="#000000"/>
</vector>

A app/src/main/res/drawable/baseline_single_arrow_up.xml => app/src/main/res/drawable/baseline_single_arrow_up.xml +9 -0
@@ 0,0 1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="960"
    android:viewportHeight="960">
  <path
      android:pathData="m296,628 l-56,-56 240,-240 240,240 -56,56 -184,-183z"
      android:fillColor="#000000"/>
</vector>

A app/src/main/res/drawable/baseline_triple_arrow_down.xml => app/src/main/res/drawable/baseline_triple_arrow_down.xml +15 -0
@@ 0,0 1,15 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="960"
    android:viewportHeight="960">
  <path
      android:pathData="m664,332 l56,56 -240,240 -240,-240 56,-56 184,183z"
      android:fillColor="#000000"/>
  <path
      android:pathData="m664,537.89 l56,56 -240,240 -240,-240 56,-56 184,183z"
      android:fillColor="#000000"/>
  <path
      android:pathData="m664,126.11 l56,56 -240,240 -240,-240 56,-56 184,183z"
      android:fillColor="#000000"/>
</vector>

A app/src/main/res/drawable/baseline_triple_arrow_up.xml => app/src/main/res/drawable/baseline_triple_arrow_up.xml +15 -0
@@ 0,0 1,15 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="960"
    android:viewportHeight="960">
  <path
      android:pathData="m296,628 l-56,-56 240,-240 240,240 -56,56 -184,-183z"
      android:fillColor="#000000"/>
  <path
      android:pathData="m296,422.11 l-56,-56 240,-240 240,240 -56,56 -184,-183z"
      android:fillColor="#000000"/>
  <path
      android:pathData="m296,833.89 l-56,-56 240,-240 240,240 -56,56 -184,-183z"
      android:fillColor="#000000"/>
</vector>

M gradle/libs.versions.toml => gradle/libs.versions.toml +4 -4
@@ 1,18 1,18 @@
[versions]
agp = "8.7.3"
datastore = "1.1.1"
datastore = "1.1.2"
kotlin = "2.0.0"
coreKtx = "1.15.0"
junit = "4.13.2"
junitVersion = "1.2.1"
espressoCore = "3.6.1"
lifecycleRuntimeKtx = "2.8.7"
activityCompose = "1.9.3"
composeBom = "2024.04.01"
activityCompose = "1.10.0"
composeBom = "2025.01.00"
protobufGradlePlugin = "0.9.4"
protobufJavalite = "4.27.0"
roomRuntime = "2.6.1"
vbNfcReader = "0.1.0"
vbNfcReader = "0.2.0-SNAPSHOT"
dimReader = "2.1.0"

[libraries]