~cytrogen/vbhelper

dd893a08dab42ecf261c59b0f6529159343652cd — nacabaro 1 year, 3 months ago 7f22650 + cac5198
Merge pull request #15 from nacabaro/ui/home_screen

A few things, again...
M app/src/main/java/com/github/nacabaro/vbhelper/daos/CharacterDao.kt => app/src/main/java/com/github/nacabaro/vbhelper/daos/CharacterDao.kt +12 -0
@@ 5,6 5,7 @@ import androidx.room.Insert
import androidx.room.Query
import com.github.nacabaro.vbhelper.domain.Character
import com.github.nacabaro.vbhelper.domain.Sprites
import com.github.nacabaro.vbhelper.dtos.CharacterDtos

@Dao
interface CharacterDao {


@@ 25,4 26,15 @@ interface CharacterDao {

    @Query("SELECT * FROM Sprites")
    suspend fun getAllSprites(): List<Sprites>

    @Query("""
        SELECT 
            d.dimId as cardId,
            c.monIndex as charId
        FROM Character c
        JOIN UserCharacter uc ON c.id = uc.charId
        JOIN Dim d ON c.dimId = d.id
        WHERE uc.id = :charId
    """)
    suspend fun getCharacterInfo(charId: Long): CharacterDtos.DiMInfo
}
\ 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 +4 -1
@@ 21,7 21,7 @@ interface UserCharacterDao {
    fun insertTransformationHistory(vararg transformationHistory: TransformationHistory)

    @Query("SELECT * FROM TransformationHistory WHERE monId = :monId")
    fun getTransformationHistory(monId: Int): List<TransformationHistory>
    fun getTransformationHistory(monId: Long): List<TransformationHistory>

    @Query("""
        SELECT


@@ 36,4 36,7 @@ interface UserCharacterDao {

    @Query("SELECT * FROM UserCharacter WHERE id = :id")
    suspend fun getCharacter(id: Long): UserCharacter

    @Query("SELECT * FROM BECharacterData WHERE id = :id")
    suspend fun getBeData(id: Long): BECharacterData
}
\ No newline at end of file

M app/src/main/java/com/github/nacabaro/vbhelper/dtos/CharacterDtos.kt => app/src/main/java/com/github/nacabaro/vbhelper/dtos/CharacterDtos.kt +5 -1
@@ 1,6 1,5 @@
package com.github.nacabaro.vbhelper.dtos

import androidx.room.PrimaryKey
import com.github.cfogrady.vbnfc.data.NfcCharacter
import com.github.nacabaro.vbhelper.domain.DeviceType



@@ 29,4 28,9 @@ object CharacterDtos {
        val spriteWidth: Int,
        val spriteHeight: Int
    )

    data class DiMInfo(
        val cardId: Int,
        val charId: 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 +16 -10
@@ 35,45 35,51 @@ fun AppNavigation(
    ) { contentPadding ->
        NavHost(
            navController = navController,
            startDestination = BottomNavItem.Home.route,
            startDestination = NavigationItems.Home.route,
            modifier = Modifier
                .padding(contentPadding)
        ) {
            composable(BottomNavItem.Battles.route) {
            composable(NavigationItems.Battles.route) {
                BattlesScreen()
            }
            composable(BottomNavItem.Home.route) {
            composable(NavigationItems.Home.route) {
                HomeScreen(
                    navController = navController
                )
            }
            composable(BottomNavItem.Storage.route) {
                StorageScreen()
            composable(NavigationItems.Storage.route) {
                StorageScreen(
                    navController = navController
                )
            }
            composable(BottomNavItem.Scan.route) {
            composable(NavigationItems.Scan.route) {
                val characterIdString = it.arguments?.getString("characterId")
                val characterId = characterIdString?.toLongOrNull()

                ScanScreen(
                    navController = navController,
                    scanScreenController = applicationNavigationHandlers.scanScreenController,
                    characterId = characterId
                )
            }
            composable(BottomNavItem.Dex.route) {
            composable(NavigationItems.Dex.route) {
                DexScreen(
                    navController = navController
                )
            }
            composable(BottomNavItem.Settings.route) {
            composable(NavigationItems.Settings.route) {
                SettingsScreen(
                    navController = navController,
                    settingsScreenController = applicationNavigationHandlers.settingsScreenController,
                    onClickImportCard = onClickImportCard
                )
            }
            composable(BottomNavItem.Viewer.route) {
            composable(NavigationItems.Viewer.route) {
                SpriteViewer(
                    navController = navController
                )
            }
            composable(BottomNavItem.CardView.route) {
            composable(NavigationItems.CardView.route) {
                val dimId = it.arguments?.getString("dimId")
                Log.d("dimId", dimId.toString())
                if (dimId != null) {

D app/src/main/java/com/github/nacabaro/vbhelper/navigation/BottomNavItem.kt => app/src/main/java/com/github/nacabaro/vbhelper/navigation/BottomNavItem.kt +0 -18
@@ 1,18 0,0 @@
package com.github.nacabaro.vbhelper.navigation

import com.github.nacabaro.vbhelper.R

sealed class BottomNavItem (
    var route: String,
    var icon: Int,
    var label: String
) {
    object Scan : BottomNavItem("Scan", R.drawable.baseline_nfc_24, "Scan")
    object Battles : BottomNavItem("Battle", R.drawable.baseline_swords_24, "Battle")
    object Home : BottomNavItem("Home", R.drawable.baseline_cottage_24, "Home")
    object Dex : BottomNavItem("Dex", R.drawable.baseline_menu_book_24, "Dex")
    object Storage : BottomNavItem("Storage", R.drawable.baseline_catching_pokemon_24, "Storage")
    object Settings : BottomNavItem("Settings", R.drawable.baseline_settings_24, "Settings")
    object Viewer : BottomNavItem("Viewer", R.drawable.baseline_image_24, "Viewer")
    object CardView : BottomNavItem("Card/{dimId}", R.drawable.baseline_image_24, "Card")
}
\ No newline at end of file

M app/src/main/java/com/github/nacabaro/vbhelper/navigation/BottomNavigationBar.kt => app/src/main/java/com/github/nacabaro/vbhelper/navigation/BottomNavigationBar.kt +5 -5
@@ 13,11 13,11 @@ import androidx.navigation.compose.currentBackStackEntryAsState
@Composable
fun BottomNavigationBar(navController: NavController) {
    val items = listOf(
        BottomNavItem.Scan,
        BottomNavItem.Battles,
        BottomNavItem.Home,
        BottomNavItem.Dex,
        BottomNavItem.Storage,
        NavigationItems.Scan,
        NavigationItems.Battles,
        NavigationItems.Home,
        NavigationItems.Dex,
        NavigationItems.Storage,
    )
    NavigationBar {
        val currentBackStackEntry = navController.currentBackStackEntryAsState()

A app/src/main/java/com/github/nacabaro/vbhelper/navigation/NavigationItems.kt => app/src/main/java/com/github/nacabaro/vbhelper/navigation/NavigationItems.kt +18 -0
@@ 0,0 1,18 @@
package com.github.nacabaro.vbhelper.navigation

import com.github.nacabaro.vbhelper.R

sealed class NavigationItems (
    var route: String,
    var icon: Int,
    var label: String
) {
    object Scan : NavigationItems("Scan/{characterId}", R.drawable.baseline_nfc_24, "Scan")
    object Battles : NavigationItems("Battle", R.drawable.baseline_swords_24, "Battle")
    object Home : NavigationItems("Home", R.drawable.baseline_cottage_24, "Home")
    object Dex : NavigationItems("Dex", R.drawable.baseline_menu_book_24, "Dex")
    object Storage : NavigationItems("Storage", R.drawable.baseline_catching_pokemon_24, "Storage")
    object Settings : NavigationItems("Settings", R.drawable.baseline_settings_24, "Settings")
    object Viewer : NavigationItems("Viewer", R.drawable.baseline_image_24, "Viewer")
    object CardView : NavigationItems("Card/{dimId}", R.drawable.baseline_image_24, "Card")
}
\ No newline at end of file

M app/src/main/java/com/github/nacabaro/vbhelper/screens/DexScreen.kt => app/src/main/java/com/github/nacabaro/vbhelper/screens/DexScreen.kt +3 -3
@@ 19,7 19,7 @@ import com.github.nacabaro.vbhelper.components.DexDiMEntry
import com.github.nacabaro.vbhelper.components.TopBanner
import com.github.nacabaro.vbhelper.di.VBHelper
import com.github.nacabaro.vbhelper.domain.Dim
import com.github.nacabaro.vbhelper.navigation.BottomNavItem
import com.github.nacabaro.vbhelper.navigation.NavigationItems
import com.github.nacabaro.vbhelper.source.DexRepository
import kotlinx.coroutines.launch



@@ 45,7 45,7 @@ fun DexScreen(
            TopBanner(
                text = "Discovered Digimon",
                onGearClick = {
                    navController.navigate(BottomNavItem.Viewer.route)
                    navController.navigate(NavigationItems.Viewer.route)
                }
            )
        }


@@ 65,7 65,7 @@ fun DexScreen(
                    onClick = {
                        navController
                            .navigate(
                                BottomNavItem
                                NavigationItems
                                    .CardView.route
                                    .replace("{dimId}", "${it.id}")
                            )

M app/src/main/java/com/github/nacabaro/vbhelper/screens/HomeScreen.kt => app/src/main/java/com/github/nacabaro/vbhelper/screens/HomeScreen.kt +2 -2
@@ 8,7 8,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.navigation.NavController
import com.github.nacabaro.vbhelper.components.TopBanner
import com.github.nacabaro.vbhelper.navigation.BottomNavItem
import com.github.nacabaro.vbhelper.navigation.NavigationItems

@Composable
fun HomeScreen(


@@ 19,7 19,7 @@ fun HomeScreen(
            TopBanner(
                text = "VB Helper",
                onGearClick = {
                    navController.navigate(BottomNavItem.Settings.route)
                    navController.navigate(NavigationItems.Settings.route)
                }
            )
        }

M app/src/main/java/com/github/nacabaro/vbhelper/screens/StorageScreen.kt => app/src/main/java/com/github/nacabaro/vbhelper/screens/StorageScreen.kt +32 -12
@@ 1,11 1,11 @@
package com.github.nacabaro.vbhelper.screens

import android.util.Log
import androidx.compose.foundation.Image
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.layout.size


@@ 21,11 21,9 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier


@@ 34,18 32,22 @@ 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.launch


@Composable
fun StorageScreen() {
fun StorageScreen(
    navController: NavController
) {
    val coroutineScope = rememberCoroutineScope()
    val application = LocalContext.current.applicationContext as VBHelper
    val storageRepository = StorageRepository(application.container.db)


@@ 96,14 98,24 @@ fun StorageScreen() {
                    ),
                    modifier = Modifier
                        .padding(8.dp)
                        .size(96.dp)

                        .size(96.dp),
                    onClick = {
                        selectedCharacter = index.id
                    }
                )

                if (selectedCharacter != null) {
                    StorageDialog(
                        characterId = selectedCharacter!!,
                        onDismissRequest = { selectedCharacter = null }
                        onDismissRequest = { selectedCharacter = null },
                        onSendToBracelet = {
                            navController.navigate(
                                NavigationItems.Scan.route.replace(
                                    "{characterId}",
                                    selectedCharacter.toString()
                                )
                            )
                        }
                    )
                }
            }


@@ 114,7 126,8 @@ fun StorageScreen() {
@Composable
fun StorageDialog(
    characterId: Long,
    onDismissRequest: () -> Unit
    onDismissRequest: () -> Unit,
    onSendToBracelet: () -> Unit
) {
    val coroutineScope = rememberCoroutineScope()
    val application = LocalContext.current.applicationContext as VBHelper


@@ 149,10 162,17 @@ fun StorageDialog(
                            .padding(8.dp)
                    )
                }
                Button(
                    onClick = onDismissRequest
                ) {
                    Text(text = "Close")
                Row {
                    Button(
                        onClick = onSendToBracelet
                    ) {
                        Text(text = "Send to bracelet")
                    }
                    Button(
                        onClick = onDismissRequest
                    ) {
                        Text(text = "Close")
                    }
                }
            }
        }

M app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/ReadingCharacter.kt => app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/ReadingCharacter.kt +2 -1
@@ 15,11 15,12 @@ import com.github.nacabaro.vbhelper.components.TopBanner

@Composable
fun ReadingCharacterScreen(
    topBannerText: String,
    onClickCancel: () -> Unit,
) {
    Scaffold (
        topBar = {
            TopBanner("Reading Character")
            TopBanner(topBannerText)
        }
    ) { innerPadding ->
        Column (

M app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/ScanScreen.kt => app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/ScanScreen.kt +116 -20
@@ 12,6 12,7 @@ import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf


@@ 25,25 26,48 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.navigation.NavController
import androidx.navigation.compose.rememberNavController
import com.github.cfogrady.vbnfc.data.NfcCharacter
import com.github.nacabaro.vbhelper.ActivityLifecycleListener
import com.github.nacabaro.vbhelper.components.TopBanner
import com.github.nacabaro.vbhelper.navigation.BottomNavItem
import com.github.nacabaro.vbhelper.di.VBHelper
import com.github.nacabaro.vbhelper.navigation.NavigationItems
import com.github.nacabaro.vbhelper.source.StorageRepository
import com.github.nacabaro.vbhelper.source.isMissingSecrets
import com.github.nacabaro.vbhelper.source.proto.Secrets
import com.github.nacabaro.vbhelper.utils.characterToNfc
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.withContext

const val SCAN_SCREEN_ACTIVITY_LIFECYCLE_LISTENER = "SCAN_SCREEN_ACTIVITY_LIFECYCLE_LISTENER"

@Composable
fun ScanScreen(
    navController: NavController,
    characterId: Long?,
    scanScreenController: ScanScreenController,
) {
    val secrets by scanScreenController.secretsFlow.collectAsState(null)
    var readingScreen by remember { mutableStateOf(false) }
    var writingScreen by remember { mutableStateOf(false) }
    var isDoneReadingCharacter by remember { mutableStateOf(false) }
    var isDoneSendingCard by remember { mutableStateOf(false) }
    var isDoneWritingCharacter by remember { mutableStateOf(false) }

    DisposableEffect(readingScreen) {
    val application = LocalContext.current.applicationContext as VBHelper
    val storageRepository = StorageRepository(application.container.db)
    var nfcCharacter by remember { mutableStateOf<NfcCharacter?>(null) }

    val context = LocalContext.current
    LaunchedEffect(storageRepository) {
        withContext(Dispatchers.IO) {
            if(characterId != null) {
                nfcCharacter = characterToNfc(context, characterId)
            }
        }
    }

    DisposableEffect(readingScreen || writingScreen) {
        if(readingScreen) {
            scanScreenController.registerActivityLifecycleListener(SCAN_SCREEN_ACTIVITY_LIFECYCLE_LISTENER, object: ActivityLifecycleListener {
                override fun onPause() {


@@ 60,9 84,39 @@ fun ScanScreen(
            scanScreenController.onClickRead(secrets!!) {
                isDoneReadingCharacter = true
            }
        } else if (writingScreen) {
            scanScreenController.registerActivityLifecycleListener(
                SCAN_SCREEN_ACTIVITY_LIFECYCLE_LISTENER,
                object : ActivityLifecycleListener {
                    override fun onPause() {
                        scanScreenController.cancelRead()
                    }

                    override fun onResume() {
                        if (!isDoneSendingCard) {
                            scanScreenController.onClickCheckCard(secrets!!, nfcCharacter!!) {
                                isDoneSendingCard = true
                            }
                        } else if (!isDoneWritingCharacter) {
                            scanScreenController.onClickWrite(secrets!!, nfcCharacter!!) {
                                isDoneWritingCharacter = true
                            }
                        }
                    }
                }
            )
            if (!isDoneSendingCard) {
                scanScreenController.onClickCheckCard(secrets!!, nfcCharacter!!) {
                    isDoneSendingCard = true
                }
            } else if (!isDoneWritingCharacter) {
                scanScreenController.onClickWrite(secrets!!, nfcCharacter!!) {
                    isDoneWritingCharacter = true
                }
            }
        }
        onDispose {
            if(readingScreen) {
            if(readingScreen || writingScreen) {
                scanScreenController.unregisterActivityLifecycleListener(SCAN_SCREEN_ACTIVITY_LIFECYCLE_LISTENER)
                scanScreenController.cancelRead()
            }


@@ 71,33 125,68 @@ fun ScanScreen(

    if (isDoneReadingCharacter) {
        readingScreen = false
        navController.navigate(BottomNavItem.Home.route)
        navController.navigate(NavigationItems.Home.route)
    } else if (isDoneSendingCard && isDoneWritingCharacter) {
        writingScreen = false
        navController.navigate(NavigationItems.Home.route)
    }

    if (readingScreen) {
        ReadingCharacterScreen {
        ReadingCharacterScreen("Reading character") {
            readingScreen = false
            scanScreenController.cancelRead()
        }
    } else if (writingScreen) {
        if (!isDoneSendingCard) {
            ReadingCharacterScreen("Sending card") {
                isDoneSendingCard = true
                scanScreenController.cancelRead()
            }
        } else if (!isDoneWritingCharacter) {
            ReadingCharacterScreen("Writing character") {
                isDoneWritingCharacter = true
                writingScreen = false
                scanScreenController.cancelRead()
            }
        }
    } else {
        val context = LocalContext.current
        ChooseConnectOption(
            onClickRead = {
                if(secrets == null) {
                    Toast.makeText(context, "Secrets is not yet initialized. Try again.", Toast.LENGTH_SHORT).show()
                } else if(secrets?.isMissingSecrets() == true) {
                    Toast.makeText(context, "Secrets not yet imported. Go to Settings and Import APK", Toast.LENGTH_SHORT).show()
                } else {
                    readingScreen = true // kicks off nfc adapter in DisposableEffect
            onClickRead = when {
                characterId != null -> null
                else -> {
                    {
                        if(secrets == null) {
                            Toast.makeText(context, "Secrets is not yet initialized. Try again.", Toast.LENGTH_SHORT).show()
                        } else if(secrets?.isMissingSecrets() == true) {
                            Toast.makeText(context, "Secrets not yet imported. Go to Settings and Import APK", Toast.LENGTH_SHORT).show()
                        } else {
                            readingScreen = true // kicks off nfc adapter in DisposableEffect
                        }
                    }
                }
            },
            onClickWrite = when {
                nfcCharacter == null -> null
                else -> {
                    {
                        if(secrets == null) {
                            Toast.makeText(context, "Secrets is not yet initialized. Try again.", Toast.LENGTH_SHORT).show()
                        } else if(secrets?.isMissingSecrets() == true) {
                            Toast.makeText(context, "Secrets not yet imported. Go to Settings and Import APK", Toast.LENGTH_SHORT).show()
                        } else {
                            writingScreen = true // kicks off nfc adapter in DisposableEffect
                        }
                    }
                }
            }
        )
    }
}

@Composable
private fun ChooseConnectOption(
    onClickRead: () -> Unit,
fun ChooseConnectOption(
    onClickRead: (() -> Unit)? = null,
    onClickWrite: (() -> Unit)? = null,
) {
    Scaffold(
        topBar = { TopBanner(text = "Scan a Vital Bracelet") }


@@ 111,12 200,14 @@ private fun ChooseConnectOption(
        ) {
            ScanButton(
                text = "Vital Bracelet to App",
                onClick = onClickRead,
                disabled = onClickRead == null,
                onClick = onClickRead?: {  },
            )
            Spacer(modifier = Modifier.height(16.dp))
            ScanButton(
                text = "App to Vital Bracelet",
                onClick = {}
                disabled = onClickWrite == null,
                onClick = onClickWrite?: {  },
            )
        }
    }


@@ 127,11 218,13 @@ private fun ChooseConnectOption(
fun ScanButton(
    text: String,
    onClick: () -> Unit,
    modifier: Modifier = Modifier
    modifier: Modifier = Modifier,
    disabled: Boolean = false,
) {
    Button(
        onClick = onClick,
        modifier = modifier
        modifier = modifier,
        enabled = !disabled,
    ) {
        Text(
            text = text,


@@ 157,7 250,10 @@ fun ScanScreenPreview() {

            }
            override fun onClickRead(secrets: Secrets, onComplete: ()->Unit) {}
            override fun onClickCheckCard(secrets: Secrets, nfcCharacter: NfcCharacter, onComplete: () -> Unit) {}
            override fun onClickWrite(secrets: Secrets, nfcCharacter: NfcCharacter, onComplete: () -> Unit) {}
            override fun cancelRead() {}
        }
        },
        characterId = null
    )
}
\ No newline at end of file

M app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/ScanScreenController.kt => app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/ScanScreenController.kt +4 -0
@@ 1,5 1,6 @@
package com.github.nacabaro.vbhelper.screens.scanScreen

import com.github.cfogrady.vbnfc.data.NfcCharacter
import com.github.nacabaro.vbhelper.ActivityLifecycleListener
import com.github.nacabaro.vbhelper.source.proto.Secrets
import kotlinx.coroutines.flow.Flow


@@ 7,6 8,9 @@ import kotlinx.coroutines.flow.Flow
interface ScanScreenController {
    val secretsFlow: Flow<Secrets>
    fun onClickRead(secrets: Secrets, onComplete: ()->Unit)
    fun onClickCheckCard(secrets: Secrets, nfcCharacter: NfcCharacter, onComplete: () -> Unit)
    fun onClickWrite(secrets: Secrets, nfcCharacter: NfcCharacter, onComplete: () -> Unit)

    fun cancelRead()

    fun registerActivityLifecycleListener(key: String, activityLifecycleListener: ActivityLifecycleListener)

M app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/ScanScreenControllerImpl.kt => app/src/main/java/com/github/nacabaro/vbhelper/screens/scanScreen/ScanScreenControllerImpl.kt +24 -0
@@ 109,6 109,30 @@ class ScanScreenControllerImpl(
        }
    }

    override fun onClickWrite(
        secrets: Secrets,
        nfcCharacter: NfcCharacter,
        onComplete: () -> Unit
    ) {
        handleTag(secrets) { tagCommunicator ->
            tagCommunicator.sendCharacter(nfcCharacter)
            onComplete.invoke()
            "Sent character successfully!"
        }
    }

    override fun onClickCheckCard(
        secrets: Secrets,
        nfcCharacter: NfcCharacter,
        onComplete: () -> Unit
    ) {
        handleTag(secrets) { tagCommunicator ->
            tagCommunicator.prepareDIMForCharacter(nfcCharacter.dimId)
            onComplete.invoke()
            "Sent DIM successfully!"
        }
    }

    // EXTRACTED DIRECTLY FROM EXAMPLE APP
    private fun showWirelessSettings() {
        Toast.makeText(context, "NFC must be enabled", Toast.LENGTH_SHORT).show()

M app/src/main/java/com/github/nacabaro/vbhelper/source/StorageRepository.kt => app/src/main/java/com/github/nacabaro/vbhelper/source/StorageRepository.kt +14 -0
@@ 1,6 1,8 @@
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



@@ 14,4 16,16 @@ class StorageRepository (
    suspend fun getSingleCharacter(id: Long): UserCharacter {
        return db.userCharacterDao().getCharacter(id)
    }

    suspend fun getCharacterBeData(id: Long): BECharacterData {
        return db.userCharacterDao().getBeData(id)
    }

    fun getTransformationHistory(characterId: Long): List<TransformationHistory> {
        return db.userCharacterDao().getTransformationHistory(characterId)
    }

    suspend fun getCharacterData(id: Long): CharacterDtos.DiMInfo {
        return db.characterDao().getCharacterInfo(id)
    }
}
\ No newline at end of file

A app/src/main/java/com/github/nacabaro/vbhelper/utils/CharacterToNFCCharacter.kt => app/src/main/java/com/github/nacabaro/vbhelper/utils/CharacterToNFCCharacter.kt +86 -0
@@ 0,0 1,86 @@
package com.github.nacabaro.vbhelper.utils

import android.content.Context
import com.github.cfogrady.vbnfc.be.BENfcCharacter
import com.github.cfogrady.vbnfc.be.FirmwareVersion
import com.github.cfogrady.vbnfc.data.NfcCharacter
import com.github.nacabaro.vbhelper.di.VBHelper
import com.github.nacabaro.vbhelper.domain.DeviceType
import com.github.nacabaro.vbhelper.source.StorageRepository

suspend fun characterToNfc(context: Context, characterId: Long): NfcCharacter? {
    val app = context.applicationContext as VBHelper
    val database = app.container.db
    val storageRepository = StorageRepository(database)
    val userCharacter = storageRepository.getSingleCharacter(characterId)
    val characterInfo = storageRepository.getCharacterData(characterId)

    if (userCharacter.characterType == DeviceType.BEDevice) {
        val beData = storageRepository.getCharacterBeData(characterId)
        val transformationHistory = storageRepository
            .getTransformationHistory(characterId)
            .map {
                NfcCharacter.Transformation(
                    toCharIndex = it.toCharIndex.toUByte(),
                    year = it.year.toUShort(),
                    month = it.month.toUByte(),
                    day = it.day.toUByte()
                )
            }.toTypedArray()

        // Maybe this is the issue?
        val dummyVitalHistory = arrayOf<NfcCharacter.DailyVitals>()

        val nfcData = BENfcCharacter(
            dimId = characterInfo.cardId.toUShort(),
            charIndex = characterInfo.charId.toUShort(),
            stage = userCharacter.stage.toByte(),
            attribute = userCharacter.attribute,
            ageInDays = userCharacter.ageInDays.toByte(),
            nextAdventureMissionStage = userCharacter.nextAdventureMissionStage.toByte(),
            mood = userCharacter.mood.toByte(),
            vitalPoints = userCharacter.vitalPoints.toUShort(),
            itemEffectMentalStateValue = beData.itemEffectMentalStateValue.toByte(),
            itemEffectMentalStateMinutesRemaining = beData.itemEffectMentalStateMinutesRemaining.toByte(),
            itemEffectActivityLevelValue = beData.itemEffectActivityLevelValue.toByte(),
            itemEffectActivityLevelMinutesRemaining = beData.itemEffectActivityLevelMinutesRemaining.toByte(),
            itemEffectVitalPointsChangeValue = beData.itemEffectVitalPointsChangeValue.toByte(),
            itemEffectVitalPointsChangeMinutesRemaining = beData.itemEffectVitalPointsChangeMinutesRemaining.toByte(),
            transformationCountdownInMinutes = userCharacter.transformationCountdown.toUShort(),
            injuryStatus = userCharacter.injuryStatus,
            trainingPp = userCharacter.trophies.toUShort(),
            currentPhaseBattlesWon = userCharacter.currentPhaseBattlesWon.toUShort(),
            currentPhaseBattlesLost = userCharacter.currentPhaseBattlesLost.toUShort(),
            totalBattlesWon = userCharacter.totalBattlesWon.toUShort(),
            totalBattlesLost = userCharacter.totalBattlesLost.toUShort(),
            activityLevel = userCharacter.activityLevel.toByte(),
            heartRateCurrent = userCharacter.heartRateCurrent.toUByte(),
            transformationHistory = transformationHistory,
            vitalHistory = arrayOf(),
            appReserved1 = byteArrayOf(),
            appReserved2 = Array(2, { 0u }),
            trainingHp = beData.trainingHp.toUShort(),
            trainingAp = beData.trainingAp.toUShort(),
            trainingBp = beData.trainingBp.toUShort(),
            remainingTrainingTimeInMinutes = beData.remainingTrainingTimeInMinutes.toUShort(),
            abilityRarity = beData.abilityRarity,
            abilityType = beData.abilityType.toUShort(),
            abilityBranch = beData.abilityBranch.toUShort(),
            abilityReset = beData.abilityReset.toByte(),
            rank = beData.rank.toByte(),
            itemType = beData.itemType.toByte(),
            itemMultiplier = beData.itemMultiplier.toByte(),
            itemRemainingTime = beData.itemRemainingTime.toByte(),
            otp0 = byteArrayOf(8),
            otp1 = byteArrayOf(8),
            characterCreationFirmwareVersion = FirmwareVersion(
                minorVersion = beData.minorVersion.toByte(),
                majorVersion = beData.majorVersion.toByte()
            )
        )

        return nfcData
    }

    return null
}
\ No newline at end of file