Compare commits

...
7 Commits
Author SHA1 Message Date
nacabaro fa8641c874 Added logger
Logger is a class (AppLogger) that sits between Log class and allows me to store debug information inside the app.

This debug information can be seen at anytime from within the application settings, and it's not sent out to any server.
2026-05-19 22:29:41 +00:00
nacabaro 366f425539 Updated libraries
Moved library definitions to the catalog, and updated the catalog
2026-01-28 02:05:43 +01:00
nacabaro 37076a9bf0 VERSION UP 2026-01-25 20:00:55 +01:00
nacabaroandGitHub 8c99046edb Merge pull request #47 from nacabaro/specialMissions/fix
Fix Special Missions a bit more
2026-01-25 19:58:39 +01:00
nacabaro c990a495b7 Fix Special Missions a bit more
Now they should behave a bit better, and it will not allow the watch to have multiple missions of the same type, plus you can now delete special missions.
2026-01-25 19:58:03 +01:00
nacabaroandGitHub 0e61723db1 Merge pull request #46 from nacabaro/ui/cardIcon
Card icon in home screen
2026-01-25 17:53:30 +01:00
nacabaro 8815907563 Card icon in home screen
Together with character sprite
2026-01-25 17:35:54 +01:00
33 changed files with 484 additions and 180 deletions
+28 -20
View File
@@ -1,21 +1,23 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins { plugins {
alias(libs.plugins.android.application) alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android) alias(libs.plugins.kotlin.android)
alias(libs.plugins.kotlin.compose) alias(libs.plugins.kotlin.compose)
id("com.google.devtools.ksp") version "2.0.21-1.0.27" id("com.google.devtools.ksp") version "2.3.0"
id("com.google.protobuf") id("com.google.protobuf")
} }
android { android {
namespace = "com.github.nacabaro.vbhelper" namespace = "com.github.nacabaro.vbhelper"
compileSdk = 35 compileSdk = 36
defaultConfig { defaultConfig {
applicationId = "com.github.nacabaro.vbhelper" applicationId = "com.github.nacabaro.vbhelper"
minSdk = 28 minSdk = 28
targetSdk = 35 targetSdk = 36
versionCode = 1 versionCode = 1
versionName = "Alpha 0.6.2" versionName = "Alpha 0.6.3.2"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
} }
@@ -33,9 +35,7 @@ android {
sourceCompatibility = JavaVersion.VERSION_11 sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11 targetCompatibility = JavaVersion.VERSION_11
} }
kotlinOptions {
jvmTarget = "11"
}
buildFeatures { buildFeatures {
compose = true compose = true
} }
@@ -45,14 +45,17 @@ android {
} }
} }
kotlin {
compilerOptions {
jvmTarget = JvmTarget.JVM_11
}
}
protobuf { protobuf {
protoc { protoc {
artifact = "com.google.protobuf:protoc:4.27.0" artifact = "com.google.protobuf:protoc:4.27.0"
} }
// Generates the java Protobuf-lite code for the Protobufs in this project. See
// https://github.com/google/protobuf-gradle-plugin#customizing-protobuf-compilation
// for more information.
generateProtoTasks { generateProtoTasks {
all().forEach { task -> all().forEach { task ->
task.builtins { task.builtins {
@@ -68,10 +71,13 @@ dependencies {
implementation(libs.androidx.room.runtime) implementation(libs.androidx.room.runtime)
implementation(libs.vb.nfc.reader) implementation(libs.vb.nfc.reader)
implementation(libs.dim.reader) implementation(libs.dim.reader)
ksp(libs.androidx.room.compiler)
annotationProcessor(libs.androidx.room.compiler)
implementation(libs.androidx.core.ktx) implementation(libs.androidx.core.ktx)
implementation("androidx.room:room-ktx:2.6.1") implementation(libs.androidx.room.ktx)
implementation(libs.androidx.datastore.preferences)
implementation(libs.androidx.navigation.compose)
implementation(libs.material)
implementation(libs.protobuf.javalite)
implementation(libs.androidx.compose.material)
implementation(libs.androidx.lifecycle.runtime.ktx) implementation(libs.androidx.lifecycle.runtime.ktx)
implementation(libs.androidx.activity.compose) implementation(libs.androidx.activity.compose)
implementation(platform(libs.androidx.compose.bom)) implementation(platform(libs.androidx.compose.bom))
@@ -80,16 +86,18 @@ dependencies {
implementation(libs.androidx.ui.graphics) implementation(libs.androidx.ui.graphics)
implementation(libs.androidx.ui.tooling.preview) implementation(libs.androidx.ui.tooling.preview)
implementation(libs.androidx.material3) implementation(libs.androidx.material3)
implementation(libs.androidx.compose.material.icons.extended)
debugImplementation(libs.androidx.ui.tooling)
debugImplementation(libs.androidx.ui.test.manifest)
testImplementation(libs.junit) testImplementation(libs.junit)
ksp(libs.androidx.room.compiler)
annotationProcessor(libs.androidx.room.compiler)
androidTestImplementation(libs.androidx.junit) androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core) androidTestImplementation(libs.androidx.espresso.core)
androidTestImplementation(platform(libs.androidx.compose.bom)) androidTestImplementation(platform(libs.androidx.compose.bom))
androidTestImplementation(libs.androidx.ui.test.junit4) androidTestImplementation(libs.androidx.ui.test.junit4)
debugImplementation(libs.androidx.ui.tooling)
debugImplementation(libs.androidx.ui.test.manifest)
implementation("androidx.navigation:navigation-compose:2.7.0")
implementation("com.google.android.material:material:1.2.0")
implementation(libs.protobuf.javalite)
implementation("androidx.compose.material:material")
implementation("androidx.datastore:datastore-preferences:1.1.7")
} }
@@ -1,7 +1,6 @@
package com.github.nacabaro.vbhelper package com.github.nacabaro.vbhelper
import android.os.Bundle import android.os.Bundle
import android.util.Log
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge import androidx.activity.enableEdgeToEdge
@@ -68,13 +67,10 @@ class MainActivity : ComponentActivity() {
) )
} }
} }
Log.i("MainActivity", "Activity onCreated")
} }
override fun onPause() { override fun onPause() {
super.onPause() super.onPause()
Log.i("MainActivity", "onPause")
for(activityListener in onActivityLifecycleListeners) { for(activityListener in onActivityLifecycleListeners) {
activityListener.value.onPause() activityListener.value.onPause()
} }
@@ -82,7 +78,6 @@ class MainActivity : ComponentActivity() {
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
Log.i("MainActivity", "Resume")
for(activityListener in onActivityLifecycleListeners) { for(activityListener in onActivityLifecycleListeners) {
activityListener.value.onResume() activityListener.value.onResume()
} }
@@ -43,6 +43,7 @@ import androidx.compose.ui.res.stringResource
fun CharacterEntry( fun CharacterEntry(
icon: BitmapData, icon: BitmapData,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
cardIcon: BitmapData? = null,
obscure: Boolean = false, obscure: Boolean = false,
disabled: Boolean = false, disabled: Boolean = false,
shape: Shape = MaterialTheme.shapes.medium, shape: Shape = MaterialTheme.shapes.medium,
@@ -55,6 +56,7 @@ fun CharacterEntry(
val bitmap = remember (icon.bitmap) { val bitmap = remember (icon.bitmap) {
if(obscure) icon.getObscuredBitmap() else icon.getBitmap() if(obscure) icon.getObscuredBitmap() else icon.getBitmap()
} }
val iconSizeMultiplier = 3
val imageBitmap = remember(bitmap) { bitmap.asImageBitmap() } val imageBitmap = remember(bitmap) { bitmap.asImageBitmap() }
val density: Float = LocalContext.current.resources.displayMetrics.density val density: Float = LocalContext.current.resources.displayMetrics.density
val dpSize = (icon.width * multiplier / density).dp val dpSize = (icon.width * multiplier / density).dp
@@ -86,7 +88,24 @@ fun CharacterEntry(
}, },
modifier = Modifier modifier = Modifier
.size(dpSize) .size(dpSize)
.align(Alignment.BottomCenter)
) )
if (cardIcon != null) {
val bitmap = remember (icon.bitmap) { cardIcon.getBitmap() }
val iconBitmap = remember(bitmap) { bitmap.asImageBitmap() }
val dpSize = (icon.width * iconSizeMultiplier /density).dp
Image(
bitmap = iconBitmap,
contentDescription = "Card icon",
filterQuality = FilterQuality.None,
modifier = Modifier
.size(dpSize)
.align(Alignment.BottomEnd)
.padding(8.dp)
)
}
} }
} }
} }
@@ -133,7 +152,8 @@ fun ItemDisplay(
fun SpecialMissionsEntry( fun SpecialMissionsEntry(
specialMission: SpecialMissions, specialMission: SpecialMissions,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
onClickCard: () -> Unit = { }, onClickMission: (Long) -> Unit = { },
onClickCollect: (Long) -> Unit = { }
) { ) {
val textValue = when (specialMission.missionType) { val textValue = when (specialMission.missionType) {
SpecialMission.Type.NONE -> stringResource(R.string.special_mission_none) SpecialMission.Type.NONE -> stringResource(R.string.special_mission_none)
@@ -200,10 +220,12 @@ fun SpecialMissionsEntry(
Card( Card(
modifier = modifier, modifier = modifier,
shape = androidx.compose.material.MaterialTheme.shapes.small, shape = androidx.compose.material.MaterialTheme.shapes.small,
onClick = if (specialMission.status == SpecialMission.Status.COMPLETED || specialMission.status == SpecialMission.Status.FAILED) { onClick = if (specialMission.status == SpecialMission.Status.COMPLETED) {
onClickCard { onClickCollect(specialMission.id) }
} else { } else if (specialMission.status == SpecialMission.Status.UNAVAILABLE) {
{ } { }
} else {
{ onClickMission(specialMission.id) }
}, },
colors = CardDefaults.cardColors( colors = CardDefaults.cardColors(
containerColor = color containerColor = color
@@ -5,6 +5,7 @@ import androidx.room.Insert
import androidx.room.OnConflictStrategy import androidx.room.OnConflictStrategy
import androidx.room.Query import androidx.room.Query
import com.github.nacabaro.vbhelper.domain.card.Card import com.github.nacabaro.vbhelper.domain.card.Card
import com.github.nacabaro.vbhelper.dtos.CardDtos
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
@Dao @Dao
@@ -34,4 +35,15 @@ interface CardDao {
@Query("DELETE FROM Card WHERE id = :id") @Query("DELETE FROM Card WHERE id = :id")
suspend fun deleteCard(id: Long) suspend fun deleteCard(id: Long)
@Query("""
SELECT
c.logo as cardIcon,
c.logoWidth as cardIconWidth,
c.logoHeight as cardIconHeight
FROM Card c
JOIN CardCharacter cc ON cc.cardId = c.id
WHERE cc.id = :charaId
""")
fun getCardIconByCharaId(charaId: Long): Flow<CardDtos.CardIcon>
} }
@@ -2,6 +2,8 @@ package com.github.nacabaro.vbhelper.daos
import androidx.room.Dao import androidx.room.Dao
import androidx.room.Query import androidx.room.Query
import com.github.nacabaro.vbhelper.domain.device_data.SpecialMissions
import kotlinx.coroutines.flow.Flow
@Dao @Dao
interface SpecialMissionDao { interface SpecialMissionDao {
@@ -12,4 +14,11 @@ interface SpecialMissionDao {
WHERE id = :id WHERE id = :id
""") """)
suspend fun clearSpecialMission(id: Long) suspend fun clearSpecialMission(id: Long)
@Query("""
SELECT *
FROM SpecialMissions
WHERE id = :id
""")
fun getSpecialMission(id: Long): Flow<SpecialMissions>
} }
@@ -53,7 +53,7 @@ interface UserCharacterDao {
WHERE monId = :monId WHERE monId = :monId
""" """
) )
suspend fun getTransformationHistory(monId: Long): List<CharacterDtos.TransformationHistory>? fun getTransformationHistory(monId: Long): Flow<List<CharacterDtos.TransformationHistory>>
@Query( @Query(
""" """
@@ -110,13 +110,13 @@ interface UserCharacterDao {
suspend fun getCharacter(id: Long): UserCharacter suspend fun getCharacter(id: Long): UserCharacter
@Query("SELECT * FROM BECharacterData WHERE id = :id") @Query("SELECT * FROM BECharacterData WHERE id = :id")
suspend fun getBeData(id: Long): BECharacterData fun getBeData(id: Long): Flow<BECharacterData>
@Query("SELECT * FROM VBCharacterData WHERE id = :id") @Query("SELECT * FROM VBCharacterData WHERE id = :id")
suspend fun getVbData(id: Long): VBCharacterData fun getVbData(id: Long): Flow<VBCharacterData>
@Query("SELECT * FROM SpecialMissions WHERE characterId = :id") @Query("SELECT * FROM SpecialMissions WHERE characterId = :id")
suspend fun getSpecialMissions(id: Long): List<SpecialMissions> fun getSpecialMissions(id: Long): Flow<List<SpecialMissions>>
@Query( @Query(
""" """
@@ -143,7 +143,7 @@ interface UserCharacterDao {
LIMIT 1 LIMIT 1
""" """
) )
suspend fun getActiveCharacter(): CharacterDtos.CharacterWithSprites? fun getActiveCharacter(): Flow<CharacterDtos.CharacterWithSprites?>
@Query("DELETE FROM UserCharacter WHERE id = :id") @Query("DELETE FROM UserCharacter WHERE id = :id")
fun deleteCharacterById(id: Long) fun deleteCharacterById(id: Long)
@@ -24,4 +24,10 @@ object CardDtos {
val characterHp: Int, val characterHp: Int,
val steps: Int, val steps: Int,
) )
data class CardIcon (
val cardIcon: ByteArray,
val cardIconWidth: Int,
val cardIconHeight: Int
)
} }
@@ -0,0 +1,25 @@
import android.util.Log
object AppLogger {
private val logs = StringBuilder()
fun d(tag: String, message: String) {
Log.d(tag, message)
append("DEBUG", tag, message)
}
fun e(tag: String, message: String, tr: Throwable? = null) {
Log.e(tag, message, tr)
append("ERROR", tag, message + (tr?.stackTraceToString() ?: ""))
}
private fun append(level: String, tag: String, message: String) {
logs.append("${System.currentTimeMillis()} [$level][$tag] $message\n")
}
fun getLogs(): String = logs.toString()
fun clear() {
logs.clear()
}
}
@@ -1,13 +1,12 @@
package com.github.nacabaro.vbhelper.navigation package com.github.nacabaro.vbhelper.navigation
import android.util.Log
import androidx.compose.animation.core.tween import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut import androidx.compose.animation.fadeOut
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
@@ -37,6 +36,7 @@ import com.github.nacabaro.vbhelper.screens.adventureScreen.AdventureScreenContr
import com.github.nacabaro.vbhelper.screens.cardScreen.CardAdventureScreen import com.github.nacabaro.vbhelper.screens.cardScreen.CardAdventureScreen
import com.github.nacabaro.vbhelper.screens.cardScreen.CardScreenControllerImpl import com.github.nacabaro.vbhelper.screens.cardScreen.CardScreenControllerImpl
import com.github.nacabaro.vbhelper.screens.settingsScreen.CreditsScreen import com.github.nacabaro.vbhelper.screens.settingsScreen.CreditsScreen
import com.github.nacabaro.vbhelper.screens.settingsScreen.LoggerScreen
import com.github.nacabaro.vbhelper.screens.spriteViewer.SpriteViewerControllerImpl import com.github.nacabaro.vbhelper.screens.spriteViewer.SpriteViewerControllerImpl
import com.github.nacabaro.vbhelper.screens.storageScreen.StorageScreenControllerImpl import com.github.nacabaro.vbhelper.screens.storageScreen.StorageScreenControllerImpl
import com.github.nacabaro.vbhelper.source.StorageRepository import com.github.nacabaro.vbhelper.source.StorageRepository
@@ -99,20 +99,14 @@ fun AppNavigation(
composable(NavigationItems.Scan.route) { composable(NavigationItems.Scan.route) {
val characterIdString = it.arguments?.getString("characterId") val characterIdString = it.arguments?.getString("characterId")
var characterId by remember { mutableStateOf(characterIdString?.toLongOrNull()) } var characterId by remember { mutableStateOf(characterIdString?.toLongOrNull()) }
Log.d("ScanScreen", "characterId: $characterId")
val launchedFromHomeScreen = (characterIdString?.toLongOrNull() == null) val launchedFromHomeScreen = (characterIdString?.toLongOrNull() == null)
if (characterId == null) { if (characterId == null) {
val context = LocalContext.current.applicationContext as VBHelper val context = LocalContext.current.applicationContext as VBHelper
val storageRepository = StorageRepository(context.container.db) val storageRepository = StorageRepository(context.container.db)
val characterData by storageRepository.getActiveCharacter().collectAsState(null)
LaunchedEffect(characterId) {
if (characterId == null) {
val characterData = storageRepository.getActiveCharacter()
if (characterData != null) { if (characterData != null) {
characterId = characterData.id characterId = characterData!!.id
}
}
} }
} }
@@ -189,6 +183,11 @@ fun AppNavigation(
) )
} }
} }
composable(NavigationItems.Logger.route) {
LoggerScreen(
navController = navController
)
}
} }
} }
} }
@@ -98,4 +98,10 @@ sealed class NavigationItems(
R.drawable.baseline_data_24, R.drawable.baseline_data_24,
R.string.nav_credits R.string.nav_credits
) )
object Logger : NavigationItems(
"Logger",
R.drawable.baseline_bug_report,
R.string.nav_logs
)
} }
@@ -1,6 +1,5 @@
package com.github.nacabaro.vbhelper.screens.cardScreen package com.github.nacabaro.vbhelper.screens.cardScreen
import android.util.Log
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
@@ -98,8 +97,8 @@ fun CardsScreen(
selectedCard.value = null selectedCard.value = null
}, },
onRename = { newName -> onRename = { newName ->
Log.d("CardsScreen", "New name: $newName") AppLogger.d("CardsScreen", "New name: $newName")
Log.d("CardsScreen", "Card: ${selectedCard.value.toString()}") AppLogger.d("CardsScreen", "Card: ${selectedCard.value.toString()}")
cardScreenController cardScreenController
.renameCard( .renameCard(
cardId = selectedCard.value!!.cardId, cardId = selectedCard.value!!.cardId,
@@ -11,6 +11,7 @@ import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
@@ -28,9 +29,7 @@ import com.github.nacabaro.vbhelper.components.TopBanner
import com.github.nacabaro.vbhelper.di.VBHelper import com.github.nacabaro.vbhelper.di.VBHelper
import com.github.nacabaro.vbhelper.utils.DeviceType import com.github.nacabaro.vbhelper.utils.DeviceType
import com.github.nacabaro.vbhelper.domain.device_data.BECharacterData import com.github.nacabaro.vbhelper.domain.device_data.BECharacterData
import com.github.nacabaro.vbhelper.domain.device_data.SpecialMissions
import com.github.nacabaro.vbhelper.domain.device_data.VBCharacterData import com.github.nacabaro.vbhelper.domain.device_data.VBCharacterData
import com.github.nacabaro.vbhelper.dtos.CharacterDtos
import com.github.nacabaro.vbhelper.dtos.ItemDtos import com.github.nacabaro.vbhelper.dtos.ItemDtos
import com.github.nacabaro.vbhelper.navigation.NavigationItems import com.github.nacabaro.vbhelper.navigation.NavigationItems
import com.github.nacabaro.vbhelper.screens.homeScreens.screens.BEBEmHomeScreen import com.github.nacabaro.vbhelper.screens.homeScreens.screens.BEBEmHomeScreen
@@ -38,9 +37,12 @@ import com.github.nacabaro.vbhelper.screens.homeScreens.screens.BEDiMHomeScreen
import com.github.nacabaro.vbhelper.screens.homeScreens.screens.VBDiMHomeScreen import com.github.nacabaro.vbhelper.screens.homeScreens.screens.VBDiMHomeScreen
import com.github.nacabaro.vbhelper.screens.itemsScreen.ObtainedItemDialog import com.github.nacabaro.vbhelper.screens.itemsScreen.ObtainedItemDialog
import com.github.nacabaro.vbhelper.source.StorageRepository import com.github.nacabaro.vbhelper.source.StorageRepository
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import com.github.nacabaro.vbhelper.R import com.github.nacabaro.vbhelper.R
import com.github.nacabaro.vbhelper.dtos.CardDtos
import com.github.nacabaro.vbhelper.source.CardRepository
import com.github.nacabaro.vbhelper.utils.BitmapData
import kotlinx.coroutines.flow.flowOf
import kotlin.collections.emptyList
@Composable @Composable
fun HomeScreen( fun HomeScreen(
@@ -49,30 +51,60 @@ fun HomeScreen(
) { ) {
val application = LocalContext.current.applicationContext as VBHelper val application = LocalContext.current.applicationContext as VBHelper
val storageRepository = StorageRepository(application.container.db) val storageRepository = StorageRepository(application.container.db)
val activeMon = remember { mutableStateOf<CharacterDtos.CharacterWithSprites?>(null) } val cardRepository = CardRepository(application.container.db)
val transformationHistory = remember { mutableStateOf<List<CharacterDtos.TransformationHistory>?>(null) }
val beData = remember { mutableStateOf<BECharacterData?>(null) } val activeMon by storageRepository
val vbData = remember { mutableStateOf<VBCharacterData?>(null) } .getActiveCharacter()
val vbSpecialMissions = remember { mutableStateOf<List<SpecialMissions>>(emptyList()) } .collectAsState(initial = null)
val cardIconData by (
activeMon
?.let { chara ->
cardRepository.getCardIconByCharaId(chara.charId)
}
?: flowOf<CardDtos.CardIcon?>(null)
).collectAsState(initial = null)
val transformationHistory by (
activeMon
?.let { chara ->
storageRepository.getTransformationHistory(chara.id)
}
?: flowOf(emptyList())
).collectAsState(initial = emptyList())
val vbSpecialMissions by (
activeMon
?.takeIf { it.characterType == DeviceType.VBDevice }
?.let { chara ->
storageRepository.getSpecialMissions(chara.id)
}
?: flowOf(emptyList())
).collectAsState(initial = emptyList())
val vbData by (
activeMon
?.takeIf { it.characterType == DeviceType.VBDevice }
?.let { chara ->
storageRepository.getCharacterVbData(chara.id)
}
?: flowOf<VBCharacterData?>(null)
).collectAsState(initial = null)
val beData by (
activeMon
?.takeIf { it.characterType == DeviceType.BEDevice }
?.let { chara ->
storageRepository.getCharacterBeData(chara.id)
}
?: flowOf<BECharacterData?>(null)
).collectAsState(initial = null)
var adventureMissionsFinished by rememberSaveable { mutableStateOf(false) } var adventureMissionsFinished by rememberSaveable { mutableStateOf(false) }
var betaWarning by rememberSaveable { mutableStateOf(true) } var betaWarning by rememberSaveable { mutableStateOf(true) }
var collectedItem by remember { mutableStateOf<ItemDtos.PurchasedItem?>(null) } var collectedItem by remember { mutableStateOf<ItemDtos.PurchasedItem?>(null) }
var collectedCurrency by remember { mutableStateOf<Int?>(null) } var collectedCurrency by remember { mutableStateOf<Int?>(null) }
LaunchedEffect(storageRepository, activeMon, collectedItem) {
withContext(Dispatchers.IO) {
activeMon.value = storageRepository.getActiveCharacter()
if (activeMon.value != null && activeMon.value!!.characterType == DeviceType.BEDevice) {
beData.value = storageRepository.getCharacterBeData(activeMon.value!!.id)
transformationHistory.value = storageRepository.getTransformationHistory(activeMon.value!!.id)
} else if (activeMon.value != null && activeMon.value!!.characterType == DeviceType.VBDevice) {
vbData.value = storageRepository.getCharacterVbData(activeMon.value!!.id)
vbSpecialMissions.value = storageRepository.getSpecialMissions(activeMon.value!!.id)
transformationHistory.value = storageRepository.getTransformationHistory(activeMon.value!!.id)
}
}
}
LaunchedEffect(true) { LaunchedEffect(true) {
homeScreenController homeScreenController
.didAdventureMissionsFinish { .didAdventureMissionsFinish {
@@ -93,7 +125,7 @@ fun HomeScreen(
) )
} }
) { contentPadding -> ) { contentPadding ->
if (activeMon.value == null || (beData.value == null && vbData.value == null) || transformationHistory.value == null) { if (activeMon == null || (beData == null && vbData == null) || cardIconData == null || transformationHistory.isEmpty()) {
Column ( Column (
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center, verticalArrangement = Arrangement.Center,
@@ -104,32 +136,41 @@ fun HomeScreen(
Text(text = stringResource(R.string.adventure_empty_state)) Text(text = stringResource(R.string.adventure_empty_state))
} }
} else { } else {
if (activeMon.value!!.isBemCard) { val cardIcon = BitmapData(
bitmap = cardIconData!!.cardIcon,
width = cardIconData!!.cardIconWidth,
height = cardIconData!!.cardIconHeight
)
if (activeMon!!.isBemCard && beData != null) {
BEBEmHomeScreen( BEBEmHomeScreen(
activeMon = activeMon.value!!, activeMon = activeMon!!,
beData = beData.value!!, beData = beData!!,
transformationHistory = transformationHistory.value!!, transformationHistory = transformationHistory,
contentPadding = contentPadding
)
} else if (!activeMon.value!!.isBemCard && activeMon.value!!.characterType == DeviceType.BEDevice) {
BEDiMHomeScreen(
activeMon = activeMon.value!!,
beData = beData.value!!,
transformationHistory = transformationHistory.value!!,
contentPadding = contentPadding
)
} else {
VBDiMHomeScreen(
activeMon = activeMon.value!!,
vbData = vbData.value!!,
transformationHistory = transformationHistory.value!!,
contentPadding = contentPadding, contentPadding = contentPadding,
specialMissions = vbSpecialMissions.value, cardIcon = cardIcon
)
} else if (!activeMon!!.isBemCard && activeMon!!.characterType == DeviceType.BEDevice && beData != null) {
BEDiMHomeScreen(
activeMon = activeMon!!,
beData = beData!!,
transformationHistory = transformationHistory,
contentPadding = contentPadding,
cardIcon = cardIcon
)
} else if (vbData != null) {
VBDiMHomeScreen(
activeMon = activeMon!!,
vbData = vbData!!,
transformationHistory = transformationHistory,
contentPadding = contentPadding,
specialMissions = vbSpecialMissions,
homeScreenController = homeScreenController, homeScreenController = homeScreenController,
onClickCollect = { item, currency -> onClickCollect = { item, currency ->
collectedItem = item collectedItem = item
collectedCurrency = currency collectedCurrency = currency
} },
cardIcon = cardIcon
) )
} }
} }
@@ -1,9 +1,8 @@
package com.github.nacabaro.vbhelper.screens.homeScreens package com.github.nacabaro.vbhelper.screens.homeScreens
import com.github.cfogrady.vbnfc.vb.SpecialMission
import com.github.nacabaro.vbhelper.dtos.ItemDtos import com.github.nacabaro.vbhelper.dtos.ItemDtos
interface HomeScreenController { interface HomeScreenController {
fun didAdventureMissionsFinish(onCompletion: (Boolean) -> Unit) fun didAdventureMissionsFinish(onCompletion: (Boolean) -> Unit)
fun clearSpecialMission(missionId: Long, missionCompletion: SpecialMission.Status, onCleared: (ItemDtos.PurchasedItem?, Int?) -> Unit) fun clearSpecialMission(missionId: Long, onCleared: (ItemDtos.PurchasedItem?, Int?) -> Unit)
} }
@@ -33,13 +33,18 @@ class HomeScreenControllerImpl(
} }
} }
override fun clearSpecialMission(missionId: Long, missionCompletion: SpecialMission.Status, onCleared: (ItemDtos.PurchasedItem?, Int?) -> Unit) { override fun clearSpecialMission(missionId: Long, onCleared: (ItemDtos.PurchasedItem?, Int?) -> Unit) {
componentActivity.lifecycleScope.launch { componentActivity.lifecycleScope.launch {
val missionStatus = database
.specialMissionDao()
.getSpecialMission(missionId)
.first()
database database
.specialMissionDao() .specialMissionDao()
.clearSpecialMission(missionId) .clearSpecialMission(missionId)
if (missionCompletion == SpecialMission.Status.COMPLETED) { if (missionStatus.status == SpecialMission.Status.COMPLETED) {
val randomItem = database val randomItem = database
.itemDao() .itemDao()
.getAllItems() .getAllItems()
@@ -73,7 +78,6 @@ class HomeScreenControllerImpl(
} else { } else {
onCleared(null, null) onCleared(null, null)
} }
} }
} }
} }
@@ -0,0 +1,56 @@
package com.github.nacabaro.vbhelper.screens.homeScreens.dialogs
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.Card
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import com.github.nacabaro.vbhelper.R
@Composable
fun DeleteSpecialMissionDialog(
onClickDismiss: () -> Unit,
onClickDelete: () -> Unit
) {
Dialog(
onDismissRequest = onClickDismiss
) {
Card {
Column(
modifier = Modifier
.padding(16.dp)
) {
Text(
text = stringResource(R.string.home_special_mission_delete_main),
textAlign = TextAlign.Center
)
Spacer(modifier = Modifier.padding(8.dp))
Row {
Button(
onClick = onClickDismiss,
modifier = Modifier
.padding(8.dp)
) {
Text(text = stringResource(R.string.home_special_mission_delete_dismiss))
}
Button(
onClick = onClickDelete,
modifier = Modifier
.padding(8.dp)
) {
Text(text = stringResource(R.string.home_special_mission_delete_remove))
}
}
}
}
}
}
@@ -26,6 +26,7 @@ import java.util.Locale
fun BEBEmHomeScreen( fun BEBEmHomeScreen(
activeMon: CharacterDtos.CharacterWithSprites, activeMon: CharacterDtos.CharacterWithSprites,
beData: BECharacterData, beData: BECharacterData,
cardIcon: BitmapData,
transformationHistory: List<CharacterDtos.TransformationHistory>, transformationHistory: List<CharacterDtos.TransformationHistory>,
contentPadding: PaddingValues contentPadding: PaddingValues
) { ) {
@@ -46,6 +47,7 @@ fun BEBEmHomeScreen(
), ),
multiplier = 8, multiplier = 8,
shape = androidx.compose.material.MaterialTheme.shapes.small, shape = androidx.compose.material.MaterialTheme.shapes.small,
cardIcon = cardIcon,
modifier = Modifier modifier = Modifier
.weight(1f) .weight(1f)
.aspectRatio(1f) .aspectRatio(1f)
@@ -26,6 +26,7 @@ import kotlin.text.format
@Composable @Composable
fun BEDiMHomeScreen( fun BEDiMHomeScreen(
activeMon: CharacterDtos.CharacterWithSprites, activeMon: CharacterDtos.CharacterWithSprites,
cardIcon: BitmapData,
beData: BECharacterData, beData: BECharacterData,
transformationHistory: List<CharacterDtos.TransformationHistory>, transformationHistory: List<CharacterDtos.TransformationHistory>,
contentPadding: PaddingValues contentPadding: PaddingValues
@@ -45,6 +46,7 @@ fun BEDiMHomeScreen(
width = activeMon.spriteWidth, width = activeMon.spriteWidth,
height = activeMon.spriteHeight height = activeMon.spriteHeight
), ),
cardIcon = cardIcon,
multiplier = 8, multiplier = 8,
shape = androidx.compose.material.MaterialTheme.shapes.small, shape = androidx.compose.material.MaterialTheme.shapes.small,
modifier = Modifier modifier = Modifier
@@ -10,6 +10,10 @@ import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
@@ -26,11 +30,13 @@ import com.github.nacabaro.vbhelper.screens.homeScreens.HomeScreenControllerImpl
import com.github.nacabaro.vbhelper.utils.BitmapData import com.github.nacabaro.vbhelper.utils.BitmapData
import java.util.Locale import java.util.Locale
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import com.github.nacabaro.vbhelper.screens.homeScreens.dialogs.DeleteSpecialMissionDialog
@Composable @Composable
fun VBDiMHomeScreen( fun VBDiMHomeScreen(
activeMon: CharacterDtos.CharacterWithSprites, activeMon: CharacterDtos.CharacterWithSprites,
cardIcon: BitmapData,
vbData: VBCharacterData, vbData: VBCharacterData,
specialMissions: List<SpecialMissions>, specialMissions: List<SpecialMissions>,
homeScreenController: HomeScreenControllerImpl, homeScreenController: HomeScreenControllerImpl,
@@ -38,6 +44,8 @@ fun VBDiMHomeScreen(
contentPadding: PaddingValues, contentPadding: PaddingValues,
onClickCollect: (ItemDtos.PurchasedItem?, Int?) -> Unit onClickCollect: (ItemDtos.PurchasedItem?, Int?) -> Unit
) { ) {
var selectedSpecialMissionId by remember { mutableStateOf<Long>(-1) }
Column( Column(
modifier = Modifier modifier = Modifier
.padding(top = contentPadding.calculateTopPadding()) .padding(top = contentPadding.calculateTopPadding())
@@ -53,6 +61,7 @@ fun VBDiMHomeScreen(
width = activeMon.spriteWidth, width = activeMon.spriteWidth,
height = activeMon.spriteHeight height = activeMon.spriteHeight
), ),
cardIcon = cardIcon,
multiplier = 8, multiplier = 8,
shape = androidx.compose.material.MaterialTheme.shapes.small, shape = androidx.compose.material.MaterialTheme.shapes.small,
modifier = Modifier modifier = Modifier
@@ -181,11 +190,28 @@ fun VBDiMHomeScreen(
modifier = Modifier modifier = Modifier
.weight(1f) .weight(1f)
.padding(8.dp), .padding(8.dp),
) { onClickMission = { missionId ->
selectedSpecialMissionId = missionId
},
onClickCollect = {
homeScreenController homeScreenController
.clearSpecialMission(mission.id, mission.status, onClickCollect) .clearSpecialMission(selectedSpecialMissionId, onClickCollect)
}
)
} }
} }
} }
if (selectedSpecialMissionId.toInt() != -1) {
DeleteSpecialMissionDialog(
onClickDismiss = {
selectedSpecialMissionId = -1
},
onClickDelete = {
homeScreenController
.clearSpecialMission(selectedSpecialMissionId, onClickCollect)
selectedSpecialMissionId = -1
}
)
} }
} }
@@ -11,6 +11,8 @@ import com.github.nacabaro.vbhelper.domain.device_data.VBCharacterData
import com.github.nacabaro.vbhelper.dtos.ItemDtos import com.github.nacabaro.vbhelper.dtos.ItemDtos
import com.github.nacabaro.vbhelper.utils.DeviceType import com.github.nacabaro.vbhelper.utils.DeviceType
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
@@ -52,9 +54,16 @@ class ItemsScreenControllerImpl (
var vbCharacterData: VBCharacterData? = null var vbCharacterData: VBCharacterData? = null
if (characterData.characterType == DeviceType.BEDevice) { if (characterData.characterType == DeviceType.BEDevice) {
beCharacterData = database.userCharacterDao().getBeData(characterId) beCharacterData = database
.userCharacterDao()
.getBeData(characterId)
.firstOrNull()
} else if (characterData.characterType == DeviceType.VBDevice) { } else if (characterData.characterType == DeviceType.VBDevice) {
vbCharacterData = database.userCharacterDao().getVbData(characterId) vbCharacterData = database
.userCharacterDao()
.getVbData(characterId)
.firstOrNull()
} }
if ( if (
@@ -154,32 +163,34 @@ class ItemsScreenControllerImpl (
else -> 0 else -> 0
} }
val specialMissionSlot = when (itemIcon) {
ItemTypes.Step8k.id -> 0
ItemTypes.Step4k.id -> 0
ItemTypes.Vitals1000.id -> 1
ItemTypes.Vitals250.id -> 1
ItemTypes.Battle20.id -> 2
ItemTypes.Battle5.id -> 2
ItemTypes.Win10.id -> 3
ItemTypes.Win4.id -> 3
else -> 0
}
val availableSpecialMissions = database val availableSpecialMissions = database
.userCharacterDao() .userCharacterDao()
.getSpecialMissions(characterId) .getSpecialMissions(characterId)
.first()
var firstUnavailableMissionSlot: Long = 0 var newSpecialMission = availableSpecialMissions[specialMissionSlot]
var watchId = 0 newSpecialMission = SpecialMissions(
id = newSpecialMission.id,
for ((index, mission) in availableSpecialMissions.withIndex()) { characterId = newSpecialMission.characterId,
if (
mission.status == SpecialMission.Status.UNAVAILABLE
) {
firstUnavailableMissionSlot = mission.id
watchId = index + 1
}
}
val newSpecialMission = SpecialMissions(
id = firstUnavailableMissionSlot,
characterId = characterId,
missionType = specialMissionType,
goal = specialMissionGoal, goal = specialMissionGoal,
timeLimitInMinutes = itemLength, watchId = newSpecialMission.watchId,
watchId = watchId,
status = SpecialMission.Status.AVAILABLE,
progress = 0, progress = 0,
timeElapsedInMinutes = 0 status = SpecialMission.Status.AVAILABLE,
timeElapsedInMinutes = 0,
timeLimitInMinutes = itemLength,
missionType = specialMissionType
) )
database database
@@ -6,7 +6,6 @@ import android.nfc.Tag
import android.nfc.tech.NfcA import android.nfc.tech.NfcA
import android.os.Bundle import android.os.Bundle
import android.provider.Settings import android.provider.Settings
import android.util.Log
import android.widget.Toast import android.widget.Toast
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
@@ -127,18 +126,18 @@ class ScanScreenControllerImpl(
handleTag(secrets) { tagCommunicator -> handleTag(secrets) { tagCommunicator ->
try { try {
if (nfcCharacter is VBNfcCharacter) { if (nfcCharacter is VBNfcCharacter) {
Log.d("SendCharacter", "VBNfcCharacter") AppLogger.d("SendCharacter", "VBNfcCharacter")
val castNfcCharacter: VBNfcCharacter = nfcCharacter val castNfcCharacter: VBNfcCharacter = nfcCharacter
tagCommunicator.sendCharacter(castNfcCharacter) tagCommunicator.sendCharacter(castNfcCharacter)
} else if (nfcCharacter is BENfcCharacter) { } else if (nfcCharacter is BENfcCharacter) {
Log.d("SendCharacter", "BENfcCharacter") AppLogger.d("SendCharacter", "BENfcCharacter")
val castNfcCharacter: BENfcCharacter = nfcCharacter val castNfcCharacter: BENfcCharacter = nfcCharacter
tagCommunicator.sendCharacter(castNfcCharacter) tagCommunicator.sendCharacter(castNfcCharacter)
} }
onComplete.invoke() onComplete.invoke()
componentActivity.getString(R.string.scan_sent_character_success) componentActivity.getString(R.string.scan_sent_character_success)
} catch (e: Throwable) { } catch (e: Throwable) {
Log.e("TAG", e.stackTraceToString()) AppLogger.e("TAG", e.stackTraceToString())
componentActivity.getString(R.string.scan_error_generic) componentActivity.getString(R.string.scan_error_generic)
} }
} }
@@ -178,7 +177,7 @@ class ScanScreenControllerImpl(
) )
val character = nfcGenerator.characterToNfc(characterId) val character = nfcGenerator.characterToNfc(characterId)
Log.d("CharacterType", character.toString()) AppLogger.d("CharacterType", character.toString())
return character return character
} }
@@ -2,7 +2,6 @@ package com.github.nacabaro.vbhelper.screens.scanScreen.converters
import android.icu.util.Calendar import android.icu.util.Calendar
import android.icu.util.TimeZone import android.icu.util.TimeZone
import android.util.Log
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import com.github.cfogrady.vbnfc.be.BENfcCharacter import com.github.cfogrady.vbnfc.be.BENfcCharacter
import com.github.cfogrady.vbnfc.be.FirmwareVersion import com.github.cfogrady.vbnfc.be.FirmwareVersion
@@ -55,6 +54,7 @@ class ToNfcConverter(
val vbData = database val vbData = database
.userCharacterDao() .userCharacterDao()
.getVbData(characterId) .getVbData(characterId)
.first()
val paddedTransformationArray = generateTransformationHistory(characterId, 9) val paddedTransformationArray = generateTransformationHistory(characterId, 9)
@@ -116,6 +116,7 @@ class ToNfcConverter(
val specialMissions = database val specialMissions = database
.userCharacterDao() .userCharacterDao()
.getSpecialMissions(characterId) .getSpecialMissions(characterId)
.first()
val watchSpecialMissions = specialMissions.map { val watchSpecialMissions = specialMissions.map {
SpecialMission( SpecialMission(
@@ -159,7 +160,7 @@ class ToNfcConverter(
} }
nfcVitalsHistory.map { nfcVitalsHistory.map {
Log.d("NFC", it.toString()) AppLogger.d("NFC", it.toString())
} }
return nfcVitalsHistory return nfcVitalsHistory
@@ -175,6 +176,7 @@ class ToNfcConverter(
val beData = database val beData = database
.userCharacterDao() .userCharacterDao()
.getBeData(characterId) .getBeData(characterId)
.first()
val paddedTransformationArray = generateTransformationHistory(characterId) val paddedTransformationArray = generateTransformationHistory(characterId)
@@ -237,13 +239,14 @@ class ToNfcConverter(
): Array<NfcCharacter.Transformation> { ): Array<NfcCharacter.Transformation> {
val transformationHistory = database val transformationHistory = database
.userCharacterDao() .userCharacterDao()
.getTransformationHistory(characterId)!! .getTransformationHistory(characterId)
.first()
.map { .map {
val date = Date(it.transformationDate) val date = Date(it.transformationDate)
val calendar = android.icu.util.GregorianCalendar(TimeZone.getTimeZone("UTC")) val calendar = android.icu.util.GregorianCalendar(TimeZone.getTimeZone("UTC"))
calendar.time = date calendar.time = date
Log.d( AppLogger.d(
"TransformationHistory", "TransformationHistory",
"Year: ${calendar.get(Calendar.YEAR)}, " + "Year: ${calendar.get(Calendar.YEAR)}, " +
"Month: ${calendar.get(Calendar.MONTH) + 1}, " + "Month: ${calendar.get(Calendar.MONTH) + 1}, " +
@@ -0,0 +1,44 @@
package com.github.nacabaro.vbhelper.screens.settingsScreen
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.navigation.NavController
import com.github.nacabaro.vbhelper.R
import com.github.nacabaro.vbhelper.components.TopBanner
@Composable
fun LoggerScreen(
navController: NavController
) {
Scaffold (
topBar = {
TopBanner(
text = stringResource(R.string.logs_title),
onBackClick = {
navController.popBackStack()
}
)
},
modifier = Modifier
.fillMaxSize()
) { contentPadding ->
Column (
modifier = Modifier
.padding(top = contentPadding.calculateTopPadding())
) {
Text(
text = AppLogger.getLogs(),
modifier = Modifier
.verticalScroll(rememberScrollState())
)
}
}
}
@@ -1,7 +1,6 @@
package com.github.nacabaro.vbhelper.screens.settingsScreen package com.github.nacabaro.vbhelper.screens.settingsScreen
import android.content.Intent import android.content.Intent
import android.net.Uri
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
@@ -24,6 +23,7 @@ import androidx.navigation.NavController
import com.github.nacabaro.vbhelper.components.TopBanner import com.github.nacabaro.vbhelper.components.TopBanner
import com.github.nacabaro.vbhelper.navigation.NavigationItems import com.github.nacabaro.vbhelper.navigation.NavigationItems
import com.github.nacabaro.vbhelper.R import com.github.nacabaro.vbhelper.R
import androidx.core.net.toUri
@Composable @Composable
@@ -80,10 +80,16 @@ fun SettingsScreen(
) { ) {
val browserIntent = Intent( val browserIntent = Intent(
Intent.ACTION_VIEW, Intent.ACTION_VIEW,
Uri.parse("https://github.com/nacabaro/vbhelper/") "https://github.com/nacabaro/vbhelper/".toUri()
) )
context.startActivity(browserIntent) context.startActivity(browserIntent)
} }
SettingsEntry(
title = stringResource(R.string.settings_logs_title),
description = stringResource(R.string.settings_logs_desc)
) {
navController.navigate(NavigationItems.Logger.route)
}
SettingsSection(title = stringResource(R.string.settings_section_data)) SettingsSection(title = stringResource(R.string.settings_section_data))
SettingsEntry( SettingsEntry(
@@ -1,6 +1,5 @@
package com.github.nacabaro.vbhelper.screens.settingsScreen.controllers package com.github.nacabaro.vbhelper.screens.settingsScreen.controllers
import android.util.Log
import com.github.cfogrady.vb.dim.card.BemCard import com.github.cfogrady.vb.dim.card.BemCard
import com.github.cfogrady.vb.dim.card.DimCard import com.github.cfogrady.vb.dim.card.DimCard
import com.github.cfogrady.vb.dim.card.DimReader import com.github.cfogrady.vb.dim.card.DimReader
@@ -154,7 +153,7 @@ class CardImportController(
cardId: Long, cardId: Long,
card: com.github.cfogrady.vb.dim.card.Card<*, *, *, *, *, *> card: com.github.cfogrady.vb.dim.card.Card<*, *, *, *, *, *>
) { ) {
Log.d("importAdventureMissions", "Importing adventure missions") AppLogger.d("importAdventureMissions", "Importing adventure missions")
if (card is BemCard) { if (card is BemCard) {
card.adventureLevels.levels.forEach { card.adventureLevels.levels.forEach {
database database
@@ -190,13 +189,13 @@ class CardImportController(
cardId: Long, cardId: Long,
card: com.github.cfogrady.vb.dim.card.Card<*, *, *, *, *, *> card: com.github.cfogrady.vb.dim.card.Card<*, *, *, *, *, *>
) { ) {
Log.d("importCardFusions", "Importing card fusions") AppLogger.d("importCardFusions", "Importing card fusions")
if (card is DimCard) { if (card is DimCard) {
card card
.attributeFusions .attributeFusions
.entries .entries
.forEach { .forEach {
Log.d("importCardFusions", "Importing fusion: ${it.attribute1Fusion}") AppLogger.d("importCardFusions", "Importing fusion: ${it.attribute1Fusion}")
if (it.attribute1Fusion != 65535 && it.characterIndex != 65535) { if (it.attribute1Fusion != 65535 && it.characterIndex != 65535) {
database database
.cardFusionsDao() .cardFusionsDao()
@@ -2,7 +2,6 @@ package com.github.nacabaro.vbhelper.screens.settingsScreen.controllers
import android.net.Uri import android.net.Uri
import android.provider.OpenableColumns import android.provider.OpenableColumns
import android.util.Log
import android.widget.Toast import android.widget.Toast
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
@@ -41,7 +40,7 @@ class DatabaseManagementController(
componentActivity.finishAffinity() componentActivity.finishAffinity()
} }
} catch (e: Exception) { } catch (e: Exception) {
Log.e("ScanScreenController", "Error exporting database $e") AppLogger.e("ScanScreenController", "Error exporting database $e")
componentActivity.runOnUiThread { componentActivity.runOnUiThread {
Toast.makeText(componentActivity, "Error exporting database: ${e.message}", Toast.LENGTH_LONG).show() Toast.makeText(componentActivity, "Error exporting database: ${e.message}", Toast.LENGTH_LONG).show()
} }
@@ -84,7 +83,7 @@ class DatabaseManagementController(
componentActivity.finishAffinity() componentActivity.finishAffinity()
} }
} catch (e: Exception) { } catch (e: Exception) {
Log.e("ScanScreenController", "Error importing database $e") AppLogger.e("ScanScreenController", "Error importing database $e")
componentActivity.runOnUiThread { componentActivity.runOnUiThread {
Toast.makeText(componentActivity, "Error importing database: ${e.message}", Toast.LENGTH_LONG).show() Toast.makeText(componentActivity, "Error importing database: ${e.message}", Toast.LENGTH_LONG).show()
} }
@@ -1,7 +1,6 @@
package com.github.nacabaro.vbhelper.screens.spriteViewer package com.github.nacabaro.vbhelper.screens.spriteViewer
import android.graphics.Bitmap import android.graphics.Bitmap
import android.util.Log
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
@@ -13,7 +12,6 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.asImageBitmap import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
@@ -27,8 +25,6 @@ fun SpriteViewer(
) { ) {
val spriteList = remember { mutableStateListOf<Bitmap>() } val spriteList = remember { mutableStateListOf<Bitmap>() }
Log.d("SpriteViewer", "spriteList: $spriteList")
LaunchedEffect(spriteViewerController) { LaunchedEffect(spriteViewerController) {
val sprites = spriteViewerController.getAllSprites() val sprites = spriteViewerController.getAllSprites()
val bitmapData = spriteViewerController.convertToBitmap(sprites) val bitmapData = spriteViewerController.convertToBitmap(sprites)
@@ -1,13 +1,11 @@
package com.github.nacabaro.vbhelper.screens.spriteViewer package com.github.nacabaro.vbhelper.screens.spriteViewer
import android.graphics.Bitmap import android.graphics.Bitmap
import android.util.Log
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.compose.runtime.remember
import androidx.compose.ui.graphics.asImageBitmap
import com.github.nacabaro.vbhelper.di.VBHelper import com.github.nacabaro.vbhelper.di.VBHelper
import com.github.nacabaro.vbhelper.domain.characters.Sprite import com.github.nacabaro.vbhelper.domain.characters.Sprite
import java.nio.ByteBuffer import java.nio.ByteBuffer
import androidx.core.graphics.createBitmap
class SpriteViewerControllerImpl( class SpriteViewerControllerImpl(
private val context: ComponentActivity private val context: ComponentActivity
@@ -24,41 +22,40 @@ class SpriteViewerControllerImpl(
val bitmapList = mutableListOf<Bitmap>() val bitmapList = mutableListOf<Bitmap>()
for (sprite in sprites) { for (sprite in sprites) {
Log.d("SpriteViewer", "sprite: $sprite") bitmapList.add(createBitmap(sprite.width, sprite.height, Bitmap.Config.RGB_565).apply {
bitmapList.add(Bitmap.createBitmap(sprite.width, sprite.height, Bitmap.Config.RGB_565).apply {
copyPixelsFromBuffer(ByteBuffer.wrap(sprite.spriteIdle1)) copyPixelsFromBuffer(ByteBuffer.wrap(sprite.spriteIdle1))
}) })
bitmapList.add(Bitmap.createBitmap(sprite.width, sprite.height, Bitmap.Config.RGB_565).apply { bitmapList.add(createBitmap(sprite.width, sprite.height, Bitmap.Config.RGB_565).apply {
copyPixelsFromBuffer(ByteBuffer.wrap(sprite.spriteIdle2)) copyPixelsFromBuffer(ByteBuffer.wrap(sprite.spriteIdle2))
}) })
bitmapList.add(Bitmap.createBitmap(sprite.width, sprite.height, Bitmap.Config.RGB_565).apply { bitmapList.add(createBitmap(sprite.width, sprite.height, Bitmap.Config.RGB_565).apply {
copyPixelsFromBuffer(ByteBuffer.wrap(sprite.spriteWalk1)) copyPixelsFromBuffer(ByteBuffer.wrap(sprite.spriteWalk1))
}) })
bitmapList.add(Bitmap.createBitmap(sprite.width, sprite.height, Bitmap.Config.RGB_565).apply { bitmapList.add(createBitmap(sprite.width, sprite.height, Bitmap.Config.RGB_565).apply {
copyPixelsFromBuffer(ByteBuffer.wrap(sprite.spriteWalk2)) copyPixelsFromBuffer(ByteBuffer.wrap(sprite.spriteWalk2))
}) })
bitmapList.add(Bitmap.createBitmap(sprite.width, sprite.height, Bitmap.Config.RGB_565).apply { bitmapList.add(createBitmap(sprite.width, sprite.height, Bitmap.Config.RGB_565).apply {
copyPixelsFromBuffer(ByteBuffer.wrap(sprite.spriteRun1)) copyPixelsFromBuffer(ByteBuffer.wrap(sprite.spriteRun1))
}) })
bitmapList.add(Bitmap.createBitmap(sprite.width, sprite.height, Bitmap.Config.RGB_565).apply { bitmapList.add(createBitmap(sprite.width, sprite.height, Bitmap.Config.RGB_565).apply {
copyPixelsFromBuffer(ByteBuffer.wrap(sprite.spriteRun2)) copyPixelsFromBuffer(ByteBuffer.wrap(sprite.spriteRun2))
}) })
bitmapList.add(Bitmap.createBitmap(sprite.width, sprite.height, Bitmap.Config.RGB_565).apply { bitmapList.add(createBitmap(sprite.width, sprite.height, Bitmap.Config.RGB_565).apply {
copyPixelsFromBuffer(ByteBuffer.wrap(sprite.spriteTrain1)) copyPixelsFromBuffer(ByteBuffer.wrap(sprite.spriteTrain1))
}) })
bitmapList.add(Bitmap.createBitmap(sprite.width, sprite.height, Bitmap.Config.RGB_565).apply { bitmapList.add(createBitmap(sprite.width, sprite.height, Bitmap.Config.RGB_565).apply {
copyPixelsFromBuffer(ByteBuffer.wrap(sprite.spriteTrain2)) copyPixelsFromBuffer(ByteBuffer.wrap(sprite.spriteTrain2))
}) })
bitmapList.add(Bitmap.createBitmap(sprite.width, sprite.height, Bitmap.Config.RGB_565).apply { bitmapList.add(createBitmap(sprite.width, sprite.height, Bitmap.Config.RGB_565).apply {
copyPixelsFromBuffer(ByteBuffer.wrap(sprite.spriteHappy)) copyPixelsFromBuffer(ByteBuffer.wrap(sprite.spriteHappy))
}) })
bitmapList.add(Bitmap.createBitmap(sprite.width, sprite.height, Bitmap.Config.RGB_565).apply { bitmapList.add(createBitmap(sprite.width, sprite.height, Bitmap.Config.RGB_565).apply {
copyPixelsFromBuffer(ByteBuffer.wrap(sprite.spriteSleep)) copyPixelsFromBuffer(ByteBuffer.wrap(sprite.spriteSleep))
}) })
bitmapList.add(Bitmap.createBitmap(sprite.width, sprite.height, Bitmap.Config.RGB_565).apply { bitmapList.add(createBitmap(sprite.width, sprite.height, Bitmap.Config.RGB_565).apply {
copyPixelsFromBuffer(ByteBuffer.wrap(sprite.spriteAttack)) copyPixelsFromBuffer(ByteBuffer.wrap(sprite.spriteAttack))
}) })
bitmapList.add(Bitmap.createBitmap(sprite.width, sprite.height, Bitmap.Config.RGB_565).apply { bitmapList.add(createBitmap(sprite.width, sprite.height, Bitmap.Config.RGB_565).apply {
copyPixelsFromBuffer(ByteBuffer.wrap(sprite.spriteDodge)) copyPixelsFromBuffer(ByteBuffer.wrap(sprite.spriteDodge))
}) })
} }
@@ -1,6 +1,5 @@
package com.github.nacabaro.vbhelper.screens.storageScreen package com.github.nacabaro.vbhelper.screens.storageScreen
import android.util.Log
import android.widget.Toast import android.widget.Toast
import androidx.compose.foundation.gestures.Orientation import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.gestures.scrollable import androidx.compose.foundation.gestures.scrollable
@@ -149,7 +148,7 @@ fun StorageScreen(
.deleteCharacter( .deleteCharacter(
characterId = selectedCharacter!!, characterId = selectedCharacter!!,
onCompletion = { onCompletion = {
Log.d("StorageScreen", "Character deleted") AppLogger.d("StorageScreen", "Character deleted")
} }
) )
selectedCharacter = null selectedCharacter = null
@@ -0,0 +1,15 @@
package com.github.nacabaro.vbhelper.source
import com.github.nacabaro.vbhelper.database.AppDatabase
import com.github.nacabaro.vbhelper.dtos.CardDtos
import kotlinx.coroutines.flow.Flow
class CardRepository (
private val db: AppDatabase
) {
fun getCardIconByCharaId(charaId: Long): Flow<CardDtos.CardIcon> {
return db
.cardDao()
.getCardIconByCharaId(charaId = charaId)
}
}
@@ -19,19 +19,19 @@ class StorageRepository (
return db.userCharacterDao().getCharacterWithSprites(id) return db.userCharacterDao().getCharacterWithSprites(id)
} }
suspend fun getCharacterBeData(id: Long): BECharacterData { fun getCharacterBeData(id: Long): Flow<BECharacterData> {
return db.userCharacterDao().getBeData(id) return db.userCharacterDao().getBeData(id)
} }
suspend fun getTransformationHistory(characterId: Long): List<CharacterDtos.TransformationHistory>? { fun getTransformationHistory(characterId: Long): Flow<List<CharacterDtos.TransformationHistory>> {
return db.userCharacterDao().getTransformationHistory(characterId) return db.userCharacterDao().getTransformationHistory(characterId)
} }
suspend fun getCharacterVbData(id: Long): VBCharacterData { fun getCharacterVbData(id: Long): Flow<VBCharacterData> {
return db.userCharacterDao().getVbData(id) return db.userCharacterDao().getVbData(id)
} }
suspend fun getSpecialMissions(id: Long): List<SpecialMissions> { fun getSpecialMissions(id: Long): Flow<List<SpecialMissions>> {
return db.userCharacterDao().getSpecialMissions(id) return db.userCharacterDao().getSpecialMissions(id)
} }
@@ -39,7 +39,7 @@ class StorageRepository (
return db.itemDao().getItem(id) return db.itemDao().getItem(id)
} }
suspend fun getActiveCharacter(): CharacterDtos.CharacterWithSprites? { fun getActiveCharacter(): Flow<CharacterDtos.CharacterWithSprites?> {
return db.userCharacterDao().getActiveCharacter() return db.userCharacterDao().getActiveCharacter()
} }
@@ -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,760q66,0 113,-47t47,-113v-160q0,-66 -47,-113t-113,-47q-66,0 -113,47t-47,113v160q0,66 47,113t113,47ZM400,640h160v-80L400,560v80ZM400,480h160v-80L400,400v80ZM480,520ZM480,840q-65,0 -120.5,-32T272,720L160,720v-80h84q-3,-20 -3.5,-40t-0.5,-40h-80v-80h80q0,-20 0.5,-40t3.5,-40h-84v-80h112q14,-23 31.5,-43t40.5,-35l-64,-66 56,-56 86,86q28,-9 57,-9t57,9l88,-86 56,56 -66,66q23,15 41.5,34.5T688,320h112v80h-84q3,20 3.5,40t0.5,40h80v80h-80q0,20 -0.5,40t-3.5,40h84v80L688,720q-32,56 -87.5,88T480,840Z"
android:fillColor="#000000"/>
</vector>
+7
View File
@@ -222,5 +222,12 @@
<string name="special_mission_wins_progress">Won %1$d battles</string> <string name="special_mission_wins_progress">Won %1$d battles</string>
<string name="special_mission_vitals_progress">Earned %1$d vitals</string> <string name="special_mission_vitals_progress">Earned %1$d vitals</string>
<string name="special_mission_icon_content_description">Special mission icon</string> <string name="special_mission_icon_content_description">Special mission icon</string>
<string name="home_special_mission_delete_main">Are you sure you want to delete this special mission with this progress?</string>
<string name="home_special_mission_delete_dismiss">Dismiss</string>
<string name="home_special_mission_delete_remove">Remove</string>
<string name="nav_logs">Logs</string>
<string name="settings_logs_title">Log viewer</string>
<string name="settings_logs_desc">Check logs generated within the app</string>
<string name="logs_title">Application logs</string>
</resources> </resources>
+21 -12
View File
@@ -1,24 +1,32 @@
[versions] [versions]
agp = "8.13.1" agp = "8.13.2"
datastore = "1.1.2" datastore = "1.2.0"
kotlin = "2.0.0" datastorePreferences = "1.2.0"
coreKtx = "1.15.0" kotlin = "2.3.0"
coreKtx = "1.17.0"
junit = "4.13.2" junit = "4.13.2"
junitVersion = "1.2.1" junitVersion = "1.3.0"
espressoCore = "3.6.1" espressoCore = "3.7.0"
lifecycleRuntimeKtx = "2.8.7" lifecycleRuntimeKtx = "2.10.0"
activityCompose = "1.10.0" activityCompose = "1.12.2"
composeBom = "2025.01.00" composeBom = "2026.01.00"
protobufGradlePlugin = "0.9.4" material = "1.13.0"
protobufJavalite = "4.27.0" navigationCompose = "2.9.6"
roomRuntime = "2.6.1" protobufGradlePlugin = "0.9.6"
protobufJavalite = "4.33.4"
roomRuntime = "2.8.4"
vbNfcReader = "0.2.0-SNAPSHOT" vbNfcReader = "0.2.0-SNAPSHOT"
dimReader = "2.1.0" dimReader = "2.1.0"
[libraries] [libraries]
androidx-compose-material = { module = "androidx.compose.material:material" }
androidx-compose-material-icons-extended = { module = "androidx.compose.material:material-icons-extended" }
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
androidx-datastore = { module = "androidx.datastore:datastore", version.ref = "datastore" } androidx-datastore = { module = "androidx.datastore:datastore", version.ref = "datastore" }
androidx-datastore-preferences = { module = "androidx.datastore:datastore-preferences", version.ref = "datastorePreferences" }
androidx-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "navigationCompose" }
androidx-room-compiler = { module = "androidx.room:room-compiler", version.ref = "roomRuntime" } androidx-room-compiler = { module = "androidx.room:room-compiler", version.ref = "roomRuntime" }
androidx-room-ktx = { module = "androidx.room:room-ktx", version.ref = "roomRuntime" }
androidx-room-runtime = { module = "androidx.room:room-runtime", version.ref = "roomRuntime" } androidx-room-runtime = { module = "androidx.room:room-runtime", version.ref = "roomRuntime" }
junit = { group = "junit", name = "junit", version.ref = "junit" } junit = { group = "junit", name = "junit", version.ref = "junit" }
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" } androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
@@ -33,6 +41,7 @@ androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-toolin
androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" } androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" } androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
androidx-material3 = { group = "androidx.compose.material3", name = "material3" } androidx-material3 = { group = "androidx.compose.material3", name = "material3" }
material = { module = "com.google.android.material:material", version.ref = "material" }
protobuf-gradle-plugin = { module = "com.google.protobuf:protobuf-gradle-plugin", version.ref = "protobufGradlePlugin" } protobuf-gradle-plugin = { module = "com.google.protobuf:protobuf-gradle-plugin", version.ref = "protobufGradlePlugin" }
protobuf-javalite = { module = "com.google.protobuf:protobuf-javalite", version.ref = "protobufJavalite" } protobuf-javalite = { module = "com.google.protobuf:protobuf-javalite", version.ref = "protobufJavalite" }
vb-nfc-reader = { module = "com.github.cfogrady:vb-nfc-reader", version.ref = "vbNfcReader" } vb-nfc-reader = { module = "com.github.cfogrady:vb-nfc-reader", version.ref = "vbNfcReader" }