mirror of
https://github.com/nacabaro/vbhelper.git
synced 2026-10-01 15:41:36 +00:00
Compare commits
31
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d46769b0cb | ||
|
|
fce05870c5 | ||
|
|
d749af0a9c | ||
|
|
076f6bfacf | ||
|
|
a4f3b701e5 | ||
|
|
da134247b1 | ||
|
|
e36a700d9f | ||
|
|
fb6b6d2825 | ||
|
|
e17f6c23e4 | ||
|
|
409474b5d1 | ||
|
|
547318864b | ||
|
|
5e92895656 | ||
|
|
25500e9cb4 | ||
|
|
1a38cefb92 | ||
|
|
a974bd366e | ||
|
|
bb5f66d167 | ||
|
|
09394871e5 | ||
|
|
972a640394 | ||
|
|
35a7c9d959 | ||
|
|
b4a73031e7 | ||
|
|
3e41f82273 | ||
|
|
f3740d4816 | ||
|
|
305b776b52 | ||
|
|
c0a67e382b | ||
|
|
bc7a05ccd3 | ||
|
|
660dbaca19 | ||
|
|
b89dfb0df3 | ||
|
|
23e233227a | ||
|
|
542072c238 | ||
|
|
42dd87f0c4 | ||
|
|
635be60cdb |
@@ -15,7 +15,7 @@ android {
|
|||||||
minSdk = 28
|
minSdk = 28
|
||||||
targetSdk = 35
|
targetSdk = 35
|
||||||
versionCode = 1
|
versionCode = 1
|
||||||
versionName = "1.0"
|
versionName = "Alpha 0.1"
|
||||||
|
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
@@ -1,45 +1,24 @@
|
|||||||
package com.github.nacabaro.vbhelper
|
package com.github.nacabaro.vbhelper
|
||||||
|
|
||||||
import android.content.Intent
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.widget.Toast
|
|
||||||
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
|
||||||
import androidx.activity.result.ActivityResultLauncher
|
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.lifecycle.lifecycleScope
|
|
||||||
import com.github.cfogrady.vb.dim.card.BemCard
|
|
||||||
import com.github.cfogrady.vb.dim.card.DimReader
|
|
||||||
import com.github.nacabaro.vbhelper.navigation.AppNavigation
|
import com.github.nacabaro.vbhelper.navigation.AppNavigation
|
||||||
import com.github.cfogrady.vbnfc.be.BENfcCharacter
|
|
||||||
import com.github.cfogrady.vbnfc.data.NfcCharacter
|
|
||||||
import com.github.cfogrady.vbnfc.vb.VBNfcCharacter
|
|
||||||
import com.github.nacabaro.vbhelper.di.VBHelper
|
import com.github.nacabaro.vbhelper.di.VBHelper
|
||||||
import com.github.nacabaro.vbhelper.domain.characters.Card
|
|
||||||
import com.github.nacabaro.vbhelper.domain.Sprites
|
|
||||||
import com.github.nacabaro.vbhelper.domain.characters.Character
|
|
||||||
import com.github.nacabaro.vbhelper.domain.characters.Dex
|
|
||||||
import com.github.nacabaro.vbhelper.domain.device_data.BECharacterData
|
|
||||||
import com.github.nacabaro.vbhelper.domain.device_data.UserCharacter
|
|
||||||
import com.github.nacabaro.vbhelper.navigation.AppNavigationHandlers
|
import com.github.nacabaro.vbhelper.navigation.AppNavigationHandlers
|
||||||
|
import com.github.nacabaro.vbhelper.screens.homeScreens.HomeScreenControllerImpl
|
||||||
|
import com.github.nacabaro.vbhelper.screens.itemsScreen.ItemsScreenControllerImpl
|
||||||
import com.github.nacabaro.vbhelper.screens.scanScreen.ScanScreenControllerImpl
|
import com.github.nacabaro.vbhelper.screens.scanScreen.ScanScreenControllerImpl
|
||||||
import com.github.nacabaro.vbhelper.screens.SettingsScreenController
|
import com.github.nacabaro.vbhelper.screens.settingsScreen.SettingsScreenControllerImpl
|
||||||
import com.github.nacabaro.vbhelper.source.ApkSecretsImporter
|
import com.github.nacabaro.vbhelper.screens.adventureScreen.AdventureScreenControllerImpl
|
||||||
|
import com.github.nacabaro.vbhelper.screens.storageScreen.StorageScreenControllerImpl
|
||||||
import com.github.nacabaro.vbhelper.ui.theme.VBHelperTheme
|
import com.github.nacabaro.vbhelper.ui.theme.VBHelperTheme
|
||||||
import com.github.nacabaro.vbhelper.utils.DeviceType
|
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import java.util.GregorianCalendar
|
|
||||||
|
|
||||||
class MainActivity : ComponentActivity() {
|
class MainActivity : ComponentActivity() {
|
||||||
|
|
||||||
private var nfcCharacter = MutableStateFlow<NfcCharacter?>(null)
|
|
||||||
|
|
||||||
private lateinit var activityResultLauncher: ActivityResultLauncher<Intent>
|
|
||||||
|
|
||||||
private val onActivityLifecycleListeners = HashMap<String, ActivityLifecycleListener>()
|
private val onActivityLifecycleListeners = HashMap<String, ActivityLifecycleListener>()
|
||||||
|
|
||||||
private fun registerActivityLifecycleListener(key: String, activityLifecycleListener: ActivityLifecycleListener) {
|
private fun registerActivityLifecycleListener(key: String, activityLifecycleListener: ActivityLifecycleListener) {
|
||||||
@@ -54,26 +33,36 @@ class MainActivity : ComponentActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
registerFileActivityResult()
|
|
||||||
|
|
||||||
val application = applicationContext as VBHelper
|
val application = applicationContext as VBHelper
|
||||||
val settingsScreenController = SettingsScreenController.Factory(this, ApkSecretsImporter(), application.container.dataStoreSecretsRepository)
|
|
||||||
.buildSettingScreenHandlers()
|
|
||||||
val scanScreenController = ScanScreenControllerImpl(
|
val scanScreenController = ScanScreenControllerImpl(
|
||||||
application.container.dataStoreSecretsRepository.secretsFlow,
|
application.container.dataStoreSecretsRepository.secretsFlow,
|
||||||
this::handleReceivedNfcCharacter,
|
|
||||||
this,
|
this,
|
||||||
this::registerActivityLifecycleListener,
|
this::registerActivityLifecycleListener,
|
||||||
this::unregisterActivityLifecycleListener)
|
this::unregisterActivityLifecycleListener
|
||||||
|
)
|
||||||
|
val settingsScreenController = SettingsScreenControllerImpl(this)
|
||||||
|
val itemsScreenController = ItemsScreenControllerImpl(this)
|
||||||
|
val adventureScreenController = AdventureScreenControllerImpl(this)
|
||||||
|
val storageScreenController = StorageScreenControllerImpl(this)
|
||||||
|
val homeScreenController = HomeScreenControllerImpl(this)
|
||||||
|
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
enableEdgeToEdge()
|
enableEdgeToEdge()
|
||||||
|
|
||||||
setContent {
|
setContent {
|
||||||
VBHelperTheme {
|
VBHelperTheme {
|
||||||
MainApplication(settingsScreenController, scanScreenController)
|
MainApplication(
|
||||||
|
scanScreenController = scanScreenController,
|
||||||
|
settingsScreenController = settingsScreenController,
|
||||||
|
itemsScreenController = itemsScreenController,
|
||||||
|
adventureScreenController = adventureScreenController,
|
||||||
|
homeScreenController = homeScreenController,
|
||||||
|
storageScreenController = storageScreenController
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.i("MainActivity", "Activity onCreated")
|
Log.i("MainActivity", "Activity onCreated")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,232 +82,25 @@ class MainActivity : ComponentActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun registerFileActivityResult() {
|
|
||||||
activityResultLauncher = registerForActivityResult(
|
|
||||||
ActivityResultContracts.StartActivityForResult()
|
|
||||||
) {
|
|
||||||
lifecycleScope.launch {
|
|
||||||
val application = applicationContext as VBHelper
|
|
||||||
val storageRepository = application.container.db
|
|
||||||
|
|
||||||
if (it.resultCode != RESULT_OK) {
|
|
||||||
Toast.makeText(applicationContext, "Import operation cancelled.", Toast.LENGTH_SHORT).show()
|
|
||||||
}
|
|
||||||
val contentResolver = applicationContext.contentResolver
|
|
||||||
val inputStream = contentResolver.openInputStream(it.data!!.data!!)
|
|
||||||
inputStream.use { fileReader ->
|
|
||||||
val dimReader = DimReader()
|
|
||||||
val card = dimReader.readCard(fileReader, false)
|
|
||||||
|
|
||||||
Log.i("MainActivity", "Card name: ${card is BemCard}")
|
|
||||||
|
|
||||||
val cardModel = Card(
|
|
||||||
dimId = card.header.dimId,
|
|
||||||
logo = card.spriteData.sprites[0].pixelData,
|
|
||||||
name = card.spriteData.text, // TODO Make user write card name
|
|
||||||
stageCount = card.adventureLevels.levels.size,
|
|
||||||
logoHeight = card.spriteData.sprites[0].height,
|
|
||||||
logoWidth = card.spriteData.sprites[0].width,
|
|
||||||
isBEm = card is BemCard
|
|
||||||
)
|
|
||||||
|
|
||||||
val dimId = storageRepository
|
|
||||||
.dimDao()
|
|
||||||
.insertNewDim(cardModel)
|
|
||||||
|
|
||||||
val characters = card.characterStats.characterEntries
|
|
||||||
|
|
||||||
var spriteCounter = when (card is BemCard) {
|
|
||||||
true -> 55
|
|
||||||
false -> 10
|
|
||||||
}
|
|
||||||
|
|
||||||
val domainCharacters = mutableListOf<Character>()
|
|
||||||
|
|
||||||
for (index in 0 until characters.size) {
|
|
||||||
domainCharacters.add(
|
|
||||||
Character(
|
|
||||||
dimId = dimId,
|
|
||||||
monIndex = index,
|
|
||||||
name = card.spriteData.sprites[spriteCounter].pixelData,
|
|
||||||
stage = characters[index].stage,
|
|
||||||
attribute = characters[index].attribute,
|
|
||||||
baseHp = characters[index].hp,
|
|
||||||
baseBp = characters[index].dp,
|
|
||||||
baseAp = characters[index].ap,
|
|
||||||
sprite1 = card.spriteData.sprites[spriteCounter + 1].pixelData,
|
|
||||||
sprite2 = card.spriteData.sprites[spriteCounter + 2].pixelData,
|
|
||||||
nameWidth = card.spriteData.sprites[spriteCounter].width,
|
|
||||||
nameHeight = card.spriteData.sprites[spriteCounter].height,
|
|
||||||
spritesWidth = card.spriteData.sprites[spriteCounter + 1].width,
|
|
||||||
spritesHeight = card.spriteData.sprites[spriteCounter + 1].height
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
// TODO: Improve this
|
|
||||||
if (card is BemCard) {
|
|
||||||
spriteCounter += 14
|
|
||||||
} else {
|
|
||||||
when (index) {
|
|
||||||
0 -> spriteCounter += 6
|
|
||||||
1 -> spriteCounter += 7
|
|
||||||
else -> spriteCounter += 14
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
storageRepository
|
|
||||||
.characterDao()
|
|
||||||
.insertCharacter(*domainCharacters.toTypedArray())
|
|
||||||
|
|
||||||
val sprites = card.spriteData.sprites.map { sprite ->
|
|
||||||
Sprites(
|
|
||||||
id = 0,
|
|
||||||
sprite = sprite.pixelData,
|
|
||||||
width = sprite.width,
|
|
||||||
height = sprite.height
|
|
||||||
)
|
|
||||||
}
|
|
||||||
storageRepository
|
|
||||||
.characterDao()
|
|
||||||
.insertSprite(*sprites.toTypedArray())
|
|
||||||
}
|
|
||||||
inputStream?.close()
|
|
||||||
Toast.makeText(applicationContext, "Import successful!", Toast.LENGTH_SHORT).show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun MainApplication(settingsScreenController: SettingsScreenController, scanScreenController: ScanScreenControllerImpl) {
|
private fun MainApplication(
|
||||||
|
scanScreenController: ScanScreenControllerImpl,
|
||||||
|
settingsScreenController: SettingsScreenControllerImpl,
|
||||||
|
itemsScreenController: ItemsScreenControllerImpl,
|
||||||
|
adventureScreenController: AdventureScreenControllerImpl,
|
||||||
|
storageScreenController: StorageScreenControllerImpl,
|
||||||
|
homeScreenController: HomeScreenControllerImpl,
|
||||||
|
|
||||||
|
) {
|
||||||
AppNavigation(
|
AppNavigation(
|
||||||
applicationNavigationHandlers = AppNavigationHandlers(settingsScreenController, scanScreenController),
|
applicationNavigationHandlers = AppNavigationHandlers(
|
||||||
onClickImportCard = {
|
settingsScreenController,
|
||||||
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
|
scanScreenController,
|
||||||
addCategory(Intent.CATEGORY_OPENABLE)
|
itemsScreenController,
|
||||||
type = "*/*"
|
adventureScreenController,
|
||||||
}
|
storageScreenController,
|
||||||
activityResultLauncher.launch(intent)
|
homeScreenController
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleReceivedNfcCharacter(character: NfcCharacter): String {
|
|
||||||
nfcCharacter.value = character
|
|
||||||
|
|
||||||
val importStatus = addCharacterScannedIntoDatabase()
|
|
||||||
|
|
||||||
return importStatus
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
/*
|
|
||||||
TODO:
|
|
||||||
- Support for regular VB
|
|
||||||
|
|
||||||
The good news is that the theory behind inserting to the database should be working
|
|
||||||
now, it's a matter of implementing the functionality to parse dim/bem cards and use my
|
|
||||||
domain model.
|
|
||||||
*/
|
|
||||||
private fun addCharacterScannedIntoDatabase(): String {
|
|
||||||
val application = applicationContext as VBHelper
|
|
||||||
val storageRepository = application.container.db
|
|
||||||
|
|
||||||
val dimData = storageRepository
|
|
||||||
.dimDao()
|
|
||||||
.getDimById(nfcCharacter.value!!.dimId.toInt())
|
|
||||||
|
|
||||||
if (dimData == null) return "Card not found"
|
|
||||||
|
|
||||||
val cardCharData = storageRepository
|
|
||||||
.characterDao()
|
|
||||||
.getCharacterByMonIndex(nfcCharacter.value!!.charIndex.toInt(), dimData.id)
|
|
||||||
|
|
||||||
val characterData = UserCharacter(
|
|
||||||
charId = cardCharData.id,
|
|
||||||
stage = nfcCharacter.value!!.stage.toInt(),
|
|
||||||
attribute = nfcCharacter.value!!.attribute,
|
|
||||||
ageInDays = nfcCharacter.value!!.ageInDays.toInt(),
|
|
||||||
nextAdventureMissionStage = nfcCharacter.value!!.nextAdventureMissionStage.toInt(),
|
|
||||||
mood = nfcCharacter.value!!.mood.toInt(),
|
|
||||||
vitalPoints = nfcCharacter.value!!.vitalPoints.toInt(),
|
|
||||||
transformationCountdown = nfcCharacter.value!!.transformationCountdownInMinutes.toInt(),
|
|
||||||
injuryStatus = nfcCharacter.value!!.injuryStatus,
|
|
||||||
trophies = nfcCharacter.value!!.trophies.toInt(),
|
|
||||||
currentPhaseBattlesWon = nfcCharacter.value!!.currentPhaseBattlesWon.toInt(),
|
|
||||||
currentPhaseBattlesLost = nfcCharacter.value!!.currentPhaseBattlesLost.toInt(),
|
|
||||||
totalBattlesWon = nfcCharacter.value!!.totalBattlesWon.toInt(),
|
|
||||||
totalBattlesLost = nfcCharacter.value!!.totalBattlesLost.toInt(),
|
|
||||||
activityLevel = nfcCharacter.value!!.activityLevel.toInt(),
|
|
||||||
heartRateCurrent = nfcCharacter.value!!.heartRateCurrent.toInt(),
|
|
||||||
characterType = when (nfcCharacter.value) {
|
|
||||||
is BENfcCharacter -> DeviceType.BEDevice
|
|
||||||
else -> DeviceType.VBDevice
|
|
||||||
},
|
|
||||||
isActive = true
|
|
||||||
)
|
|
||||||
|
|
||||||
storageRepository
|
|
||||||
.userCharacterDao()
|
|
||||||
.clearActiveCharacter()
|
|
||||||
|
|
||||||
val characterId: Long = storageRepository
|
|
||||||
.userCharacterDao()
|
|
||||||
.insertCharacterData(characterData)
|
|
||||||
|
|
||||||
if (nfcCharacter.value is BENfcCharacter) {
|
|
||||||
val beCharacter = nfcCharacter.value as BENfcCharacter
|
|
||||||
val extraCharacterData = BECharacterData(
|
|
||||||
id = characterId,
|
|
||||||
trainingHp = beCharacter.trainingHp.toInt(),
|
|
||||||
trainingAp = beCharacter.trainingAp.toInt(),
|
|
||||||
trainingBp = beCharacter.trainingBp.toInt(),
|
|
||||||
remainingTrainingTimeInMinutes = beCharacter.remainingTrainingTimeInMinutes.toInt(),
|
|
||||||
itemEffectActivityLevelValue = beCharacter.itemEffectActivityLevelValue.toInt(),
|
|
||||||
itemEffectMentalStateValue = beCharacter.itemEffectMentalStateValue.toInt(),
|
|
||||||
itemEffectMentalStateMinutesRemaining = beCharacter.itemEffectMentalStateMinutesRemaining.toInt(),
|
|
||||||
itemEffectActivityLevelMinutesRemaining = beCharacter.itemEffectActivityLevelMinutesRemaining.toInt(),
|
|
||||||
itemEffectVitalPointsChangeValue = beCharacter.itemEffectVitalPointsChangeValue.toInt(),
|
|
||||||
itemEffectVitalPointsChangeMinutesRemaining = beCharacter.itemEffectVitalPointsChangeMinutesRemaining.toInt(),
|
|
||||||
abilityRarity = beCharacter.abilityRarity,
|
|
||||||
abilityType = beCharacter.abilityType.toInt(),
|
|
||||||
abilityBranch = beCharacter.abilityBranch.toInt(),
|
|
||||||
abilityReset = beCharacter.abilityReset.toInt(),
|
|
||||||
rank = beCharacter.abilityReset.toInt(),
|
|
||||||
itemType = beCharacter.itemType.toInt(),
|
|
||||||
itemMultiplier = beCharacter.itemMultiplier.toInt(),
|
|
||||||
itemRemainingTime = beCharacter.itemRemainingTime.toInt(),
|
|
||||||
otp0 = "", //beCharacter.value!!.otp0.toString(),
|
|
||||||
otp1 = "", //beCharacter.value!!.otp1.toString(),
|
|
||||||
minorVersion = beCharacter.characterCreationFirmwareVersion.minorVersion.toInt(),
|
|
||||||
majorVersion = beCharacter.characterCreationFirmwareVersion.majorVersion.toInt(),
|
|
||||||
)
|
)
|
||||||
|
)
|
||||||
storageRepository
|
|
||||||
.userCharacterDao()
|
|
||||||
.insertBECharacterData(extraCharacterData)
|
|
||||||
|
|
||||||
val transformationHistoryWatch = beCharacter.transformationHistory
|
|
||||||
transformationHistoryWatch.map { item ->
|
|
||||||
if (item.toCharIndex.toInt() != 255) {
|
|
||||||
val date = GregorianCalendar(item.year.toInt(), item.month.toInt(), item.day.toInt())
|
|
||||||
.time
|
|
||||||
.time
|
|
||||||
|
|
||||||
storageRepository
|
|
||||||
.characterDao()
|
|
||||||
.insertTransformation(characterId, item.toCharIndex.toInt(), dimData.id, date)
|
|
||||||
|
|
||||||
storageRepository
|
|
||||||
.dexDao()
|
|
||||||
.insertCharacter(item.toCharIndex.toInt(), dimData.id, date)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (nfcCharacter.value is VBNfcCharacter) {
|
|
||||||
return "Not implemented yet"
|
|
||||||
}
|
|
||||||
|
|
||||||
return "Done reading character!"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package com.github.nacabaro.vbhelper.components
|
package com.github.nacabaro.vbhelper.components
|
||||||
|
|
||||||
import android.util.Log
|
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.compose.foundation.Image
|
import androidx.compose.foundation.Image
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
@@ -18,29 +17,27 @@ import androidx.compose.runtime.Composable
|
|||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.ColorFilter
|
||||||
import androidx.compose.ui.graphics.FilterQuality
|
import androidx.compose.ui.graphics.FilterQuality
|
||||||
import androidx.compose.ui.graphics.asImageBitmap
|
import androidx.compose.ui.graphics.asImageBitmap
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
|
||||||
import com.github.nacabaro.vbhelper.utils.BitmapData
|
import com.github.nacabaro.vbhelper.utils.BitmapData
|
||||||
import com.github.nacabaro.vbhelper.utils.getBitmap
|
import com.github.nacabaro.vbhelper.utils.getBitmap
|
||||||
import androidx.compose.ui.graphics.Shape
|
import androidx.compose.ui.graphics.Shape
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.unit.Dp
|
|
||||||
import androidx.compose.ui.unit.TextUnit
|
|
||||||
import com.github.nacabaro.vbhelper.utils.getObscuredBitmap
|
import com.github.nacabaro.vbhelper.utils.getObscuredBitmap
|
||||||
import java.nio.ByteBuffer
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun CharacterEntry(
|
fun CharacterEntry(
|
||||||
icon: BitmapData,
|
icon: BitmapData,
|
||||||
obscure: Boolean = false,
|
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
|
obscure: Boolean = false,
|
||||||
|
disabled: Boolean = false,
|
||||||
shape: Shape = MaterialTheme.shapes.medium,
|
shape: Shape = MaterialTheme.shapes.medium,
|
||||||
multiplier: Int = 3,
|
multiplier: Int = 4,
|
||||||
onClick: () -> Unit = { }
|
onClick: () -> Unit = { }
|
||||||
) {
|
) {
|
||||||
val bitmap = remember (icon.bitmap) {
|
val bitmap = remember (icon.bitmap) {
|
||||||
@@ -52,7 +49,10 @@ fun CharacterEntry(
|
|||||||
|
|
||||||
Card(
|
Card(
|
||||||
shape = shape,
|
shape = shape,
|
||||||
onClick = onClick,
|
onClick = when (disabled) {
|
||||||
|
true -> { {} }
|
||||||
|
false -> onClick
|
||||||
|
},
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.aspectRatio(1f)
|
.aspectRatio(1f)
|
||||||
.padding(8.dp)
|
.padding(8.dp)
|
||||||
@@ -61,12 +61,16 @@ fun CharacterEntry(
|
|||||||
contentAlignment = Alignment.BottomCenter,
|
contentAlignment = Alignment.BottomCenter,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.padding(16.dp)
|
.padding(4.dp)
|
||||||
) {
|
) {
|
||||||
Image(
|
Image(
|
||||||
bitmap = imageBitmap,
|
bitmap = imageBitmap,
|
||||||
contentDescription = "Icon",
|
contentDescription = "Icon",
|
||||||
filterQuality = FilterQuality.None,
|
filterQuality = FilterQuality.None,
|
||||||
|
colorFilter = when (obscure) {
|
||||||
|
true -> ColorFilter.tint(color = MaterialTheme.colorScheme.secondary)
|
||||||
|
false -> null
|
||||||
|
},
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.size(dpSize)
|
.size(dpSize)
|
||||||
)
|
)
|
||||||
@@ -79,8 +83,6 @@ fun ItemDisplay(
|
|||||||
icon: Int,
|
icon: Int,
|
||||||
textValue: String,
|
textValue: String,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
iconSize: Dp = 48.dp,
|
|
||||||
textSize: TextUnit = 24.sp,
|
|
||||||
definition: String = "",
|
definition: String = "",
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
@@ -101,15 +103,14 @@ fun ItemDisplay(
|
|||||||
painter = painterResource(icon),
|
painter = painterResource(icon),
|
||||||
contentDescription = "Vitals",
|
contentDescription = "Vitals",
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
.fillMaxSize(0.5f)
|
||||||
.padding(8.dp)
|
.padding(8.dp)
|
||||||
.size(iconSize)
|
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
text = textValue,
|
text = textValue,
|
||||||
textAlign = TextAlign.Center,
|
textAlign = TextAlign.Center,
|
||||||
fontSize = textSize,
|
|
||||||
fontFamily = MaterialTheme.typography.titleLarge.fontFamily,
|
fontFamily = MaterialTheme.typography.titleLarge.fontFamily,
|
||||||
fontWeight = FontWeight.Bold
|
fontWeight = FontWeight.Bold,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,8 @@ fun TopBanner(
|
|||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
onGearClick: (() -> Unit)? = null,
|
onGearClick: (() -> Unit)? = null,
|
||||||
onBackClick: (() -> Unit)? = null,
|
onBackClick: (() -> Unit)? = null,
|
||||||
onScanClick: (() -> Unit)? = null
|
onScanClick: (() -> Unit)? = null,
|
||||||
|
onAdventureClick: (() -> Unit)? = null
|
||||||
) {
|
) {
|
||||||
Box( // Use Box to overlay elements
|
Box( // Use Box to overlay elements
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
@@ -49,7 +50,18 @@ fun TopBanner(
|
|||||||
contentDescription = "Settings"
|
contentDescription = "Settings"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
} else if (onAdventureClick != null) {
|
||||||
|
IconButton(
|
||||||
|
onClick = onAdventureClick,
|
||||||
|
modifier = Modifier
|
||||||
|
.align(Alignment.CenterEnd) // Place gear icon at the end
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
painter = painterResource(R.drawable.baseline_fort_24), // Use a gear icon
|
||||||
|
contentDescription = "Adventure"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (onScanClick != null) {
|
if (onScanClick != null) {
|
||||||
IconButton(
|
IconButton(
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.daos
|
||||||
|
|
||||||
|
import androidx.room.Dao
|
||||||
|
import androidx.room.Query
|
||||||
|
import com.github.nacabaro.vbhelper.dtos.CharacterDtos
|
||||||
|
|
||||||
|
|
||||||
|
@Dao
|
||||||
|
interface AdventureDao {
|
||||||
|
@Query("""
|
||||||
|
INSERT INTO Adventure (characterId, originalDuration, finishesAdventure)
|
||||||
|
VALUES (:characterId, :originalDuration, strftime('%s', 'now') + :timeInSeconds)
|
||||||
|
""")
|
||||||
|
fun insertNewAdventure(characterId: Long, originalDuration: Long, timeInSeconds: Long)
|
||||||
|
|
||||||
|
@Query("""
|
||||||
|
SELECT COUNT(*) FROM Adventure
|
||||||
|
""")
|
||||||
|
fun getAdventureCount(): Int
|
||||||
|
|
||||||
|
@Query("""
|
||||||
|
SELECT
|
||||||
|
uc.*,
|
||||||
|
c.stage,
|
||||||
|
c.attribute,
|
||||||
|
c.sprite1 AS spriteIdle,
|
||||||
|
c.spritesWidth AS spriteWidth,
|
||||||
|
c.spritesHeight AS spriteHeight,
|
||||||
|
d.isBEm as isBemCard,
|
||||||
|
a.finishesAdventure AS finishesAdventure,
|
||||||
|
a.originalDuration AS originalTimeInMinutes
|
||||||
|
FROM UserCharacter uc
|
||||||
|
JOIN Character c ON uc.charId = c.id
|
||||||
|
JOIN Card d ON c.dimId = d.id
|
||||||
|
JOIN Adventure a ON uc.id = a.characterId
|
||||||
|
""")
|
||||||
|
suspend fun getAdventureCharacters(): List<CharacterDtos.AdventureCharacterWithSprites>
|
||||||
|
|
||||||
|
@Query("""
|
||||||
|
DELETE FROM Adventure
|
||||||
|
WHERE characterId = :characterId
|
||||||
|
""")
|
||||||
|
suspend fun deleteAdventure(characterId: Long)
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.daos
|
||||||
|
|
||||||
|
import androidx.room.Dao
|
||||||
|
import androidx.room.Query
|
||||||
|
import com.github.nacabaro.vbhelper.dtos.ItemDtos
|
||||||
|
|
||||||
|
@Dao
|
||||||
|
interface ItemDao {
|
||||||
|
@Query(
|
||||||
|
"""
|
||||||
|
SELECT *
|
||||||
|
FROM Items
|
||||||
|
ORDER BY Items.itemIcon ASC
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
suspend fun getAllItems(): List<ItemDtos.ItemsWithQuantities>
|
||||||
|
|
||||||
|
@Query(
|
||||||
|
"""
|
||||||
|
SELECT *
|
||||||
|
FROM Items
|
||||||
|
WHERE quantity > 0
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
suspend fun getAllUserItems(): List<ItemDtos.ItemsWithQuantities>
|
||||||
|
|
||||||
|
@Query(
|
||||||
|
"""
|
||||||
|
SELECT *
|
||||||
|
FROM Items
|
||||||
|
WHERE Items.id = :itemId
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
fun getItem(itemId: Long): ItemDtos.ItemsWithQuantities
|
||||||
|
|
||||||
|
@Query(
|
||||||
|
"""
|
||||||
|
UPDATE Items
|
||||||
|
SET quantity = quantity - 1
|
||||||
|
WHERE id = :itemId
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
fun useItem(itemId: Long)
|
||||||
|
|
||||||
|
@Query(
|
||||||
|
"""
|
||||||
|
UPDATE Items
|
||||||
|
SET quantity = quantity + :itemAmount
|
||||||
|
WHERE id = :itemId
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
suspend fun purchaseItem(itemId: Long, itemAmount: Int)
|
||||||
|
}
|
||||||
@@ -4,6 +4,8 @@ import androidx.room.Dao
|
|||||||
import androidx.room.Insert
|
import androidx.room.Insert
|
||||||
import androidx.room.OnConflictStrategy
|
import androidx.room.OnConflictStrategy
|
||||||
import androidx.room.Query
|
import androidx.room.Query
|
||||||
|
import androidx.room.Upsert
|
||||||
|
import com.github.nacabaro.vbhelper.domain.characters.Character
|
||||||
import com.github.nacabaro.vbhelper.domain.device_data.UserCharacter
|
import com.github.nacabaro.vbhelper.domain.device_data.UserCharacter
|
||||||
import com.github.nacabaro.vbhelper.domain.device_data.BECharacterData
|
import com.github.nacabaro.vbhelper.domain.device_data.BECharacterData
|
||||||
import com.github.nacabaro.vbhelper.domain.device_data.TransformationHistory
|
import com.github.nacabaro.vbhelper.domain.device_data.TransformationHistory
|
||||||
@@ -17,6 +19,12 @@ interface UserCharacterDao {
|
|||||||
@Insert
|
@Insert
|
||||||
fun insertBECharacterData(characterData: BECharacterData)
|
fun insertBECharacterData(characterData: BECharacterData)
|
||||||
|
|
||||||
|
@Upsert
|
||||||
|
fun updateCharacter(character: UserCharacter)
|
||||||
|
|
||||||
|
@Upsert
|
||||||
|
fun updateBECharacterData(characterData: BECharacterData)
|
||||||
|
|
||||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||||
fun insertTransformationHistory(vararg transformationHistory: TransformationHistory)
|
fun insertTransformationHistory(vararg transformationHistory: TransformationHistory)
|
||||||
|
|
||||||
@@ -38,17 +46,46 @@ interface UserCharacterDao {
|
|||||||
"""
|
"""
|
||||||
SELECT
|
SELECT
|
||||||
uc.*,
|
uc.*,
|
||||||
|
c.stage,
|
||||||
|
c.attribute,
|
||||||
c.sprite1 AS spriteIdle,
|
c.sprite1 AS spriteIdle,
|
||||||
c.spritesWidth AS spriteWidth,
|
c.spritesWidth AS spriteWidth,
|
||||||
c.spritesHeight AS spriteHeight,
|
c.spritesHeight AS spriteHeight,
|
||||||
d.isBEm as isBemCard
|
c.name as nameSprite,
|
||||||
|
c.nameWidth as nameSpriteWidth,
|
||||||
|
c.nameHeight as nameSpriteHeight,
|
||||||
|
d.isBEm as isBemCard,
|
||||||
|
a.characterId = uc.id as isInAdventure
|
||||||
FROM UserCharacter uc
|
FROM UserCharacter uc
|
||||||
JOIN Character c ON uc.charId = c.id
|
JOIN Character c ON uc.charId = c.id
|
||||||
JOIN Card d ON c.dimId = d.id
|
JOIN Card d ON c.dimId = d.id
|
||||||
|
LEFT JOIN Adventure a ON a.characterId = uc.id
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
suspend fun getAllCharacters(): List<CharacterDtos.CharacterWithSprites>
|
suspend fun getAllCharacters(): List<CharacterDtos.CharacterWithSprites>
|
||||||
|
|
||||||
|
@Query(
|
||||||
|
"""
|
||||||
|
SELECT
|
||||||
|
uc.*,
|
||||||
|
c.stage,
|
||||||
|
c.attribute,
|
||||||
|
c.sprite1 AS spriteIdle,
|
||||||
|
c.spritesWidth AS spriteWidth,
|
||||||
|
c.spritesHeight AS spriteHeight,
|
||||||
|
c.name as nameSprite,
|
||||||
|
c.nameWidth as nameSpriteWidth,
|
||||||
|
c.nameHeight as nameSpriteHeight,
|
||||||
|
d.isBEm as isBemCard,
|
||||||
|
a.characterId = uc.id as isInAdventure
|
||||||
|
FROM UserCharacter uc
|
||||||
|
JOIN Character c ON uc.charId = c.id
|
||||||
|
JOIN Card d ON c.dimId = d.id
|
||||||
|
LEFT JOIN Adventure a ON a.characterId = uc.id
|
||||||
|
WHERE uc.id = :id
|
||||||
|
""")
|
||||||
|
suspend fun getCharacterWithSprites(id: Long): CharacterDtos.CharacterWithSprites
|
||||||
|
|
||||||
@Query("SELECT * FROM UserCharacter WHERE id = :id")
|
@Query("SELECT * FROM UserCharacter WHERE id = :id")
|
||||||
suspend fun getCharacter(id: Long): UserCharacter
|
suspend fun getCharacter(id: Long): UserCharacter
|
||||||
|
|
||||||
@@ -59,17 +96,23 @@ interface UserCharacterDao {
|
|||||||
"""
|
"""
|
||||||
SELECT
|
SELECT
|
||||||
uc.*,
|
uc.*,
|
||||||
|
c.stage,
|
||||||
|
c.attribute,
|
||||||
c.sprite1 AS spriteIdle,
|
c.sprite1 AS spriteIdle,
|
||||||
c.spritesWidth AS spriteWidth,
|
c.spritesWidth AS spriteWidth,
|
||||||
c.spritesHeight AS spriteHeight,
|
c.spritesHeight AS spriteHeight,
|
||||||
d.isBEm as isBemCard
|
c.name as nameSprite,
|
||||||
|
c.nameWidth as nameSpriteWidth,
|
||||||
|
c.nameHeight as nameSpriteHeight,
|
||||||
|
d.isBEm as isBemCard,
|
||||||
|
a.characterId as isInAdventure
|
||||||
FROM UserCharacter uc
|
FROM UserCharacter uc
|
||||||
JOIN Character c ON uc.charId = c.id
|
JOIN Character c ON uc.charId = c.id
|
||||||
JOIN Card d ON c.dimId = d.id
|
JOIN Card d ON c.dimId = d.id
|
||||||
|
LEFT JOIN Adventure a ON a.characterId = uc.id
|
||||||
WHERE uc.isActive = 1
|
WHERE uc.isActive = 1
|
||||||
LIMIT 1
|
LIMIT 1
|
||||||
"""
|
""")
|
||||||
)
|
|
||||||
suspend fun getActiveCharacter(): CharacterDtos.CharacterWithSprites?
|
suspend fun getActiveCharacter(): CharacterDtos.CharacterWithSprites?
|
||||||
|
|
||||||
@Query("DELETE FROM UserCharacter WHERE id = :id")
|
@Query("DELETE FROM UserCharacter WHERE id = :id")
|
||||||
@@ -80,4 +123,15 @@ interface UserCharacterDao {
|
|||||||
|
|
||||||
@Query("UPDATE UserCharacter SET isActive = 1 WHERE id = :id")
|
@Query("UPDATE UserCharacter SET isActive = 1 WHERE id = :id")
|
||||||
fun setActiveCharacter(id: Long)
|
fun setActiveCharacter(id: Long)
|
||||||
|
|
||||||
|
@Query(
|
||||||
|
"""
|
||||||
|
SELECT c.*
|
||||||
|
FROM Character c
|
||||||
|
join UserCharacter uc on c.id = uc.charId
|
||||||
|
where uc.id = :charId
|
||||||
|
LIMIT 1
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
suspend fun getCharacterInfo(charId: Long): Character
|
||||||
}
|
}
|
||||||
@@ -2,17 +2,21 @@ package com.github.nacabaro.vbhelper.database
|
|||||||
|
|
||||||
import androidx.room.Database
|
import androidx.room.Database
|
||||||
import androidx.room.RoomDatabase
|
import androidx.room.RoomDatabase
|
||||||
|
import com.github.nacabaro.vbhelper.daos.AdventureDao
|
||||||
import com.github.nacabaro.vbhelper.daos.CharacterDao
|
import com.github.nacabaro.vbhelper.daos.CharacterDao
|
||||||
import com.github.nacabaro.vbhelper.daos.DexDao
|
import com.github.nacabaro.vbhelper.daos.DexDao
|
||||||
import com.github.nacabaro.vbhelper.daos.DiMDao
|
import com.github.nacabaro.vbhelper.daos.DiMDao
|
||||||
|
import com.github.nacabaro.vbhelper.daos.ItemDao
|
||||||
import com.github.nacabaro.vbhelper.daos.UserCharacterDao
|
import com.github.nacabaro.vbhelper.daos.UserCharacterDao
|
||||||
import com.github.nacabaro.vbhelper.domain.characters.Character
|
import com.github.nacabaro.vbhelper.domain.characters.Character
|
||||||
import com.github.nacabaro.vbhelper.domain.characters.Card
|
import com.github.nacabaro.vbhelper.domain.characters.Card
|
||||||
import com.github.nacabaro.vbhelper.domain.Sprites
|
import com.github.nacabaro.vbhelper.domain.Sprites
|
||||||
|
import com.github.nacabaro.vbhelper.domain.characters.Adventure
|
||||||
import com.github.nacabaro.vbhelper.domain.characters.Dex
|
import com.github.nacabaro.vbhelper.domain.characters.Dex
|
||||||
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.TransformationHistory
|
import com.github.nacabaro.vbhelper.domain.device_data.TransformationHistory
|
||||||
import com.github.nacabaro.vbhelper.domain.device_data.UserCharacter
|
import com.github.nacabaro.vbhelper.domain.device_data.UserCharacter
|
||||||
|
import com.github.nacabaro.vbhelper.domain.items.Items
|
||||||
|
|
||||||
@Database(
|
@Database(
|
||||||
version = 1,
|
version = 1,
|
||||||
@@ -23,7 +27,9 @@ import com.github.nacabaro.vbhelper.domain.device_data.UserCharacter
|
|||||||
UserCharacter::class,
|
UserCharacter::class,
|
||||||
BECharacterData::class,
|
BECharacterData::class,
|
||||||
TransformationHistory::class,
|
TransformationHistory::class,
|
||||||
Dex::class
|
Dex::class,
|
||||||
|
Items::class,
|
||||||
|
Adventure::class
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
abstract class AppDatabase : RoomDatabase() {
|
abstract class AppDatabase : RoomDatabase() {
|
||||||
@@ -31,4 +37,6 @@ abstract class AppDatabase : RoomDatabase() {
|
|||||||
abstract fun characterDao(): CharacterDao
|
abstract fun characterDao(): CharacterDao
|
||||||
abstract fun userCharacterDao(): UserCharacterDao
|
abstract fun userCharacterDao(): UserCharacterDao
|
||||||
abstract fun dexDao(): DexDao
|
abstract fun dexDao(): DexDao
|
||||||
|
abstract fun itemDao(): ItemDao
|
||||||
|
abstract fun adventureDao(): AdventureDao
|
||||||
}
|
}
|
||||||
@@ -22,7 +22,9 @@ class DefaultAppContainer(private val context: Context) : AppContainer {
|
|||||||
context = context,
|
context = context,
|
||||||
klass = AppDatabase::class.java,
|
klass = AppDatabase::class.java,
|
||||||
"internalDb"
|
"internalDb"
|
||||||
).build()
|
)
|
||||||
|
.createFromAsset("items.db")
|
||||||
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
override val dataStoreSecretsRepository = DataStoreSecretsRepository(context.secretsStore)
|
override val dataStoreSecretsRepository = DataStoreSecretsRepository(context.secretsStore)
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.domain.characters
|
||||||
|
|
||||||
|
import androidx.room.Entity
|
||||||
|
import androidx.room.ForeignKey
|
||||||
|
import androidx.room.PrimaryKey
|
||||||
|
import com.github.nacabaro.vbhelper.domain.device_data.UserCharacter
|
||||||
|
|
||||||
|
@Entity(
|
||||||
|
foreignKeys = [
|
||||||
|
ForeignKey(
|
||||||
|
entity = UserCharacter::class,
|
||||||
|
parentColumns = ["id"],
|
||||||
|
childColumns = ["characterId"],
|
||||||
|
onDelete = ForeignKey.CASCADE
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
data class Adventure(
|
||||||
|
@PrimaryKey val characterId: Long,
|
||||||
|
val originalDuration: Long,
|
||||||
|
val finishesAdventure: Long
|
||||||
|
)
|
||||||
@@ -3,6 +3,7 @@ package com.github.nacabaro.vbhelper.domain.characters
|
|||||||
import androidx.room.Entity
|
import androidx.room.Entity
|
||||||
import androidx.room.PrimaryKey
|
import androidx.room.PrimaryKey
|
||||||
import androidx.room.ForeignKey
|
import androidx.room.ForeignKey
|
||||||
|
import com.github.cfogrady.vbnfc.data.NfcCharacter
|
||||||
|
|
||||||
@Entity(
|
@Entity(
|
||||||
foreignKeys = [
|
foreignKeys = [
|
||||||
@@ -14,13 +15,18 @@ import androidx.room.ForeignKey
|
|||||||
)
|
)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
/*
|
||||||
|
* Character represents a character on a DIM card. There should only be one of these per dimId
|
||||||
|
* and monIndex.
|
||||||
|
* TODO: Customs will mean this should be unique per cardName and monIndex
|
||||||
|
*/
|
||||||
data class Character (
|
data class Character (
|
||||||
@PrimaryKey(autoGenerate = true) val id: Long = 0,
|
@PrimaryKey(autoGenerate = true) val id: Long = 0,
|
||||||
val dimId: Long,
|
val dimId: Long,
|
||||||
val monIndex: Int,
|
val monIndex: Int,
|
||||||
val name: ByteArray,
|
val name: ByteArray,
|
||||||
val stage: Int, // These should be replaced with enums
|
val stage: Int, // These should be replaced with enums
|
||||||
val attribute: Int, // This one too
|
val attribute: NfcCharacter.Attribute, // This one too
|
||||||
val baseHp: Int,
|
val baseHp: Int,
|
||||||
val baseBp: Int,
|
val baseBp: Int,
|
||||||
val baseAp: Int,
|
val baseAp: Int,
|
||||||
|
|||||||
+4
-4
@@ -20,7 +20,7 @@ data class BECharacterData (
|
|||||||
val trainingHp: Int,
|
val trainingHp: Int,
|
||||||
val trainingAp: Int,
|
val trainingAp: Int,
|
||||||
val trainingBp: Int,
|
val trainingBp: Int,
|
||||||
val remainingTrainingTimeInMinutes: Int,
|
var remainingTrainingTimeInMinutes: Int,
|
||||||
val itemEffectMentalStateValue: Int,
|
val itemEffectMentalStateValue: Int,
|
||||||
val itemEffectMentalStateMinutesRemaining: Int,
|
val itemEffectMentalStateMinutesRemaining: Int,
|
||||||
val itemEffectActivityLevelValue: Int,
|
val itemEffectActivityLevelValue: Int,
|
||||||
@@ -32,9 +32,9 @@ data class BECharacterData (
|
|||||||
val abilityBranch: Int,
|
val abilityBranch: Int,
|
||||||
val abilityReset: Int,
|
val abilityReset: Int,
|
||||||
val rank: Int,
|
val rank: Int,
|
||||||
val itemType: Int,
|
var itemType: Int,
|
||||||
val itemMultiplier: Int,
|
var itemMultiplier: Int,
|
||||||
val itemRemainingTime: Int,
|
var itemRemainingTime: Int,
|
||||||
val otp0: String,
|
val otp0: String,
|
||||||
val otp1: String,
|
val otp1: String,
|
||||||
val minorVersion: Int,
|
val minorVersion: Int,
|
||||||
|
|||||||
@@ -17,11 +17,12 @@ import com.github.nacabaro.vbhelper.domain.characters.Character
|
|||||||
)
|
)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
/**
|
||||||
|
* UserCharacter represents and instance of a character. The charId should map to a Character
|
||||||
|
*/
|
||||||
data class UserCharacter (
|
data class UserCharacter (
|
||||||
@PrimaryKey(autoGenerate = true) val id: Long = 0,
|
@PrimaryKey(autoGenerate = true) val id: Long = 0,
|
||||||
var charId: Long,
|
var charId: Long,
|
||||||
var stage: Int,
|
|
||||||
var attribute: NfcCharacter.Attribute,
|
|
||||||
var ageInDays: Int,
|
var ageInDays: Int,
|
||||||
var nextAdventureMissionStage: Int, // next adventure mission stage on the character's dim
|
var nextAdventureMissionStage: Int, // next adventure mission stage on the character's dim
|
||||||
var mood: Int,
|
var mood: Int,
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.domain.items
|
||||||
|
|
||||||
|
import androidx.room.Entity
|
||||||
|
import androidx.room.PrimaryKey
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
data class Items(
|
||||||
|
@PrimaryKey val id: Long,
|
||||||
|
val name: String,
|
||||||
|
val description: String,
|
||||||
|
val itemIcon: Int,
|
||||||
|
val itemLength: Int,
|
||||||
|
val price: Int,
|
||||||
|
val quantity: Int
|
||||||
|
)
|
||||||
@@ -27,7 +27,11 @@ object CharacterDtos {
|
|||||||
val spriteIdle: ByteArray,
|
val spriteIdle: ByteArray,
|
||||||
val spriteWidth: Int,
|
val spriteWidth: Int,
|
||||||
val spriteHeight: Int,
|
val spriteHeight: Int,
|
||||||
val isBemCard: Boolean
|
val nameSprite: ByteArray,
|
||||||
|
val nameSpriteWidth: Int,
|
||||||
|
val nameSpriteHeight: Int,
|
||||||
|
val isBemCard: Boolean,
|
||||||
|
val isInAdventure: Boolean
|
||||||
)
|
)
|
||||||
|
|
||||||
data class DiMInfo(
|
data class DiMInfo(
|
||||||
@@ -51,4 +55,31 @@ object CharacterDtos {
|
|||||||
val spriteHeight: Int,
|
val spriteHeight: Int,
|
||||||
val discoveredOn: Long?
|
val discoveredOn: Long?
|
||||||
)
|
)
|
||||||
|
|
||||||
|
data class AdventureCharacterWithSprites(
|
||||||
|
var id: Long = 0,
|
||||||
|
var charId: Long,
|
||||||
|
var stage: Int,
|
||||||
|
var attribute: NfcCharacter.Attribute,
|
||||||
|
var ageInDays: Int,
|
||||||
|
var nextAdventureMissionStage: Int, // next adventure mission stage on the character's dim
|
||||||
|
var mood: Int,
|
||||||
|
var vitalPoints: Int,
|
||||||
|
var transformationCountdown: Int,
|
||||||
|
var injuryStatus: NfcCharacter.InjuryStatus,
|
||||||
|
var trophies: Int,
|
||||||
|
var currentPhaseBattlesWon: Int,
|
||||||
|
var currentPhaseBattlesLost: Int,
|
||||||
|
var totalBattlesWon: Int,
|
||||||
|
var totalBattlesLost: Int,
|
||||||
|
var activityLevel: Int,
|
||||||
|
var heartRateCurrent: Int,
|
||||||
|
var characterType: DeviceType,
|
||||||
|
val spriteIdle: ByteArray,
|
||||||
|
val spriteWidth: Int,
|
||||||
|
val spriteHeight: Int,
|
||||||
|
val isBemCard: Boolean,
|
||||||
|
val finishesAdventure: Long,
|
||||||
|
val originalTimeInMinutes: Long
|
||||||
|
)
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.dtos
|
||||||
|
|
||||||
|
|
||||||
|
object ItemDtos {
|
||||||
|
data class ItemsWithQuantities(
|
||||||
|
val id: Long,
|
||||||
|
val name: String,
|
||||||
|
val description: String,
|
||||||
|
val itemIcon: Int,
|
||||||
|
val itemLength: Int,
|
||||||
|
val price: Int,
|
||||||
|
val quantity: Int,
|
||||||
|
)
|
||||||
|
|
||||||
|
data class PurchasedItem(
|
||||||
|
val itemId: Long,
|
||||||
|
val itemName: String,
|
||||||
|
val itemDescription: String,
|
||||||
|
val itemIcon: Int,
|
||||||
|
val itemLength: Int,
|
||||||
|
val itemAmount: Int
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
package com.github.nacabaro.vbhelper.navigation
|
package com.github.nacabaro.vbhelper.navigation
|
||||||
|
|
||||||
import android.util.Log
|
|
||||||
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
|
||||||
@@ -12,20 +11,33 @@ import com.github.nacabaro.vbhelper.screens.BattlesScreen
|
|||||||
import com.github.nacabaro.vbhelper.screens.DexScreen
|
import com.github.nacabaro.vbhelper.screens.DexScreen
|
||||||
import com.github.nacabaro.vbhelper.screens.DiMScreen
|
import com.github.nacabaro.vbhelper.screens.DiMScreen
|
||||||
import com.github.nacabaro.vbhelper.screens.homeScreens.HomeScreen
|
import com.github.nacabaro.vbhelper.screens.homeScreens.HomeScreen
|
||||||
import com.github.nacabaro.vbhelper.screens.ItemsScreen
|
import com.github.nacabaro.vbhelper.screens.itemsScreen.ItemsScreen
|
||||||
import com.github.nacabaro.vbhelper.screens.scanScreen.ScanScreen
|
import com.github.nacabaro.vbhelper.screens.scanScreen.ScanScreen
|
||||||
import com.github.nacabaro.vbhelper.screens.scanScreen.ScanScreenControllerImpl
|
import com.github.nacabaro.vbhelper.screens.scanScreen.ScanScreenControllerImpl
|
||||||
import com.github.nacabaro.vbhelper.screens.SettingsScreen
|
import com.github.nacabaro.vbhelper.screens.settingsScreen.SettingsScreen
|
||||||
import com.github.nacabaro.vbhelper.screens.SettingsScreenController
|
|
||||||
import com.github.nacabaro.vbhelper.screens.SpriteViewer
|
import com.github.nacabaro.vbhelper.screens.SpriteViewer
|
||||||
import com.github.nacabaro.vbhelper.screens.StorageScreen
|
import com.github.nacabaro.vbhelper.screens.homeScreens.HomeScreenControllerImpl
|
||||||
|
import com.github.nacabaro.vbhelper.screens.storageScreen.StorageScreen
|
||||||
|
import com.github.nacabaro.vbhelper.screens.itemsScreen.ChooseCharacterScreen
|
||||||
|
import com.github.nacabaro.vbhelper.screens.itemsScreen.ItemsScreenControllerImpl
|
||||||
|
import com.github.nacabaro.vbhelper.screens.settingsScreen.SettingsScreenControllerImpl
|
||||||
|
import com.github.nacabaro.vbhelper.screens.adventureScreen.AdventureScreen
|
||||||
|
import com.github.nacabaro.vbhelper.screens.adventureScreen.AdventureScreenControllerImpl
|
||||||
|
import com.github.nacabaro.vbhelper.screens.settingsScreen.CreditsScreen
|
||||||
|
import com.github.nacabaro.vbhelper.screens.storageScreen.StorageScreenControllerImpl
|
||||||
|
|
||||||
data class AppNavigationHandlers(val settingsScreenController: SettingsScreenController, val scanScreenController: ScanScreenControllerImpl)
|
data class AppNavigationHandlers(
|
||||||
|
val settingsScreenController: SettingsScreenControllerImpl,
|
||||||
|
val scanScreenController: ScanScreenControllerImpl,
|
||||||
|
val itemsScreenController: ItemsScreenControllerImpl,
|
||||||
|
val adventureScreenController: AdventureScreenControllerImpl,
|
||||||
|
val storageScreenController: StorageScreenControllerImpl,
|
||||||
|
val homeScreenController: HomeScreenControllerImpl
|
||||||
|
)
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun AppNavigation(
|
fun AppNavigation(
|
||||||
applicationNavigationHandlers: AppNavigationHandlers,
|
applicationNavigationHandlers: AppNavigationHandlers,
|
||||||
onClickImportCard: () -> Unit,
|
|
||||||
) {
|
) {
|
||||||
val navController = rememberNavController()
|
val navController = rememberNavController()
|
||||||
|
|
||||||
@@ -45,12 +57,15 @@ fun AppNavigation(
|
|||||||
}
|
}
|
||||||
composable(NavigationItems.Home.route) {
|
composable(NavigationItems.Home.route) {
|
||||||
HomeScreen(
|
HomeScreen(
|
||||||
navController = navController
|
navController = navController,
|
||||||
|
homeScreenController = applicationNavigationHandlers.homeScreenController
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
composable(NavigationItems.Storage.route) {
|
composable(NavigationItems.Storage.route) {
|
||||||
StorageScreen(
|
StorageScreen(
|
||||||
navController = navController
|
navController = navController,
|
||||||
|
adventureScreenController = applicationNavigationHandlers.adventureScreenController,
|
||||||
|
storageScreenController = applicationNavigationHandlers.storageScreenController
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
composable(NavigationItems.Scan.route) {
|
composable(NavigationItems.Scan.route) {
|
||||||
@@ -71,8 +86,7 @@ fun AppNavigation(
|
|||||||
composable(NavigationItems.Settings.route) {
|
composable(NavigationItems.Settings.route) {
|
||||||
SettingsScreen(
|
SettingsScreen(
|
||||||
navController = navController,
|
navController = navController,
|
||||||
settingsScreenController = applicationNavigationHandlers.settingsScreenController,
|
settingsScreenController = applicationNavigationHandlers.settingsScreenController
|
||||||
onClickImportCard = onClickImportCard
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
composable(NavigationItems.Viewer.route) {
|
composable(NavigationItems.Viewer.route) {
|
||||||
@@ -94,6 +108,29 @@ fun AppNavigation(
|
|||||||
navController = navController
|
navController = navController
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
composable(NavigationItems.ApplyItem.route) {
|
||||||
|
val itemId = it.arguments?.getString("itemId")
|
||||||
|
if (itemId != null) {
|
||||||
|
ChooseCharacterScreen(
|
||||||
|
itemsScreenController = applicationNavigationHandlers
|
||||||
|
.itemsScreenController,
|
||||||
|
navController = navController,
|
||||||
|
itemId = itemId.toLong()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
composable(NavigationItems.Adventure.route) {
|
||||||
|
AdventureScreen(
|
||||||
|
navController = navController,
|
||||||
|
storageScreenController = applicationNavigationHandlers
|
||||||
|
.adventureScreenController
|
||||||
|
)
|
||||||
|
}
|
||||||
|
composable(NavigationItems.Credits.route) {
|
||||||
|
CreditsScreen(
|
||||||
|
navController = navController
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ fun BottomNavigationBar(navController: NavController) {
|
|||||||
NavigationItems.Dex,
|
NavigationItems.Dex,
|
||||||
NavigationItems.Storage,
|
NavigationItems.Storage,
|
||||||
)
|
)
|
||||||
|
|
||||||
NavigationBar {
|
NavigationBar {
|
||||||
val currentBackStackEntry = navController.currentBackStackEntryAsState()
|
val currentBackStackEntry = navController.currentBackStackEntryAsState()
|
||||||
val currentRoute = currentBackStackEntry.value?.destination?.route
|
val currentRoute = currentBackStackEntry.value?.destination?.route
|
||||||
|
|||||||
@@ -16,4 +16,9 @@ sealed class NavigationItems (
|
|||||||
object Viewer : NavigationItems("Viewer", R.drawable.baseline_image_24, "Viewer")
|
object Viewer : NavigationItems("Viewer", R.drawable.baseline_image_24, "Viewer")
|
||||||
object CardView : NavigationItems("Card/{cardId}", R.drawable.baseline_image_24, "Card")
|
object CardView : NavigationItems("Card/{cardId}", R.drawable.baseline_image_24, "Card")
|
||||||
object Items : NavigationItems("Items", R.drawable.baseline_data_24, "Items")
|
object Items : NavigationItems("Items", R.drawable.baseline_data_24, "Items")
|
||||||
|
object MyItems : NavigationItems("MyItems", R.drawable.baseline_data_24, "My items")
|
||||||
|
object ItemsStore : NavigationItems("ItemsStore", R.drawable.baseline_data_24, "Items store")
|
||||||
|
object ApplyItem : NavigationItems("ApplyItem/{itemId}", R.drawable.baseline_data_24, "Apply item")
|
||||||
|
object Adventure : NavigationItems("Adventure", R.drawable.baseline_fort_24, "Adventure")
|
||||||
|
object Credits : NavigationItems("Credits", R.drawable.baseline_data_24, "Credits")
|
||||||
}
|
}
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
package com.github.nacabaro.vbhelper.screens
|
|
||||||
|
|
||||||
import androidx.compose.material3.Text
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.navigation.NavController
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun ItemsScreen(
|
|
||||||
navController: NavController
|
|
||||||
) {
|
|
||||||
Text(text = "Items")
|
|
||||||
}
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
package com.github.nacabaro.vbhelper.screens
|
|
||||||
|
|
||||||
import android.net.Uri
|
|
||||||
import android.widget.Toast
|
|
||||||
import androidx.activity.ComponentActivity
|
|
||||||
import androidx.activity.result.ActivityResultLauncher
|
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
|
||||||
import androidx.lifecycle.lifecycleScope
|
|
||||||
import com.github.nacabaro.vbhelper.source.SecretsImporter
|
|
||||||
import com.github.nacabaro.vbhelper.source.SecretsRepository
|
|
||||||
import com.github.nacabaro.vbhelper.source.proto.Secrets
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
|
|
||||||
data class SettingsScreenController(val apkFilePickLauncher: ActivityResultLauncher<Array<String>>) {
|
|
||||||
|
|
||||||
class Factory(private val componentActivity: ComponentActivity, private val secretsImporter: SecretsImporter, private val secretsRepository: SecretsRepository) {
|
|
||||||
|
|
||||||
fun buildSettingScreenHandlers(): SettingsScreenController {
|
|
||||||
return SettingsScreenController(
|
|
||||||
apkFilePickLauncher = buildFilePickerActivityLauncher(this::importApk)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun buildFilePickerActivityLauncher(onResult : (Uri?) ->Unit): ActivityResultLauncher<Array<String>> {
|
|
||||||
return componentActivity.registerForActivityResult(ActivityResultContracts.OpenDocument()) {
|
|
||||||
onResult.invoke(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun importApk(uri: Uri?) {
|
|
||||||
if(uri == null) {
|
|
||||||
componentActivity.runOnUiThread {
|
|
||||||
Toast.makeText(componentActivity, "APK Import Cancelled", Toast.LENGTH_SHORT)
|
|
||||||
.show()
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
componentActivity.lifecycleScope.launch(Dispatchers.IO) {
|
|
||||||
componentActivity.contentResolver.openInputStream(uri).use {
|
|
||||||
if(it == null) {
|
|
||||||
componentActivity.runOnUiThread {
|
|
||||||
Toast.makeText(
|
|
||||||
componentActivity,
|
|
||||||
"Selected file is empty!",
|
|
||||||
Toast.LENGTH_SHORT
|
|
||||||
).show()
|
|
||||||
}
|
|
||||||
return@launch
|
|
||||||
}
|
|
||||||
var secrets: Secrets? = null
|
|
||||||
try {
|
|
||||||
secrets = secretsImporter.importSecrets(it)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
componentActivity.runOnUiThread {
|
|
||||||
Toast.makeText(componentActivity, "Secrets import failed. Please only select the official Vital Arena App 2.1.0 APK.", Toast.LENGTH_SHORT).show()
|
|
||||||
}
|
|
||||||
return@launch
|
|
||||||
}
|
|
||||||
componentActivity.lifecycleScope.launch(Dispatchers.IO) {
|
|
||||||
secretsRepository.updateSecrets(secrets)
|
|
||||||
}.invokeOnCompletion {
|
|
||||||
componentActivity.runOnUiThread {
|
|
||||||
Toast.makeText(componentActivity, "Secrets successfully imported. Connections with devices are now possible.", Toast.LENGTH_SHORT).show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,196 +0,0 @@
|
|||||||
package com.github.nacabaro.vbhelper.screens
|
|
||||||
|
|
||||||
import android.util.Log
|
|
||||||
import androidx.compose.foundation.gestures.Orientation
|
|
||||||
import androidx.compose.foundation.gestures.scrollable
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
|
||||||
import androidx.compose.foundation.layout.Column
|
|
||||||
import androidx.compose.foundation.layout.Row
|
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
|
||||||
import androidx.compose.foundation.layout.padding
|
|
||||||
import androidx.compose.foundation.layout.size
|
|
||||||
import androidx.compose.foundation.lazy.grid.GridCells
|
|
||||||
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
|
|
||||||
import androidx.compose.foundation.lazy.grid.items
|
|
||||||
import androidx.compose.foundation.rememberScrollState
|
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
|
||||||
import androidx.compose.foundation.verticalScroll
|
|
||||||
import androidx.compose.material3.Button
|
|
||||||
import androidx.compose.material3.Card
|
|
||||||
import androidx.compose.material3.Scaffold
|
|
||||||
import androidx.compose.material3.Text
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
|
||||||
import androidx.compose.runtime.getValue
|
|
||||||
import androidx.compose.runtime.mutableStateOf
|
|
||||||
import androidx.compose.runtime.remember
|
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
|
||||||
import androidx.compose.runtime.setValue
|
|
||||||
import androidx.compose.ui.Alignment
|
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import androidx.compose.ui.platform.LocalContext
|
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
|
||||||
import androidx.compose.ui.unit.dp
|
|
||||||
import androidx.compose.ui.window.Dialog
|
|
||||||
import androidx.compose.ui.window.DialogProperties
|
|
||||||
import androidx.navigation.NavController
|
|
||||||
import com.github.nacabaro.vbhelper.components.CharacterEntry
|
|
||||||
import com.github.nacabaro.vbhelper.components.TopBanner
|
|
||||||
import com.github.nacabaro.vbhelper.di.VBHelper
|
|
||||||
import com.github.nacabaro.vbhelper.domain.device_data.UserCharacter
|
|
||||||
import com.github.nacabaro.vbhelper.dtos.CharacterDtos
|
|
||||||
import com.github.nacabaro.vbhelper.navigation.NavigationItems
|
|
||||||
import com.github.nacabaro.vbhelper.source.StorageRepository
|
|
||||||
import com.github.nacabaro.vbhelper.utils.BitmapData
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun StorageScreen(
|
|
||||||
navController: NavController
|
|
||||||
) {
|
|
||||||
val coroutineScope = rememberCoroutineScope()
|
|
||||||
val application = LocalContext.current.applicationContext as VBHelper
|
|
||||||
val storageRepository = StorageRepository(application.container.db)
|
|
||||||
val monList = remember { mutableStateOf<List<CharacterDtos.CharacterWithSprites>>(emptyList()) }
|
|
||||||
|
|
||||||
var selectedCharacter by remember { mutableStateOf<Long?>(null) }
|
|
||||||
|
|
||||||
LaunchedEffect(storageRepository) {
|
|
||||||
coroutineScope.launch {
|
|
||||||
val characterList = storageRepository.getAllCharacters()
|
|
||||||
monList.value = characterList
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Scaffold (
|
|
||||||
topBar = { TopBanner(text = "My Digimon") }
|
|
||||||
) { contentPadding ->
|
|
||||||
if (monList.value.isEmpty()) {
|
|
||||||
Column (
|
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
|
||||||
verticalArrangement = Arrangement.Center,
|
|
||||||
modifier = Modifier
|
|
||||||
.padding(top = contentPadding.calculateTopPadding())
|
|
||||||
.fillMaxSize()
|
|
||||||
) {
|
|
||||||
Text(
|
|
||||||
text = "Nothing to see here",
|
|
||||||
textAlign = TextAlign.Center,
|
|
||||||
modifier = Modifier
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LazyVerticalGrid(
|
|
||||||
columns = GridCells.Fixed(3),
|
|
||||||
modifier = Modifier
|
|
||||||
.scrollable(state = rememberScrollState(), orientation = Orientation.Vertical)
|
|
||||||
.padding(top = contentPadding.calculateTopPadding())
|
|
||||||
) {
|
|
||||||
items(monList.value) { index ->
|
|
||||||
CharacterEntry(
|
|
||||||
icon = BitmapData(
|
|
||||||
bitmap = index.spriteIdle,
|
|
||||||
width = index.spriteWidth,
|
|
||||||
height = index.spriteHeight
|
|
||||||
),
|
|
||||||
onClick = {
|
|
||||||
selectedCharacter = index.id
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
if (selectedCharacter != null) {
|
|
||||||
StorageDialog(
|
|
||||||
characterId = selectedCharacter!!,
|
|
||||||
onDismissRequest = { selectedCharacter = null },
|
|
||||||
onClickSetActive = {
|
|
||||||
coroutineScope.launch {
|
|
||||||
withContext(Dispatchers.IO) {
|
|
||||||
storageRepository.setActiveCharacter(selectedCharacter!!)
|
|
||||||
selectedCharacter = null
|
|
||||||
}
|
|
||||||
navController.navigate(NavigationItems.Home.route)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onSendToBracelet = {
|
|
||||||
navController.navigate(
|
|
||||||
NavigationItems.Scan.route.replace(
|
|
||||||
"{characterId}",
|
|
||||||
selectedCharacter.toString()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun StorageDialog(
|
|
||||||
characterId: Long,
|
|
||||||
onDismissRequest: () -> Unit,
|
|
||||||
onSendToBracelet: () -> Unit,
|
|
||||||
onClickSetActive: () -> Unit
|
|
||||||
) {
|
|
||||||
val coroutineScope = rememberCoroutineScope()
|
|
||||||
val application = LocalContext.current.applicationContext as VBHelper
|
|
||||||
val storageRepository = StorageRepository(application.container.db)
|
|
||||||
val character = remember { mutableStateOf<UserCharacter?>(null) }
|
|
||||||
|
|
||||||
LaunchedEffect(storageRepository) {
|
|
||||||
coroutineScope.launch {
|
|
||||||
character.value = storageRepository.getSingleCharacter(characterId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Dialog(
|
|
||||||
onDismissRequest = onDismissRequest,
|
|
||||||
properties = DialogProperties(
|
|
||||||
dismissOnBackPress = true,
|
|
||||||
dismissOnClickOutside = true
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
Card(
|
|
||||||
shape = RoundedCornerShape(16.dp)
|
|
||||||
) {
|
|
||||||
Column (
|
|
||||||
modifier = Modifier
|
|
||||||
.padding(16.dp)
|
|
||||||
) {
|
|
||||||
if (character.value != null) {
|
|
||||||
Text(
|
|
||||||
text = character.value?.toString() ?: "Loading...",
|
|
||||||
textAlign = TextAlign.Center,
|
|
||||||
modifier = Modifier
|
|
||||||
.padding(8.dp)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Row (
|
|
||||||
modifier = Modifier
|
|
||||||
.verticalScroll(rememberScrollState())
|
|
||||||
) {
|
|
||||||
Button(
|
|
||||||
onClick = onSendToBracelet
|
|
||||||
) {
|
|
||||||
Text(text = "Send to bracelet")
|
|
||||||
}
|
|
||||||
Button(
|
|
||||||
onClick = onClickSetActive
|
|
||||||
) {
|
|
||||||
Text(text = "Set active")
|
|
||||||
}
|
|
||||||
Button(
|
|
||||||
onClick = onDismissRequest
|
|
||||||
) {
|
|
||||||
Text(text = "Close")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+68
@@ -0,0 +1,68 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.screens.adventureScreen
|
||||||
|
|
||||||
|
import androidx.compose.foundation.Image
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.Card
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.FilterQuality
|
||||||
|
import androidx.compose.ui.graphics.asImageBitmap
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import com.github.nacabaro.vbhelper.utils.BitmapData
|
||||||
|
import com.github.nacabaro.vbhelper.utils.getBitmap
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun AdventureEntry(
|
||||||
|
icon: BitmapData,
|
||||||
|
timeLeft: Long,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
onClick: () -> Unit
|
||||||
|
) {
|
||||||
|
val bitmap = remember (icon.bitmap) { icon.getBitmap() }
|
||||||
|
val imageBitmap = remember(bitmap) { bitmap.asImageBitmap() }
|
||||||
|
val density: Float = LocalContext.current.resources.displayMetrics.density
|
||||||
|
val dpSize = (icon.width * 4 / density).dp
|
||||||
|
|
||||||
|
Card(
|
||||||
|
onClick = onClick,
|
||||||
|
modifier = modifier
|
||||||
|
.padding(8.dp)
|
||||||
|
.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(8.dp)
|
||||||
|
.height(96.dp)
|
||||||
|
) {
|
||||||
|
Image(
|
||||||
|
bitmap = imageBitmap,
|
||||||
|
contentDescription = null,
|
||||||
|
filterQuality = FilterQuality.None,
|
||||||
|
modifier = Modifier
|
||||||
|
.size(dpSize)
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = when {
|
||||||
|
timeLeft < 0 -> "Adventure finished"
|
||||||
|
else -> "Time left: ${formatSeconds(timeLeft)}"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun formatSeconds(totalSeconds: Long): String {
|
||||||
|
val hours = totalSeconds / 3600
|
||||||
|
val minutes = (totalSeconds % 3600) / 60
|
||||||
|
val seconds = totalSeconds % 60
|
||||||
|
|
||||||
|
return String.format(Locale.getDefault(), "%02d:%02d:%02d", hours, minutes, seconds)
|
||||||
|
}
|
||||||
+145
@@ -0,0 +1,145 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.screens.adventureScreen
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.items
|
||||||
|
import androidx.compose.material3.Scaffold
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.produceState
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.navigation.NavController
|
||||||
|
import com.github.nacabaro.vbhelper.screens.itemsScreen.ObtainedItemDialog
|
||||||
|
import com.github.nacabaro.vbhelper.components.TopBanner
|
||||||
|
import com.github.nacabaro.vbhelper.di.VBHelper
|
||||||
|
import com.github.nacabaro.vbhelper.dtos.CharacterDtos
|
||||||
|
import com.github.nacabaro.vbhelper.dtos.ItemDtos
|
||||||
|
import com.github.nacabaro.vbhelper.navigation.NavigationItems
|
||||||
|
import com.github.nacabaro.vbhelper.source.StorageRepository
|
||||||
|
import com.github.nacabaro.vbhelper.utils.BitmapData
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import java.time.Instant
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun AdventureScreen(
|
||||||
|
navController: NavController,
|
||||||
|
storageScreenController: AdventureScreenControllerImpl
|
||||||
|
) {
|
||||||
|
val coroutineScope = rememberCoroutineScope()
|
||||||
|
val application = LocalContext.current.applicationContext as VBHelper
|
||||||
|
val database = application.container.db
|
||||||
|
val storageRepository = StorageRepository(database)
|
||||||
|
val characterList = remember {
|
||||||
|
mutableStateOf<List<CharacterDtos.AdventureCharacterWithSprites>>(emptyList())
|
||||||
|
}
|
||||||
|
var obtainedItem by remember {
|
||||||
|
mutableStateOf<ItemDtos.PurchasedItem?>(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
val currentTime by produceState(initialValue = Instant.now().epochSecond) {
|
||||||
|
while (true) {
|
||||||
|
value = Instant.now().epochSecond
|
||||||
|
delay(1000)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var cancelAdventureDialog by remember {
|
||||||
|
mutableStateOf<CharacterDtos.AdventureCharacterWithSprites?>(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
LaunchedEffect(storageRepository) {
|
||||||
|
coroutineScope.launch {
|
||||||
|
characterList.value = storageRepository
|
||||||
|
.getAdventureCharacters()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Scaffold(
|
||||||
|
topBar = {
|
||||||
|
TopBanner(
|
||||||
|
text = "Adventure",
|
||||||
|
onBackClick = {
|
||||||
|
navController.popBackStack()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
) { contentPadding ->
|
||||||
|
if (characterList.value.isEmpty()) {
|
||||||
|
Column(
|
||||||
|
verticalArrangement = Arrangement.Center,
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(top = contentPadding.calculateTopPadding())
|
||||||
|
.fillMaxSize()
|
||||||
|
) {
|
||||||
|
Text(text = "Nothing to see here")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LazyColumn(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(top = contentPadding.calculateTopPadding())
|
||||||
|
) {
|
||||||
|
items(characterList.value) {
|
||||||
|
AdventureEntry(
|
||||||
|
icon = BitmapData(
|
||||||
|
bitmap = it.spriteIdle,
|
||||||
|
width = it.spriteWidth,
|
||||||
|
height = it.spriteHeight
|
||||||
|
),
|
||||||
|
timeLeft = it.finishesAdventure - currentTime,
|
||||||
|
onClick = {
|
||||||
|
if (it.finishesAdventure < currentTime) {
|
||||||
|
storageScreenController
|
||||||
|
.getItemFromAdventure(it.id) { adventureResult ->
|
||||||
|
obtainedItem = adventureResult
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cancelAdventureDialog = it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (obtainedItem != null) {
|
||||||
|
ObtainedItemDialog(
|
||||||
|
obtainedItem = obtainedItem!!,
|
||||||
|
onClickDismiss = {
|
||||||
|
obtainedItem = null
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cancelAdventureDialog != null) {
|
||||||
|
CancelAdventureDialog(
|
||||||
|
characterSprite = BitmapData(
|
||||||
|
bitmap = cancelAdventureDialog!!.spriteIdle,
|
||||||
|
width = cancelAdventureDialog!!.spriteWidth,
|
||||||
|
height = cancelAdventureDialog!!.spriteHeight
|
||||||
|
),
|
||||||
|
onDismissRequest = {
|
||||||
|
cancelAdventureDialog = null
|
||||||
|
},
|
||||||
|
onClickConfirm = {
|
||||||
|
storageScreenController.cancelAdventure(cancelAdventureDialog!!.id) {
|
||||||
|
navController.navigate(NavigationItems.Storage.route)
|
||||||
|
}
|
||||||
|
cancelAdventureDialog = null
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
+9
@@ -0,0 +1,9 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.screens.adventureScreen
|
||||||
|
|
||||||
|
import com.github.nacabaro.vbhelper.dtos.ItemDtos
|
||||||
|
|
||||||
|
interface AdventureScreenController {
|
||||||
|
fun sendCharacterToAdventure(characterId: Long, timeInMinutes: Long)
|
||||||
|
fun getItemFromAdventure(characterId: Long, onResult: (ItemDtos.PurchasedItem) -> Unit)
|
||||||
|
fun cancelAdventure(characterId: Long, onResult: () -> Unit)
|
||||||
|
}
|
||||||
+100
@@ -0,0 +1,100 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.screens.adventureScreen
|
||||||
|
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.activity.ComponentActivity
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import com.github.nacabaro.vbhelper.di.VBHelper
|
||||||
|
import com.github.nacabaro.vbhelper.dtos.ItemDtos
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlin.math.roundToInt
|
||||||
|
import kotlin.random.Random
|
||||||
|
|
||||||
|
class AdventureScreenControllerImpl(
|
||||||
|
private val componentActivity: ComponentActivity,
|
||||||
|
) : AdventureScreenController {
|
||||||
|
private val application = componentActivity.applicationContext as VBHelper
|
||||||
|
private val database = application.container.db
|
||||||
|
|
||||||
|
override fun sendCharacterToAdventure(characterId: Long, timeInMinutes: Long) {
|
||||||
|
val finishesAdventureAt = timeInMinutes * 60
|
||||||
|
componentActivity.lifecycleScope.launch(Dispatchers.IO) {
|
||||||
|
val characterData = database
|
||||||
|
.userCharacterDao()
|
||||||
|
.getCharacter(characterId)
|
||||||
|
|
||||||
|
if (characterData.isActive) {
|
||||||
|
database
|
||||||
|
.userCharacterDao()
|
||||||
|
.clearActiveCharacter()
|
||||||
|
}
|
||||||
|
|
||||||
|
database
|
||||||
|
.adventureDao()
|
||||||
|
.insertNewAdventure(characterId, timeInMinutes, finishesAdventureAt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemFromAdventure(
|
||||||
|
characterId: Long,
|
||||||
|
onResult: (ItemDtos.PurchasedItem) -> Unit
|
||||||
|
) {
|
||||||
|
componentActivity.lifecycleScope.launch(Dispatchers.IO) {
|
||||||
|
database
|
||||||
|
.adventureDao()
|
||||||
|
.deleteAdventure(characterId)
|
||||||
|
|
||||||
|
val generatedItem = generateItem(characterId)
|
||||||
|
|
||||||
|
onResult(generatedItem)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun cancelAdventure(characterId: Long, onResult: () -> Unit) {
|
||||||
|
componentActivity.lifecycleScope.launch(Dispatchers.IO) {
|
||||||
|
database
|
||||||
|
.adventureDao()
|
||||||
|
.deleteAdventure(characterId)
|
||||||
|
|
||||||
|
componentActivity
|
||||||
|
.runOnUiThread {
|
||||||
|
Toast.makeText(
|
||||||
|
componentActivity,
|
||||||
|
"Adventure canceled",
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
).show()
|
||||||
|
onResult()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun generateItem(characterId: Long): ItemDtos.PurchasedItem {
|
||||||
|
val character = database
|
||||||
|
.userCharacterDao()
|
||||||
|
.getCharacterInfo(characterId)
|
||||||
|
|
||||||
|
val randomItem = database
|
||||||
|
.itemDao()
|
||||||
|
.getAllItems()
|
||||||
|
.random()
|
||||||
|
|
||||||
|
val random = ((Random.nextFloat() * character.stage) + 3).roundToInt()
|
||||||
|
|
||||||
|
database
|
||||||
|
.itemDao()
|
||||||
|
.purchaseItem(
|
||||||
|
itemId = randomItem.id,
|
||||||
|
itemAmount = random
|
||||||
|
)
|
||||||
|
|
||||||
|
return ItemDtos.PurchasedItem(
|
||||||
|
itemId = randomItem.id,
|
||||||
|
itemAmount = random,
|
||||||
|
itemName = randomItem.name,
|
||||||
|
itemIcon = randomItem.itemIcon,
|
||||||
|
itemLength = randomItem.itemLength,
|
||||||
|
itemDescription = randomItem.description
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
+73
@@ -0,0 +1,73 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.screens.adventureScreen
|
||||||
|
|
||||||
|
import androidx.compose.foundation.Image
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.material3.Button
|
||||||
|
import androidx.compose.material3.Card
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.FilterQuality
|
||||||
|
import androidx.compose.ui.graphics.asImageBitmap
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.window.Dialog
|
||||||
|
import com.github.nacabaro.vbhelper.utils.BitmapData
|
||||||
|
import com.github.nacabaro.vbhelper.utils.getBitmap
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun CancelAdventureDialog(
|
||||||
|
characterSprite: BitmapData,
|
||||||
|
onDismissRequest: () -> Unit,
|
||||||
|
onClickConfirm: () -> Unit
|
||||||
|
) {
|
||||||
|
val bitmap = remember (characterSprite) { characterSprite.getBitmap() }
|
||||||
|
val imageBitmap = remember(bitmap) { bitmap.asImageBitmap() }
|
||||||
|
val density: Float = LocalContext.current.resources.displayMetrics.density
|
||||||
|
val dpSize = (characterSprite.width * 4 / density).dp
|
||||||
|
|
||||||
|
Dialog(
|
||||||
|
onDismissRequest = onDismissRequest
|
||||||
|
) {
|
||||||
|
Card {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(16.dp)
|
||||||
|
) {
|
||||||
|
Row {
|
||||||
|
Image(
|
||||||
|
bitmap = imageBitmap,
|
||||||
|
contentDescription = null,
|
||||||
|
filterQuality = FilterQuality.None,
|
||||||
|
modifier = Modifier
|
||||||
|
.size(dpSize)
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = "Are you sure you want to cancel this character's adventure?"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(8.dp)
|
||||||
|
) {
|
||||||
|
Button(
|
||||||
|
onClick = onClickConfirm
|
||||||
|
) {
|
||||||
|
Text(text = "Confirm")
|
||||||
|
}
|
||||||
|
Spacer(modifier = Modifier.padding(4.dp))
|
||||||
|
Button(
|
||||||
|
onClick = onDismissRequest
|
||||||
|
) {
|
||||||
|
Text(text = "Dismiss")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,8 +15,10 @@ import com.github.nacabaro.vbhelper.R
|
|||||||
import com.github.nacabaro.vbhelper.components.CharacterEntry
|
import com.github.nacabaro.vbhelper.components.CharacterEntry
|
||||||
import com.github.nacabaro.vbhelper.components.ItemDisplay
|
import com.github.nacabaro.vbhelper.components.ItemDisplay
|
||||||
import com.github.nacabaro.vbhelper.components.TransformationHistoryCard
|
import com.github.nacabaro.vbhelper.components.TransformationHistoryCard
|
||||||
|
import com.github.nacabaro.vbhelper.screens.itemsScreen.getIconResource
|
||||||
import com.github.nacabaro.vbhelper.domain.device_data.BECharacterData
|
import com.github.nacabaro.vbhelper.domain.device_data.BECharacterData
|
||||||
import com.github.nacabaro.vbhelper.dtos.CharacterDtos
|
import com.github.nacabaro.vbhelper.dtos.CharacterDtos
|
||||||
|
import com.github.nacabaro.vbhelper.screens.itemsScreen.ItemsScreenControllerImpl
|
||||||
import com.github.nacabaro.vbhelper.utils.BitmapData
|
import com.github.nacabaro.vbhelper.utils.BitmapData
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
|
||||||
@@ -153,6 +155,24 @@ fun BEBEmHomeScreen(
|
|||||||
.aspectRatio(1f)
|
.aspectRatio(1f)
|
||||||
.padding(8.dp)
|
.padding(8.dp)
|
||||||
)
|
)
|
||||||
|
if (beData.itemRemainingTime != 0) {
|
||||||
|
ItemDisplay(
|
||||||
|
icon = getIconResource(beData.itemType),
|
||||||
|
textValue = "${beData.itemRemainingTime} m",
|
||||||
|
definition = when (beData.itemType) {
|
||||||
|
ItemsScreenControllerImpl.ItemTypes.PPTraining.id -> "PP Training"
|
||||||
|
ItemsScreenControllerImpl.ItemTypes.HPTraining.id -> "HP Training"
|
||||||
|
ItemsScreenControllerImpl.ItemTypes.APTraining.id -> "AP Training"
|
||||||
|
ItemsScreenControllerImpl.ItemTypes.BPTraining.id -> "BP Training"
|
||||||
|
ItemsScreenControllerImpl.ItemTypes.AllTraining.id -> "All Training"
|
||||||
|
else -> ""
|
||||||
|
},
|
||||||
|
modifier = Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.aspectRatio(1f)
|
||||||
|
.padding(8.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Row (
|
Row (
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
|||||||
+39
-9
@@ -16,8 +16,10 @@ import com.github.nacabaro.vbhelper.R
|
|||||||
import com.github.nacabaro.vbhelper.components.CharacterEntry
|
import com.github.nacabaro.vbhelper.components.CharacterEntry
|
||||||
import com.github.nacabaro.vbhelper.components.ItemDisplay
|
import com.github.nacabaro.vbhelper.components.ItemDisplay
|
||||||
import com.github.nacabaro.vbhelper.components.TransformationHistoryCard
|
import com.github.nacabaro.vbhelper.components.TransformationHistoryCard
|
||||||
|
import com.github.nacabaro.vbhelper.screens.itemsScreen.getIconResource
|
||||||
import com.github.nacabaro.vbhelper.domain.device_data.BECharacterData
|
import com.github.nacabaro.vbhelper.domain.device_data.BECharacterData
|
||||||
import com.github.nacabaro.vbhelper.dtos.CharacterDtos
|
import com.github.nacabaro.vbhelper.dtos.CharacterDtos
|
||||||
|
import com.github.nacabaro.vbhelper.screens.itemsScreen.ItemsScreenControllerImpl
|
||||||
import com.github.nacabaro.vbhelper.utils.BitmapData
|
import com.github.nacabaro.vbhelper.utils.BitmapData
|
||||||
import kotlin.text.format
|
import kotlin.text.format
|
||||||
|
|
||||||
@@ -33,7 +35,7 @@ fun BEDiMHomeScreen(
|
|||||||
.padding(top = contentPadding.calculateTopPadding())
|
.padding(top = contentPadding.calculateTopPadding())
|
||||||
.verticalScroll(state = rememberScrollState())
|
.verticalScroll(state = rememberScrollState())
|
||||||
) {
|
) {
|
||||||
Row (
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
) {
|
) {
|
||||||
@@ -49,7 +51,7 @@ fun BEDiMHomeScreen(
|
|||||||
.weight(1f)
|
.weight(1f)
|
||||||
.aspectRatio(1f)
|
.aspectRatio(1f)
|
||||||
)
|
)
|
||||||
Column (
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.weight(0.5f)
|
.weight(0.5f)
|
||||||
.aspectRatio(0.5f)
|
.aspectRatio(0.5f)
|
||||||
@@ -74,7 +76,7 @@ fun BEDiMHomeScreen(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Row (
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
) {
|
) {
|
||||||
@@ -108,7 +110,7 @@ fun BEDiMHomeScreen(
|
|||||||
.padding(8.dp)
|
.padding(8.dp)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Row (
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
) {
|
) {
|
||||||
@@ -130,8 +132,13 @@ fun BEDiMHomeScreen(
|
|||||||
textValue = when {
|
textValue = when {
|
||||||
activeMon.totalBattlesLost == 0 -> "0.00 %"
|
activeMon.totalBattlesLost == 0 -> "0.00 %"
|
||||||
else -> {
|
else -> {
|
||||||
val battleWinPercentage = activeMon.totalBattlesWon.toFloat() / (activeMon.totalBattlesWon + activeMon.totalBattlesLost).toFloat()
|
val battleWinPercentage =
|
||||||
String.format(Locale.getDefault(), "%.2f", battleWinPercentage * 100) + " %" // Specify locale
|
activeMon.totalBattlesWon.toFloat() / (activeMon.totalBattlesWon + activeMon.totalBattlesLost).toFloat()
|
||||||
|
String.format(
|
||||||
|
Locale.getDefault(),
|
||||||
|
"%.2f",
|
||||||
|
battleWinPercentage * 100
|
||||||
|
) + " %" // Specify locale
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
definition = "Total battle win %",
|
definition = "Total battle win %",
|
||||||
@@ -145,8 +152,13 @@ fun BEDiMHomeScreen(
|
|||||||
textValue = when {
|
textValue = when {
|
||||||
activeMon.totalBattlesLost == 0 -> "0.00 %"
|
activeMon.totalBattlesLost == 0 -> "0.00 %"
|
||||||
else -> {
|
else -> {
|
||||||
val battleWinPercentage = activeMon.currentPhaseBattlesWon.toFloat() / (activeMon.currentPhaseBattlesWon + activeMon.currentPhaseBattlesLost).toFloat()
|
val battleWinPercentage =
|
||||||
String.format(Locale.getDefault(), "%.2f", battleWinPercentage * 100) + " %" // Specify locale
|
activeMon.currentPhaseBattlesWon.toFloat() / (activeMon.currentPhaseBattlesWon + activeMon.currentPhaseBattlesLost).toFloat()
|
||||||
|
String.format(
|
||||||
|
Locale.getDefault(),
|
||||||
|
"%.2f",
|
||||||
|
battleWinPercentage * 100
|
||||||
|
) + " %" // Specify locale
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
definition = "Current phase win %",
|
definition = "Current phase win %",
|
||||||
@@ -155,8 +167,26 @@ fun BEDiMHomeScreen(
|
|||||||
.aspectRatio(1f)
|
.aspectRatio(1f)
|
||||||
.padding(8.dp)
|
.padding(8.dp)
|
||||||
)
|
)
|
||||||
|
if (beData.itemRemainingTime != 0) {
|
||||||
|
ItemDisplay(
|
||||||
|
icon = getIconResource(beData.itemType),
|
||||||
|
textValue = "${beData.itemRemainingTime} m",
|
||||||
|
definition = when (beData.itemType) {
|
||||||
|
ItemsScreenControllerImpl.ItemTypes.PPTraining.id -> "PP Training"
|
||||||
|
ItemsScreenControllerImpl.ItemTypes.HPTraining.id -> "HP Training"
|
||||||
|
ItemsScreenControllerImpl.ItemTypes.APTraining.id -> "AP Training"
|
||||||
|
ItemsScreenControllerImpl.ItemTypes.BPTraining.id -> "BP Training"
|
||||||
|
ItemsScreenControllerImpl.ItemTypes.AllTraining.id -> "All Training"
|
||||||
|
else -> ""
|
||||||
|
},
|
||||||
|
modifier = Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.aspectRatio(1f)
|
||||||
|
.padding(8.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Row (
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.screens.homeScreens
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.material3.Button
|
||||||
|
import androidx.compose.material3.Card
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.window.Dialog
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun BetaWarning(
|
||||||
|
onDismissRequest: () -> Unit
|
||||||
|
) {
|
||||||
|
Dialog(
|
||||||
|
onDismissRequest = onDismissRequest
|
||||||
|
) {
|
||||||
|
Card {
|
||||||
|
Column (
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(16.dp)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = "This application is currently in alpha and it is not complete. Do not use to store important characters for you, as any future updates might delete all your characters. Sorry for the inconvenience!"
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.padding(8.dp))
|
||||||
|
Text(
|
||||||
|
text = "Also, this application does not work yet with the original VB."
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.padding(8.dp))
|
||||||
|
Text(
|
||||||
|
text = "Thank you for your understanding and patience. Sincerely, the dev team."
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.padding(8.dp))
|
||||||
|
Button(
|
||||||
|
onClick = onDismissRequest
|
||||||
|
) {
|
||||||
|
Text(text = "Dismiss")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,19 +1,27 @@
|
|||||||
package com.github.nacabaro.vbhelper.screens.homeScreens
|
package com.github.nacabaro.vbhelper.screens.homeScreens
|
||||||
|
|
||||||
import android.util.Log
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.material3.Button
|
||||||
|
import androidx.compose.material3.Card
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.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.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.window.Dialog
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
import com.github.nacabaro.vbhelper.components.TopBanner
|
import com.github.nacabaro.vbhelper.components.TopBanner
|
||||||
import com.github.nacabaro.vbhelper.di.VBHelper
|
import com.github.nacabaro.vbhelper.di.VBHelper
|
||||||
@@ -28,7 +36,8 @@ import kotlinx.coroutines.withContext
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun HomeScreen(
|
fun HomeScreen(
|
||||||
navController: NavController
|
navController: NavController,
|
||||||
|
homeScreenController: HomeScreenControllerImpl
|
||||||
) {
|
) {
|
||||||
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)
|
||||||
@@ -36,6 +45,8 @@ fun HomeScreen(
|
|||||||
val transformationHistory = remember { mutableStateOf<List<CharacterDtos.TransformationHistory>?>(null) }
|
val transformationHistory = remember { mutableStateOf<List<CharacterDtos.TransformationHistory>?>(null) }
|
||||||
val beData = remember { mutableStateOf<BECharacterData?>(null) }
|
val beData = remember { mutableStateOf<BECharacterData?>(null) }
|
||||||
val vbData = remember { mutableStateOf<VBCharacterData?>(null) }
|
val vbData = remember { mutableStateOf<VBCharacterData?>(null) }
|
||||||
|
var adventureMissionsFinished by rememberSaveable { mutableStateOf(false) }
|
||||||
|
var betaWarning by rememberSaveable { mutableStateOf(true) }
|
||||||
|
|
||||||
LaunchedEffect(storageRepository, activeMon) {
|
LaunchedEffect(storageRepository, activeMon) {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
@@ -47,6 +58,13 @@ fun HomeScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LaunchedEffect(true) {
|
||||||
|
homeScreenController
|
||||||
|
.didAdventureMissionsFinish {
|
||||||
|
adventureMissionsFinished = it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Scaffold (
|
Scaffold (
|
||||||
topBar = {
|
topBar = {
|
||||||
TopBanner(
|
TopBanner(
|
||||||
@@ -72,7 +90,6 @@ fun HomeScreen(
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (activeMon.value!!.isBemCard) {
|
if (activeMon.value!!.isBemCard) {
|
||||||
Log.d("HomeScreen", "BEDeviceBEm")
|
|
||||||
BEBEmHomeScreen(
|
BEBEmHomeScreen(
|
||||||
activeMon = activeMon.value!!,
|
activeMon = activeMon.value!!,
|
||||||
beData = beData.value!!,
|
beData = beData.value!!,
|
||||||
@@ -80,7 +97,6 @@ fun HomeScreen(
|
|||||||
contentPadding = contentPadding
|
contentPadding = contentPadding
|
||||||
)
|
)
|
||||||
} else if (!activeMon.value!!.isBemCard && activeMon.value!!.characterType == DeviceType.BEDevice) {
|
} else if (!activeMon.value!!.isBemCard && activeMon.value!!.characterType == DeviceType.BEDevice) {
|
||||||
Log.d("HomeScreen", "BEDevice")
|
|
||||||
BEDiMHomeScreen(
|
BEDiMHomeScreen(
|
||||||
activeMon = activeMon.value!!,
|
activeMon = activeMon.value!!,
|
||||||
beData = beData.value!!,
|
beData = beData.value!!,
|
||||||
@@ -97,6 +113,40 @@ fun HomeScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (adventureMissionsFinished) {
|
||||||
|
Dialog(
|
||||||
|
onDismissRequest = { adventureMissionsFinished = false },
|
||||||
|
) {
|
||||||
|
Card {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(16.dp)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = "One of your characters has finished their adventure mission!",
|
||||||
|
textAlign = TextAlign.Center
|
||||||
|
)
|
||||||
|
Button(
|
||||||
|
onClick = {
|
||||||
|
adventureMissionsFinished = false
|
||||||
|
},
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(8.dp)
|
||||||
|
.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
Text(text = "Dismiss")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (betaWarning) {
|
||||||
|
BetaWarning {
|
||||||
|
betaWarning = false
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.screens.homeScreens
|
||||||
|
|
||||||
|
interface HomeScreenController {
|
||||||
|
fun didAdventureMissionsFinish(onCompletion: (Boolean) -> Unit)
|
||||||
|
}
|
||||||
+29
@@ -0,0 +1,29 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.screens.homeScreens
|
||||||
|
|
||||||
|
import androidx.activity.ComponentActivity
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import com.github.nacabaro.vbhelper.di.VBHelper
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import java.time.Instant
|
||||||
|
|
||||||
|
class HomeScreenControllerImpl(
|
||||||
|
private val componentActivity: ComponentActivity,
|
||||||
|
): HomeScreenController {
|
||||||
|
private val application = componentActivity.applicationContext as VBHelper
|
||||||
|
private val database = application.container.db
|
||||||
|
|
||||||
|
override fun didAdventureMissionsFinish(onCompletion: (Boolean) -> Unit) {
|
||||||
|
componentActivity.lifecycleScope.launch {
|
||||||
|
val currentTime = Instant.now().epochSecond
|
||||||
|
val adventureCharacters = database
|
||||||
|
.adventureDao()
|
||||||
|
.getAdventureCharacters()
|
||||||
|
|
||||||
|
val finishedAdventureCharacters = adventureCharacters.filter { character ->
|
||||||
|
character.finishesAdventure <= currentTime
|
||||||
|
}
|
||||||
|
|
||||||
|
onCompletion(finishedAdventureCharacters.isNotEmpty())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+90
@@ -0,0 +1,90 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.screens.itemsScreen
|
||||||
|
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.lazy.grid.GridCells
|
||||||
|
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
|
||||||
|
import androidx.compose.foundation.lazy.grid.items
|
||||||
|
import androidx.compose.material3.Scaffold
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.navigation.NavController
|
||||||
|
import com.github.nacabaro.vbhelper.components.CharacterEntry
|
||||||
|
import com.github.nacabaro.vbhelper.components.TopBanner
|
||||||
|
import com.github.nacabaro.vbhelper.di.VBHelper
|
||||||
|
import com.github.nacabaro.vbhelper.dtos.CharacterDtos
|
||||||
|
import com.github.nacabaro.vbhelper.source.StorageRepository
|
||||||
|
import com.github.nacabaro.vbhelper.utils.BitmapData
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ChooseCharacterScreen(
|
||||||
|
navController: NavController,
|
||||||
|
itemsScreenController: ItemsScreenControllerImpl,
|
||||||
|
itemId: Long
|
||||||
|
) {
|
||||||
|
val coroutineScope = rememberCoroutineScope()
|
||||||
|
val application = LocalContext.current.applicationContext as VBHelper
|
||||||
|
val storageRepository = StorageRepository(application.container.db)
|
||||||
|
val characterList = remember {
|
||||||
|
mutableStateOf<List<CharacterDtos.CharacterWithSprites>>(emptyList())
|
||||||
|
}
|
||||||
|
|
||||||
|
var selectedCharacter by remember { mutableStateOf<Long?>(null) }
|
||||||
|
|
||||||
|
LaunchedEffect(storageRepository) {
|
||||||
|
coroutineScope.launch {
|
||||||
|
characterList.value = storageRepository.getAllCharacters()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LaunchedEffect (selectedCharacter) {
|
||||||
|
if (selectedCharacter != null) {
|
||||||
|
itemsScreenController.applyItem(itemId, selectedCharacter!!) {
|
||||||
|
Toast.makeText(
|
||||||
|
application.applicationContext,
|
||||||
|
"Item applied!",
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
).show()
|
||||||
|
navController.popBackStack()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Scaffold(
|
||||||
|
topBar = {
|
||||||
|
TopBanner(
|
||||||
|
text = "Choose character",
|
||||||
|
onBackClick = {
|
||||||
|
navController.popBackStack()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
) { contentPadding ->
|
||||||
|
LazyVerticalGrid(
|
||||||
|
columns = GridCells.Fixed(3),
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(top = contentPadding.calculateTopPadding())
|
||||||
|
) {
|
||||||
|
items(characterList.value) {
|
||||||
|
CharacterEntry(
|
||||||
|
icon = BitmapData(
|
||||||
|
bitmap = it.spriteIdle,
|
||||||
|
width = it.spriteWidth,
|
||||||
|
height = it.spriteHeight
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
selectedCharacter = it.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,212 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.screens.itemsScreen
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.aspectRatio
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.material3.Button
|
||||||
|
import androidx.compose.material3.Card
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.res.painterResource
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.window.Dialog
|
||||||
|
import androidx.compose.ui.window.DialogProperties
|
||||||
|
import com.github.nacabaro.vbhelper.R
|
||||||
|
import com.github.nacabaro.vbhelper.ui.theme.VBHelperTheme
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ItemElement(
|
||||||
|
itemIcon: Int,
|
||||||
|
lengthIcon: Int,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
onClick: (() -> Unit) = { }
|
||||||
|
) {
|
||||||
|
Card (
|
||||||
|
onClick = onClick,
|
||||||
|
modifier = modifier
|
||||||
|
.aspectRatio(1f)
|
||||||
|
) {
|
||||||
|
Box(modifier = Modifier.fillMaxSize()) {
|
||||||
|
Icon(
|
||||||
|
painter = painterResource(id = itemIcon),
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier
|
||||||
|
.size(96.dp)
|
||||||
|
.align(Alignment.Center)
|
||||||
|
.padding(16.dp)
|
||||||
|
)
|
||||||
|
Icon(
|
||||||
|
painter = painterResource(id = lengthIcon),
|
||||||
|
contentDescription = null,
|
||||||
|
tint = MaterialTheme.colorScheme.surfaceTint,
|
||||||
|
modifier = Modifier
|
||||||
|
.size(48.dp) // Set the size of the overlay image
|
||||||
|
.align(Alignment.TopStart) // Align to the top end (top-right corner)
|
||||||
|
.padding(8.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ItemDialog(
|
||||||
|
name: String,
|
||||||
|
description: String,
|
||||||
|
itemIcon: Int,
|
||||||
|
lengthIcon: Int,
|
||||||
|
amount: Int,
|
||||||
|
onClickUse: () -> Unit,
|
||||||
|
onClickCancel: () -> Unit
|
||||||
|
) {
|
||||||
|
Dialog(
|
||||||
|
onDismissRequest = onClickCancel,
|
||||||
|
properties = DialogProperties(
|
||||||
|
dismissOnBackPress = true,
|
||||||
|
dismissOnClickOutside = true
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
Card (
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(16.dp)
|
||||||
|
) {
|
||||||
|
Column (
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(16.dp)
|
||||||
|
) {
|
||||||
|
Row {
|
||||||
|
Box(modifier = Modifier) {
|
||||||
|
// Background image (full size)
|
||||||
|
Icon(
|
||||||
|
painter = painterResource(id = itemIcon),
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier
|
||||||
|
.size(96.dp)
|
||||||
|
.align(Alignment.Center)
|
||||||
|
)
|
||||||
|
Icon(
|
||||||
|
painter = painterResource(id = lengthIcon),
|
||||||
|
contentDescription = null,
|
||||||
|
tint = MaterialTheme.colorScheme.outline,
|
||||||
|
modifier = Modifier
|
||||||
|
.size(64.dp) // Set the size of the overlay image
|
||||||
|
.align(Alignment.BottomEnd) // Align to the top end (top-right corner)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Column (
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(16.dp)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
fontSize = MaterialTheme.typography.titleLarge.fontSize,
|
||||||
|
text = name,
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Text(
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
fontSize = MaterialTheme.typography.bodyMedium.fontSize,
|
||||||
|
fontFamily = MaterialTheme.typography.bodyMedium.fontFamily,
|
||||||
|
text = description,
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(4.dp)
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
fontSize = MaterialTheme.typography.bodySmall.fontSize,
|
||||||
|
fontFamily = MaterialTheme.typography.bodySmall.fontFamily,
|
||||||
|
text = "You have $amount of this item",
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(8.dp)
|
||||||
|
)
|
||||||
|
Row (
|
||||||
|
horizontalArrangement = Arrangement.Center,
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
Button(
|
||||||
|
onClick = onClickUse
|
||||||
|
) {
|
||||||
|
Text("Use item")
|
||||||
|
}
|
||||||
|
Spacer(modifier = Modifier.size(8.dp))
|
||||||
|
Button(
|
||||||
|
onClick = onClickCancel
|
||||||
|
) {
|
||||||
|
Text("Cancel")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getIconResource(index: Int): Int {
|
||||||
|
return when (index) {
|
||||||
|
ItemsScreenControllerImpl.ItemTypes.PPTraining.id -> R.drawable.baseline_agility_24
|
||||||
|
ItemsScreenControllerImpl.ItemTypes.APTraining.id -> R.drawable.baseline_attack_24
|
||||||
|
ItemsScreenControllerImpl.ItemTypes.HPTraining.id -> R.drawable.baseline_shield_24
|
||||||
|
ItemsScreenControllerImpl.ItemTypes.BPTraining.id -> R.drawable.baseline_trophy_24
|
||||||
|
ItemsScreenControllerImpl.ItemTypes.AllTraining.id -> R.drawable.baseline_arrow_up_24
|
||||||
|
6 -> R.drawable.baseline_timer_24
|
||||||
|
7 -> R.drawable.baseline_rank_24
|
||||||
|
8 -> R.drawable.baseline_vitals_24
|
||||||
|
else -> R.drawable.baseline_question_mark_24
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getLengthResource(index: Int): Int {
|
||||||
|
return when (index) {
|
||||||
|
15 -> R.drawable.baseline_15_min_timer
|
||||||
|
30 -> R.drawable.baseline_30_min_timer
|
||||||
|
60 -> R.drawable.baseline_60_min_timer
|
||||||
|
-60 -> R.drawable.baseline_60_min_timer
|
||||||
|
300 -> R.drawable.baseline_5_hour_timer
|
||||||
|
600 -> R.drawable.baseline_10_hour_timer
|
||||||
|
-720 -> R.drawable.baseline_12_hour_timer
|
||||||
|
-1440 -> R.drawable.baseline_24_hour_timer
|
||||||
|
6000 -> R.drawable.baseline_reset_24
|
||||||
|
1000 -> R.drawable.baseline_single_arrow_up
|
||||||
|
2500 -> R.drawable.baseline_double_arrow_up
|
||||||
|
5000 -> R.drawable.baseline_triple_arrow_up
|
||||||
|
9999 -> R.drawable.baseline_health_24
|
||||||
|
-500 -> R.drawable.baseline_single_arrow_down
|
||||||
|
-1000 -> R.drawable.baseline_double_arrow_down
|
||||||
|
-2500 -> R.drawable.baseline_triple_arrow_down
|
||||||
|
-9999 -> R.drawable.baseline_reset_24
|
||||||
|
else -> R.drawable.baseline_question_mark_24
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
@Preview(showBackground = true)
|
||||||
|
fun PreviewItemDialog() {
|
||||||
|
VBHelperTheme {
|
||||||
|
ItemDialog(
|
||||||
|
name = "AP Training x3 (60 min)",
|
||||||
|
description = "Boosts AP during training (for 60 minutes)",
|
||||||
|
itemIcon = R.drawable.baseline_attack_24,
|
||||||
|
lengthIcon = R.drawable.baseline_60_min_timer,
|
||||||
|
onClickUse = { },
|
||||||
|
onClickCancel = { },
|
||||||
|
amount = 19
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.screens.itemsScreen
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.material3.Scaffold
|
||||||
|
import androidx.compose.material3.Tab
|
||||||
|
import androidx.compose.material3.TabRow
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.navigation.NavController
|
||||||
|
import com.github.nacabaro.vbhelper.components.TopBanner
|
||||||
|
import com.github.nacabaro.vbhelper.navigation.NavigationItems
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ItemsScreen(
|
||||||
|
navController: NavController,
|
||||||
|
) {
|
||||||
|
var selectedTabItem by remember { mutableStateOf(0) }
|
||||||
|
val items = listOf(
|
||||||
|
NavigationItems.MyItems,
|
||||||
|
NavigationItems.ItemsStore
|
||||||
|
)
|
||||||
|
Scaffold(
|
||||||
|
topBar = {
|
||||||
|
Column {
|
||||||
|
TopBanner("Items")
|
||||||
|
TabRow(
|
||||||
|
selectedTabIndex = selectedTabItem,
|
||||||
|
modifier = Modifier
|
||||||
|
) {
|
||||||
|
items.forEachIndexed { index, item ->
|
||||||
|
Tab(
|
||||||
|
text = { Text(item.label) },
|
||||||
|
selected = selectedTabItem == index,
|
||||||
|
onClick = { selectedTabItem = index }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) { contentPadding ->
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(top = contentPadding.calculateTopPadding())
|
||||||
|
) {
|
||||||
|
when (selectedTabItem) {
|
||||||
|
0 -> MyItems(navController)
|
||||||
|
1 -> ItemsStore(navController)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.screens.itemsScreen
|
||||||
|
|
||||||
|
|
||||||
|
interface ItemsScreenController {
|
||||||
|
fun applyItem(itemId: Long, characterId: Long, onCompletion: () -> Unit)
|
||||||
|
}
|
||||||
+118
@@ -0,0 +1,118 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.screens.itemsScreen
|
||||||
|
|
||||||
|
import androidx.activity.ComponentActivity
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import com.github.nacabaro.vbhelper.database.AppDatabase
|
||||||
|
import com.github.nacabaro.vbhelper.di.VBHelper
|
||||||
|
import com.github.nacabaro.vbhelper.domain.device_data.BECharacterData
|
||||||
|
import com.github.nacabaro.vbhelper.dtos.ItemDtos
|
||||||
|
import com.github.nacabaro.vbhelper.utils.DeviceType
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
|
class ItemsScreenControllerImpl (
|
||||||
|
private val context: ComponentActivity,
|
||||||
|
): ItemsScreenController {
|
||||||
|
private var database: AppDatabase
|
||||||
|
|
||||||
|
enum class ItemTypes(val id: Int) {
|
||||||
|
PPTraining(1),
|
||||||
|
HPTraining(2),
|
||||||
|
APTraining(3),
|
||||||
|
BPTraining(4),
|
||||||
|
AllTraining(5),
|
||||||
|
EvoTimer(6),
|
||||||
|
LimitTimer(7),
|
||||||
|
Vitals(8)
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
val application = context.applicationContext as VBHelper
|
||||||
|
database = application.container.db
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun applyItem(itemId: Long, characterId: Long, onCompletion: () -> Unit) {
|
||||||
|
context.lifecycleScope.launch {
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
val item = getItem(itemId)
|
||||||
|
val characterData = database.userCharacterDao().getCharacter(characterId)
|
||||||
|
val beCharacterData: BECharacterData
|
||||||
|
//var vbCharacterData: VBCharacterData
|
||||||
|
|
||||||
|
if (characterData.characterType == DeviceType.BEDevice) {
|
||||||
|
beCharacterData = database.userCharacterDao().getBeData(characterId)
|
||||||
|
} else {
|
||||||
|
TODO("Not implemented")
|
||||||
|
//vbCharacterData = database.userCharacterDao().getVbData(characterId)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.itemIcon in 1 .. 5 && characterData.characterType == DeviceType.BEDevice) {
|
||||||
|
beCharacterData.itemType = item.itemIcon
|
||||||
|
beCharacterData.itemMultiplier = 3
|
||||||
|
beCharacterData.itemRemainingTime = item.itemLength
|
||||||
|
|
||||||
|
database
|
||||||
|
.userCharacterDao()
|
||||||
|
.updateBECharacterData(beCharacterData)
|
||||||
|
|
||||||
|
} else if (item.itemIcon == ItemTypes.EvoTimer.id) {
|
||||||
|
characterData.transformationCountdown += item.itemLength
|
||||||
|
if (characterData.transformationCountdown < 0) {
|
||||||
|
characterData.transformationCountdown = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// VB does not like it when the transformationCountdown is 0
|
||||||
|
if (characterData.characterType == DeviceType.VBDevice &&
|
||||||
|
characterData.transformationCountdown <= 0 ) {
|
||||||
|
characterData.transformationCountdown = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
database
|
||||||
|
.userCharacterDao()
|
||||||
|
.updateCharacter(characterData)
|
||||||
|
|
||||||
|
} else if (item.itemIcon == ItemTypes.LimitTimer.id) {
|
||||||
|
beCharacterData.remainingTrainingTimeInMinutes += item.itemLength
|
||||||
|
if (beCharacterData.remainingTrainingTimeInMinutes > 6000) {
|
||||||
|
beCharacterData.remainingTrainingTimeInMinutes = 6000
|
||||||
|
}
|
||||||
|
|
||||||
|
database
|
||||||
|
.userCharacterDao()
|
||||||
|
.updateBECharacterData(beCharacterData)
|
||||||
|
|
||||||
|
} else if (item.itemIcon == ItemTypes.Vitals.id) {
|
||||||
|
characterData.vitalPoints += item.itemLength
|
||||||
|
if (characterData.vitalPoints < 0) {
|
||||||
|
characterData.vitalPoints = 0
|
||||||
|
} else if (characterData.vitalPoints > 9999) {
|
||||||
|
characterData.vitalPoints = 9999
|
||||||
|
}
|
||||||
|
|
||||||
|
database
|
||||||
|
.userCharacterDao()
|
||||||
|
.updateCharacter(characterData)
|
||||||
|
}
|
||||||
|
|
||||||
|
consumeItem(item.id)
|
||||||
|
|
||||||
|
context.runOnUiThread {
|
||||||
|
onCompletion()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getItem(itemId: Long): ItemDtos.ItemsWithQuantities {
|
||||||
|
return database
|
||||||
|
.itemDao()
|
||||||
|
.getItem(itemId)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun consumeItem(itemId: Long) {
|
||||||
|
database
|
||||||
|
.itemDao()
|
||||||
|
.useItem(itemId)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.screens.itemsScreen
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.lazy.grid.GridCells
|
||||||
|
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
|
||||||
|
import androidx.compose.foundation.lazy.grid.items
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.navigation.NavController
|
||||||
|
import com.github.nacabaro.vbhelper.di.VBHelper
|
||||||
|
import com.github.nacabaro.vbhelper.dtos.ItemDtos
|
||||||
|
import com.github.nacabaro.vbhelper.source.ItemsRepository
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ItemsStore(
|
||||||
|
navController: NavController
|
||||||
|
) {
|
||||||
|
val application = LocalContext.current.applicationContext as VBHelper
|
||||||
|
val itemsRepository = ItemsRepository(application.container.db)
|
||||||
|
val myItems = remember { mutableStateOf(emptyList<ItemDtos.ItemsWithQuantities>()) }
|
||||||
|
var selectedElementIndex by remember { mutableStateOf<Int?>(null) }
|
||||||
|
|
||||||
|
LaunchedEffect(itemsRepository) {
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
myItems.value = itemsRepository.getAllItems()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (myItems.value.isEmpty()) {
|
||||||
|
Text("No items")
|
||||||
|
} else {
|
||||||
|
LazyVerticalGrid(
|
||||||
|
columns = GridCells.Fixed(3),
|
||||||
|
modifier = Modifier
|
||||||
|
) {
|
||||||
|
items(myItems.value) { index ->
|
||||||
|
ItemElement(
|
||||||
|
itemIcon = getIconResource(index.itemIcon),
|
||||||
|
lengthIcon = getLengthResource(index.itemLength),
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(8.dp),
|
||||||
|
onClick = {
|
||||||
|
selectedElementIndex = myItems.value.indexOf(index)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectedElementIndex != null) {
|
||||||
|
ItemDialog(
|
||||||
|
name = myItems.value[selectedElementIndex!!].name,
|
||||||
|
description = myItems.value[selectedElementIndex!!].description,
|
||||||
|
itemIcon = getIconResource(myItems.value[selectedElementIndex!!].itemIcon),
|
||||||
|
lengthIcon = getLengthResource(myItems.value[selectedElementIndex!!].itemLength),
|
||||||
|
amount = myItems.value[selectedElementIndex!!].quantity,
|
||||||
|
onClickUse = { },
|
||||||
|
onClickCancel = { selectedElementIndex = null }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.screens.itemsScreen
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.lazy.grid.GridCells
|
||||||
|
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
|
||||||
|
import androidx.compose.foundation.lazy.grid.items
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.navigation.NavController
|
||||||
|
import com.github.nacabaro.vbhelper.di.VBHelper
|
||||||
|
import com.github.nacabaro.vbhelper.dtos.ItemDtos
|
||||||
|
import com.github.nacabaro.vbhelper.navigation.NavigationItems
|
||||||
|
import com.github.nacabaro.vbhelper.source.ItemsRepository
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun MyItems(
|
||||||
|
navController: NavController,
|
||||||
|
) {
|
||||||
|
val application = LocalContext.current.applicationContext as VBHelper
|
||||||
|
val itemsRepository = ItemsRepository(application.container.db)
|
||||||
|
val myItems = remember { mutableStateOf(emptyList<ItemDtos.ItemsWithQuantities>()) }
|
||||||
|
var selectedElementIndex by remember { mutableStateOf<Int?>(null) }
|
||||||
|
|
||||||
|
LaunchedEffect(itemsRepository) {
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
myItems.value = itemsRepository.getUserItems()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (myItems.value.isEmpty()) {
|
||||||
|
Column(
|
||||||
|
verticalArrangement = Arrangement.Center,
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
modifier = Modifier.fillMaxSize()
|
||||||
|
) {
|
||||||
|
Text("No items")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LazyVerticalGrid(
|
||||||
|
columns = GridCells.Fixed(3),
|
||||||
|
modifier = Modifier
|
||||||
|
) {
|
||||||
|
items(myItems.value) { index ->
|
||||||
|
ItemElement(
|
||||||
|
itemIcon = getIconResource(index.itemIcon),
|
||||||
|
lengthIcon = getLengthResource(index.itemLength),
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(8.dp),
|
||||||
|
onClick = {
|
||||||
|
selectedElementIndex = myItems.value.indexOf(index)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectedElementIndex != null) {
|
||||||
|
ItemDialog(
|
||||||
|
name = myItems.value[selectedElementIndex!!].name,
|
||||||
|
description = myItems.value[selectedElementIndex!!].description,
|
||||||
|
itemIcon = getIconResource(myItems.value[selectedElementIndex!!].itemIcon),
|
||||||
|
lengthIcon = getLengthResource(myItems.value[selectedElementIndex!!].itemLength),
|
||||||
|
amount = myItems.value[selectedElementIndex!!].quantity,
|
||||||
|
onClickUse = {
|
||||||
|
navController
|
||||||
|
.navigate(
|
||||||
|
NavigationItems
|
||||||
|
.ApplyItem.route
|
||||||
|
.replace(
|
||||||
|
"{itemId}",
|
||||||
|
myItems.value[selectedElementIndex!!].id.toString()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
selectedElementIndex = null
|
||||||
|
},
|
||||||
|
onClickCancel = { selectedElementIndex = null }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+100
@@ -0,0 +1,100 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.screens.itemsScreen
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.material3.Button
|
||||||
|
import androidx.compose.material3.Card
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.res.painterResource
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.window.Dialog
|
||||||
|
import com.github.nacabaro.vbhelper.dtos.ItemDtos
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ObtainedItemDialog(
|
||||||
|
obtainedItem: ItemDtos.PurchasedItem,
|
||||||
|
onClickDismiss: () -> Unit
|
||||||
|
) {
|
||||||
|
Dialog(
|
||||||
|
onDismissRequest = onClickDismiss
|
||||||
|
) {
|
||||||
|
Card {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(16.dp)
|
||||||
|
) {
|
||||||
|
Column (
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(16.dp)
|
||||||
|
) {
|
||||||
|
Row {
|
||||||
|
Box(modifier = Modifier) {
|
||||||
|
Icon(
|
||||||
|
painter = painterResource(id = getIconResource(obtainedItem.itemIcon)),
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier
|
||||||
|
.size(96.dp)
|
||||||
|
.align(Alignment.Center)
|
||||||
|
)
|
||||||
|
Icon(
|
||||||
|
painter = painterResource(id = getLengthResource(obtainedItem.itemLength)),
|
||||||
|
contentDescription = null,
|
||||||
|
tint = MaterialTheme.colorScheme.outline,
|
||||||
|
modifier = Modifier
|
||||||
|
.size(64.dp)
|
||||||
|
.align(Alignment.BottomEnd)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Column (
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(16.dp)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
fontSize = MaterialTheme.typography.titleLarge.fontSize,
|
||||||
|
text = obtainedItem.itemName,
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Text(
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
fontSize = MaterialTheme.typography.bodyMedium.fontSize,
|
||||||
|
fontFamily = MaterialTheme.typography.bodyMedium.fontFamily,
|
||||||
|
text = obtainedItem.itemDescription,
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(4.dp)
|
||||||
|
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
fontSize = MaterialTheme.typography.bodySmall.fontSize,
|
||||||
|
fontFamily = MaterialTheme.typography.bodySmall.fontFamily,
|
||||||
|
text = "You have obtained ${obtainedItem.itemAmount} of this item",
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(5.dp)
|
||||||
|
)
|
||||||
|
Button(
|
||||||
|
onClick = onClickDismiss,
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
Text(text = "Dismiss")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+121
-17
@@ -11,20 +11,26 @@ import android.widget.Toast
|
|||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import com.github.cfogrady.vbnfc.TagCommunicator
|
import com.github.cfogrady.vbnfc.TagCommunicator
|
||||||
|
import com.github.cfogrady.vbnfc.be.BENfcCharacter
|
||||||
import com.github.cfogrady.vbnfc.data.NfcCharacter
|
import com.github.cfogrady.vbnfc.data.NfcCharacter
|
||||||
|
import com.github.cfogrady.vbnfc.vb.VBNfcCharacter
|
||||||
import com.github.nacabaro.vbhelper.ActivityLifecycleListener
|
import com.github.nacabaro.vbhelper.ActivityLifecycleListener
|
||||||
|
import com.github.nacabaro.vbhelper.di.VBHelper
|
||||||
|
import com.github.nacabaro.vbhelper.domain.device_data.BECharacterData
|
||||||
|
import com.github.nacabaro.vbhelper.domain.device_data.UserCharacter
|
||||||
import com.github.nacabaro.vbhelper.source.getCryptographicTransformerMap
|
import com.github.nacabaro.vbhelper.source.getCryptographicTransformerMap
|
||||||
import com.github.nacabaro.vbhelper.source.isMissingSecrets
|
import com.github.nacabaro.vbhelper.source.isMissingSecrets
|
||||||
import com.github.nacabaro.vbhelper.source.proto.Secrets
|
import com.github.nacabaro.vbhelper.source.proto.Secrets
|
||||||
|
import com.github.nacabaro.vbhelper.utils.DeviceType
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.stateIn
|
import kotlinx.coroutines.flow.stateIn
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import java.util.GregorianCalendar
|
||||||
|
|
||||||
class ScanScreenControllerImpl(
|
class ScanScreenControllerImpl(
|
||||||
override val secretsFlow: Flow<Secrets>,
|
override val secretsFlow: Flow<Secrets>,
|
||||||
private val nfcHandler: (NfcCharacter)->String,
|
private val componentActivity: ComponentActivity,
|
||||||
private val context: ComponentActivity,
|
|
||||||
private val registerActivityLifecycleListener: (String, ActivityLifecycleListener)->Unit,
|
private val registerActivityLifecycleListener: (String, ActivityLifecycleListener)->Unit,
|
||||||
private val unregisterActivityLifecycleListener: (String)->Unit,
|
private val unregisterActivityLifecycleListener: (String)->Unit,
|
||||||
): ScanScreenController {
|
): ScanScreenController {
|
||||||
@@ -32,9 +38,9 @@ class ScanScreenControllerImpl(
|
|||||||
private val nfcAdapter: NfcAdapter
|
private val nfcAdapter: NfcAdapter
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val maybeNfcAdapter = NfcAdapter.getDefaultAdapter(context)
|
val maybeNfcAdapter = NfcAdapter.getDefaultAdapter(componentActivity)
|
||||||
if (maybeNfcAdapter == null) {
|
if (maybeNfcAdapter == null) {
|
||||||
Toast.makeText(context, "No NFC on device!", Toast.LENGTH_SHORT).show()
|
Toast.makeText(componentActivity, "No NFC on device!", Toast.LENGTH_SHORT).show()
|
||||||
}
|
}
|
||||||
nfcAdapter = maybeNfcAdapter
|
nfcAdapter = maybeNfcAdapter
|
||||||
checkSecrets()
|
checkSecrets()
|
||||||
@@ -43,7 +49,7 @@ class ScanScreenControllerImpl(
|
|||||||
override fun onClickRead(secrets: Secrets, onComplete: ()->Unit) {
|
override fun onClickRead(secrets: Secrets, onComplete: ()->Unit) {
|
||||||
handleTag(secrets) { tagCommunicator ->
|
handleTag(secrets) { tagCommunicator ->
|
||||||
val character = tagCommunicator.receiveCharacter()
|
val character = tagCommunicator.receiveCharacter()
|
||||||
val resultMessage = nfcHandler(character)
|
val resultMessage = addCharacterScannedIntoDatabase(character)
|
||||||
onComplete.invoke()
|
onComplete.invoke()
|
||||||
resultMessage
|
resultMessage
|
||||||
}
|
}
|
||||||
@@ -51,7 +57,7 @@ class ScanScreenControllerImpl(
|
|||||||
|
|
||||||
override fun cancelRead() {
|
override fun cancelRead() {
|
||||||
if(nfcAdapter.isEnabled) {
|
if(nfcAdapter.isEnabled) {
|
||||||
nfcAdapter.disableReaderMode(context)
|
nfcAdapter.disableReaderMode(componentActivity)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,7 +80,7 @@ class ScanScreenControllerImpl(
|
|||||||
val options = Bundle()
|
val options = Bundle()
|
||||||
// Work around for some broken Nfc firmware implementations that poll the card too fast
|
// Work around for some broken Nfc firmware implementations that poll the card too fast
|
||||||
options.putInt(NfcAdapter.EXTRA_READER_PRESENCE_CHECK_DELAY, 250)
|
options.putInt(NfcAdapter.EXTRA_READER_PRESENCE_CHECK_DELAY, 250)
|
||||||
nfcAdapter.enableReaderMode(context, buildOnReadTag(secrets, handlerFunc), NfcAdapter.FLAG_READER_NFC_A or NfcAdapter.FLAG_READER_SKIP_NDEF_CHECK,
|
nfcAdapter.enableReaderMode(componentActivity, buildOnReadTag(secrets, handlerFunc), NfcAdapter.FLAG_READER_NFC_A or NfcAdapter.FLAG_READER_SKIP_NDEF_CHECK,
|
||||||
options
|
options
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -85,26 +91,26 @@ class ScanScreenControllerImpl(
|
|||||||
return { tag->
|
return { tag->
|
||||||
val nfcData = NfcA.get(tag)
|
val nfcData = NfcA.get(tag)
|
||||||
if (nfcData == null) {
|
if (nfcData == null) {
|
||||||
context.runOnUiThread {
|
componentActivity.runOnUiThread {
|
||||||
Toast.makeText(context, "Tag detected is not VB", Toast.LENGTH_SHORT).show()
|
Toast.makeText(componentActivity, "Tag detected is not VB", Toast.LENGTH_SHORT).show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
nfcData.connect()
|
nfcData.connect()
|
||||||
nfcData.use {
|
nfcData.use {
|
||||||
val tagCommunicator = TagCommunicator.getInstance(nfcData, secrets.getCryptographicTransformerMap())
|
val tagCommunicator = TagCommunicator.getInstance(nfcData, secrets.getCryptographicTransformerMap())
|
||||||
val successText = handlerFunc(tagCommunicator)
|
val successText = handlerFunc(tagCommunicator)
|
||||||
context.runOnUiThread {
|
componentActivity.runOnUiThread {
|
||||||
Toast.makeText(context, successText, Toast.LENGTH_SHORT).show()
|
Toast.makeText(componentActivity, successText, Toast.LENGTH_SHORT).show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun checkSecrets() {
|
private fun checkSecrets() {
|
||||||
context.lifecycleScope.launch(Dispatchers.IO) {
|
componentActivity.lifecycleScope.launch(Dispatchers.IO) {
|
||||||
if(secretsFlow.stateIn(context.lifecycleScope).value.isMissingSecrets()) {
|
if(secretsFlow.stateIn(componentActivity.lifecycleScope).value.isMissingSecrets()) {
|
||||||
context.runOnUiThread {
|
componentActivity.runOnUiThread {
|
||||||
Toast.makeText(context, "Missing Secrets. Go to settings and import Vital Arena APK", Toast.LENGTH_SHORT).show()
|
Toast.makeText(componentActivity, "Missing Secrets. Go to settings and import Vital Arena APK", Toast.LENGTH_SHORT).show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -141,7 +147,105 @@ class ScanScreenControllerImpl(
|
|||||||
|
|
||||||
// EXTRACTED DIRECTLY FROM EXAMPLE APP
|
// EXTRACTED DIRECTLY FROM EXAMPLE APP
|
||||||
private fun showWirelessSettings() {
|
private fun showWirelessSettings() {
|
||||||
Toast.makeText(context, "NFC must be enabled", Toast.LENGTH_SHORT).show()
|
Toast.makeText(componentActivity, "NFC must be enabled", Toast.LENGTH_SHORT).show()
|
||||||
context.startActivity(Intent(Settings.ACTION_WIRELESS_SETTINGS))
|
componentActivity.startActivity(Intent(Settings.ACTION_WIRELESS_SETTINGS))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun addCharacterScannedIntoDatabase(nfcCharacter: NfcCharacter): String {
|
||||||
|
val application = componentActivity.applicationContext as VBHelper
|
||||||
|
val storageRepository = application.container.db
|
||||||
|
|
||||||
|
val dimData = storageRepository
|
||||||
|
.dimDao()
|
||||||
|
.getDimById(nfcCharacter.dimId.toInt())
|
||||||
|
|
||||||
|
if (dimData == null) return "Card not found"
|
||||||
|
|
||||||
|
val cardCharData = storageRepository
|
||||||
|
.characterDao()
|
||||||
|
.getCharacterByMonIndex(nfcCharacter.charIndex.toInt(), dimData.id)
|
||||||
|
|
||||||
|
val characterData = UserCharacter(
|
||||||
|
charId = cardCharData.id,
|
||||||
|
ageInDays = nfcCharacter.ageInDays.toInt(),
|
||||||
|
nextAdventureMissionStage = nfcCharacter.nextAdventureMissionStage.toInt(),
|
||||||
|
mood = nfcCharacter.mood.toInt(),
|
||||||
|
vitalPoints = nfcCharacter.vitalPoints.toInt(),
|
||||||
|
transformationCountdown = nfcCharacter.transformationCountdownInMinutes.toInt(),
|
||||||
|
injuryStatus = nfcCharacter.injuryStatus,
|
||||||
|
trophies = nfcCharacter.trophies.toInt(),
|
||||||
|
currentPhaseBattlesWon = nfcCharacter.currentPhaseBattlesWon.toInt(),
|
||||||
|
currentPhaseBattlesLost = nfcCharacter.currentPhaseBattlesLost.toInt(),
|
||||||
|
totalBattlesWon = nfcCharacter.totalBattlesWon.toInt(),
|
||||||
|
totalBattlesLost = nfcCharacter.totalBattlesLost.toInt(),
|
||||||
|
activityLevel = nfcCharacter.activityLevel.toInt(),
|
||||||
|
heartRateCurrent = nfcCharacter.heartRateCurrent.toInt(),
|
||||||
|
characterType = when (nfcCharacter) {
|
||||||
|
is BENfcCharacter -> DeviceType.BEDevice
|
||||||
|
else -> DeviceType.VBDevice
|
||||||
|
},
|
||||||
|
isActive = true
|
||||||
|
)
|
||||||
|
|
||||||
|
storageRepository
|
||||||
|
.userCharacterDao()
|
||||||
|
.clearActiveCharacter()
|
||||||
|
|
||||||
|
val characterId: Long = storageRepository
|
||||||
|
.userCharacterDao()
|
||||||
|
.insertCharacterData(characterData)
|
||||||
|
|
||||||
|
if (nfcCharacter is BENfcCharacter) {
|
||||||
|
val extraCharacterData = BECharacterData(
|
||||||
|
id = characterId,
|
||||||
|
trainingHp = nfcCharacter.trainingHp.toInt(),
|
||||||
|
trainingAp = nfcCharacter.trainingAp.toInt(),
|
||||||
|
trainingBp = nfcCharacter.trainingBp.toInt(),
|
||||||
|
remainingTrainingTimeInMinutes = nfcCharacter.remainingTrainingTimeInMinutes.toInt(),
|
||||||
|
itemEffectActivityLevelValue = nfcCharacter.itemEffectActivityLevelValue.toInt(),
|
||||||
|
itemEffectMentalStateValue = nfcCharacter.itemEffectMentalStateValue.toInt(),
|
||||||
|
itemEffectMentalStateMinutesRemaining = nfcCharacter.itemEffectMentalStateMinutesRemaining.toInt(),
|
||||||
|
itemEffectActivityLevelMinutesRemaining = nfcCharacter.itemEffectActivityLevelMinutesRemaining.toInt(),
|
||||||
|
itemEffectVitalPointsChangeValue = nfcCharacter.itemEffectVitalPointsChangeValue.toInt(),
|
||||||
|
itemEffectVitalPointsChangeMinutesRemaining = nfcCharacter.itemEffectVitalPointsChangeMinutesRemaining.toInt(),
|
||||||
|
abilityRarity = nfcCharacter.abilityRarity,
|
||||||
|
abilityType = nfcCharacter.abilityType.toInt(),
|
||||||
|
abilityBranch = nfcCharacter.abilityBranch.toInt(),
|
||||||
|
abilityReset = nfcCharacter.abilityReset.toInt(),
|
||||||
|
rank = nfcCharacter.abilityReset.toInt(),
|
||||||
|
itemType = nfcCharacter.itemType.toInt(),
|
||||||
|
itemMultiplier = nfcCharacter.itemMultiplier.toInt(),
|
||||||
|
itemRemainingTime = nfcCharacter.itemRemainingTime.toInt(),
|
||||||
|
otp0 = "", //nfcCharacter.value!!.otp0.toString(),
|
||||||
|
otp1 = "", //nfcCharacter.value!!.otp1.toString(),
|
||||||
|
minorVersion = nfcCharacter.characterCreationFirmwareVersion.minorVersion.toInt(),
|
||||||
|
majorVersion = nfcCharacter.characterCreationFirmwareVersion.majorVersion.toInt(),
|
||||||
|
)
|
||||||
|
|
||||||
|
storageRepository
|
||||||
|
.userCharacterDao()
|
||||||
|
.insertBECharacterData(extraCharacterData)
|
||||||
|
|
||||||
|
val transformationHistoryWatch = nfcCharacter.transformationHistory
|
||||||
|
transformationHistoryWatch.map { item ->
|
||||||
|
if (item.toCharIndex.toInt() != 255) {
|
||||||
|
val date = GregorianCalendar(item.year.toInt(), item.month.toInt(), item.day.toInt())
|
||||||
|
.time
|
||||||
|
.time
|
||||||
|
|
||||||
|
storageRepository
|
||||||
|
.characterDao()
|
||||||
|
.insertTransformation(characterId, item.toCharIndex.toInt(), dimData.id, date)
|
||||||
|
|
||||||
|
storageRepository
|
||||||
|
.dexDao()
|
||||||
|
.insertCharacter(item.toCharIndex.toInt(), dimData.id, date)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (nfcCharacter is VBNfcCharacter) {
|
||||||
|
return "Not implemented yet"
|
||||||
|
}
|
||||||
|
|
||||||
|
return "Done reading character!"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+40
@@ -0,0 +1,40 @@
|
|||||||
|
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.material3.Scaffold
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.navigation.NavController
|
||||||
|
import com.github.nacabaro.vbhelper.components.TopBanner
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun CreditsScreen(
|
||||||
|
navController: NavController
|
||||||
|
) {
|
||||||
|
Scaffold (
|
||||||
|
topBar = {
|
||||||
|
TopBanner(
|
||||||
|
text = "Credits",
|
||||||
|
onBackClick = {
|
||||||
|
navController.popBackStack()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
) { contentPadding ->
|
||||||
|
Column (
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(top = contentPadding.calculateTopPadding())
|
||||||
|
) {
|
||||||
|
SettingsSection("Reverse engineering")
|
||||||
|
SettingsEntry(title = "cyanic", description = "Reversed the firmware and helped us during development.") { }
|
||||||
|
SettingsSection("Application development")
|
||||||
|
SettingsEntry(title = "cfogrady", description = "Developed vb-lib-nfc and part of this application.") { }
|
||||||
|
SettingsEntry(title = "nacabaro", description = "Developed this application.") { }
|
||||||
|
SettingsEntry(title = "lightheel", description = "Developing the battling part for this application, including server. Still in the works.") { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+17
-16
@@ -1,9 +1,5 @@
|
|||||||
package com.github.nacabaro.vbhelper.screens
|
package com.github.nacabaro.vbhelper.screens.settingsScreen
|
||||||
|
|
||||||
import android.net.Uri
|
|
||||||
import androidx.activity.ComponentActivity
|
|
||||||
import androidx.activity.result.ActivityResultLauncher
|
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
|
||||||
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
|
||||||
@@ -22,12 +18,12 @@ import androidx.compose.ui.unit.dp
|
|||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import androidx.navigation.NavController
|
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
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun SettingsScreen(
|
fun SettingsScreen(
|
||||||
navController: NavController,
|
navController: NavController,
|
||||||
settingsScreenController: SettingsScreenController,
|
settingsScreenController: SettingsScreenControllerImpl,
|
||||||
onClickImportCard: () -> Unit
|
|
||||||
) {
|
) {
|
||||||
Scaffold (
|
Scaffold (
|
||||||
topBar = {
|
topBar = {
|
||||||
@@ -49,24 +45,29 @@ fun SettingsScreen(
|
|||||||
) {
|
) {
|
||||||
SettingsSection("NFC Communication")
|
SettingsSection("NFC Communication")
|
||||||
SettingsEntry(title = "Import APK", description = "Import Secrets From Vital Arean 2.1.0 APK") {
|
SettingsEntry(title = "Import APK", description = "Import Secrets From Vital Arean 2.1.0 APK") {
|
||||||
settingsScreenController.apkFilePickLauncher.launch(arrayOf("*/*"))
|
settingsScreenController.onClickImportApk()
|
||||||
}
|
}
|
||||||
SettingsSection("DiM/BEm management")
|
SettingsSection("DiM/BEm management")
|
||||||
SettingsEntry(title = "Import DiM card", description = "Import DiM/BEm card file", onClick = onClickImportCard)
|
SettingsEntry(title = "Import card", description = "Import DiM/BEm card file") {
|
||||||
|
settingsScreenController.onClickImportCard()
|
||||||
|
}
|
||||||
SettingsEntry(title = "Rename DiM/BEm", description = "Set card name") { }
|
SettingsEntry(title = "Rename DiM/BEm", description = "Set card name") { }
|
||||||
SettingsSection("About and credits")
|
SettingsSection("About and credits")
|
||||||
SettingsEntry(title = "Credits", description = "Credits") { }
|
SettingsEntry(title = "Credits", description = "Credits") {
|
||||||
|
navController.navigate(NavigationItems.Credits.route)
|
||||||
|
}
|
||||||
SettingsEntry(title = "About", description = "About") { }
|
SettingsEntry(title = "About", description = "About") { }
|
||||||
|
SettingsSection("Data management")
|
||||||
|
SettingsEntry(title = "Export data", description = "Export application database") {
|
||||||
|
settingsScreenController.onClickOpenDirectory()
|
||||||
|
}
|
||||||
|
SettingsEntry(title = "Import data", description = "Import application database") {
|
||||||
|
settingsScreenController.onClickImportDatabase()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun buildFilePickLauncher(activity: ComponentActivity, onItemPicked: (Uri?) -> Unit): ActivityResultLauncher<Array<String>> {
|
|
||||||
return activity.registerForActivityResult(ActivityResultContracts.OpenDocument()) {
|
|
||||||
onItemPicked.invoke(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun SettingsEntry(
|
fun SettingsEntry(
|
||||||
title: String,
|
title: String,
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.screens.settingsScreen
|
||||||
|
|
||||||
|
interface SettingsScreenController {
|
||||||
|
fun onClickOpenDirectory()
|
||||||
|
fun onClickImportDatabase()
|
||||||
|
fun onClickImportApk()
|
||||||
|
fun onClickImportCard()
|
||||||
|
}
|
||||||
+322
@@ -0,0 +1,322 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.screens.settingsScreen
|
||||||
|
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import android.net.Uri
|
||||||
|
import android.provider.OpenableColumns
|
||||||
|
import android.util.Log
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.activity.ComponentActivity
|
||||||
|
import androidx.activity.result.ActivityResultLauncher
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
|
import com.github.cfogrady.vb.dim.card.BemCard
|
||||||
|
import com.github.cfogrady.vb.dim.card.DimReader
|
||||||
|
import com.github.cfogrady.vbnfc.data.NfcCharacter
|
||||||
|
import com.github.nacabaro.vbhelper.database.AppDatabase
|
||||||
|
import com.github.nacabaro.vbhelper.di.VBHelper
|
||||||
|
import com.github.nacabaro.vbhelper.domain.Sprites
|
||||||
|
import com.github.nacabaro.vbhelper.domain.characters.Card
|
||||||
|
import com.github.nacabaro.vbhelper.domain.characters.Character
|
||||||
|
import com.github.nacabaro.vbhelper.source.ApkSecretsImporter
|
||||||
|
import com.github.nacabaro.vbhelper.source.SecretsImporter
|
||||||
|
import com.github.nacabaro.vbhelper.source.SecretsRepository
|
||||||
|
import com.github.nacabaro.vbhelper.source.proto.Secrets
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import java.io.File
|
||||||
|
import java.io.InputStream
|
||||||
|
import java.io.OutputStream
|
||||||
|
|
||||||
|
|
||||||
|
class SettingsScreenControllerImpl(
|
||||||
|
private val context: ComponentActivity,
|
||||||
|
): SettingsScreenController {
|
||||||
|
private val roomDbName = "internalDb"
|
||||||
|
private val filePickerLauncher: ActivityResultLauncher<String>
|
||||||
|
private val filePickerOpenerLauncher: ActivityResultLauncher<Array<String>>
|
||||||
|
private val filePickerApk: ActivityResultLauncher<Array<String>>
|
||||||
|
private val filePickerCard: ActivityResultLauncher<Array<String>>
|
||||||
|
private val secretsImporter: SecretsImporter = ApkSecretsImporter()
|
||||||
|
private val application = context.applicationContext as VBHelper
|
||||||
|
private val secretsRepository: SecretsRepository = application.container.dataStoreSecretsRepository
|
||||||
|
private val database: AppDatabase = application.container.db
|
||||||
|
|
||||||
|
init {
|
||||||
|
filePickerLauncher = context.registerForActivityResult(
|
||||||
|
ActivityResultContracts.CreateDocument("application/octet-stream")
|
||||||
|
) { uri ->
|
||||||
|
if (uri != null) {
|
||||||
|
exportDatabase(uri)
|
||||||
|
} else {
|
||||||
|
context.runOnUiThread {
|
||||||
|
Toast.makeText(context, "No destination selected", Toast.LENGTH_SHORT)
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
filePickerOpenerLauncher = context.registerForActivityResult(
|
||||||
|
ActivityResultContracts.OpenDocument()
|
||||||
|
) { uri ->
|
||||||
|
if (uri != null) {
|
||||||
|
importDatabase(uri)
|
||||||
|
} else {
|
||||||
|
context.runOnUiThread {
|
||||||
|
Toast.makeText(context, "No source selected", Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
filePickerApk = context.registerForActivityResult(
|
||||||
|
ActivityResultContracts.OpenDocument()
|
||||||
|
) { uri ->
|
||||||
|
if (uri != null) {
|
||||||
|
importApk(uri)
|
||||||
|
} else {
|
||||||
|
context.runOnUiThread {
|
||||||
|
Toast.makeText(context, "APK import cancelled", Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
filePickerCard = context.registerForActivityResult(
|
||||||
|
ActivityResultContracts.OpenDocument()
|
||||||
|
) { uri ->
|
||||||
|
if (uri != null) {
|
||||||
|
importCard(uri)
|
||||||
|
} else {
|
||||||
|
context.runOnUiThread {
|
||||||
|
Toast.makeText(context, "Card import cancelled", Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onClickOpenDirectory() {
|
||||||
|
filePickerLauncher.launch("My application data.vbhelper")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onClickImportDatabase() {
|
||||||
|
filePickerOpenerLauncher.launch(arrayOf("application/octet-stream"))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onClickImportApk() {
|
||||||
|
filePickerApk.launch(arrayOf("*/*"))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onClickImportCard() {
|
||||||
|
filePickerCard.launch(arrayOf("*/*"))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun importCard(uri: Uri) {
|
||||||
|
context.lifecycleScope.launch(Dispatchers.IO) {
|
||||||
|
val contentResolver = context.contentResolver
|
||||||
|
val inputStream = contentResolver.openInputStream(uri)
|
||||||
|
inputStream.use { fileReader ->
|
||||||
|
val dimReader = DimReader()
|
||||||
|
val card = dimReader.readCard(fileReader, false)
|
||||||
|
|
||||||
|
val cardModel = Card(
|
||||||
|
dimId = card.header.dimId,
|
||||||
|
logo = card.spriteData.sprites[0].pixelData,
|
||||||
|
name = card.spriteData.text, // TODO Make user write card name// TODO Make user write card name
|
||||||
|
stageCount = card.adventureLevels.levels.size,
|
||||||
|
logoHeight = card.spriteData.sprites[0].height,
|
||||||
|
logoWidth = card.spriteData.sprites[0].width,
|
||||||
|
isBEm = card is BemCard
|
||||||
|
)
|
||||||
|
|
||||||
|
val dimId = database
|
||||||
|
.dimDao()
|
||||||
|
.insertNewDim(cardModel)
|
||||||
|
|
||||||
|
val characters = card.characterStats.characterEntries
|
||||||
|
|
||||||
|
var spriteCounter = when (card is BemCard) {
|
||||||
|
true -> 54
|
||||||
|
false -> 10
|
||||||
|
}
|
||||||
|
|
||||||
|
val domainCharacters = mutableListOf<Character>()
|
||||||
|
|
||||||
|
for (index in 0 until characters.size) {
|
||||||
|
domainCharacters.add(
|
||||||
|
Character(
|
||||||
|
dimId = dimId,
|
||||||
|
monIndex = index,
|
||||||
|
name = card.spriteData.sprites[spriteCounter].pixelData,
|
||||||
|
stage = characters[index].stage,
|
||||||
|
attribute = NfcCharacter.Attribute.entries[characters[index].attribute],
|
||||||
|
baseHp = characters[index].hp,
|
||||||
|
baseBp = characters[index].dp,
|
||||||
|
baseAp = characters[index].ap,
|
||||||
|
sprite1 = card.spriteData.sprites[spriteCounter + 1].pixelData,
|
||||||
|
sprite2 = card.spriteData.sprites[spriteCounter + 2].pixelData,
|
||||||
|
nameWidth = card.spriteData.sprites[spriteCounter].width,
|
||||||
|
nameHeight = card.spriteData.sprites[spriteCounter].height,
|
||||||
|
spritesWidth = card.spriteData.sprites[spriteCounter + 1].width,
|
||||||
|
spritesHeight = card.spriteData.sprites[spriteCounter + 1].height
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
spriteCounter += if (card is BemCard) {
|
||||||
|
14
|
||||||
|
} else {
|
||||||
|
when (index) {
|
||||||
|
0 -> 6
|
||||||
|
1 -> 7
|
||||||
|
else -> 14
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
database
|
||||||
|
.characterDao()
|
||||||
|
.insertCharacter(*domainCharacters.toTypedArray())
|
||||||
|
|
||||||
|
val sprites = card.spriteData.sprites.map { sprite ->
|
||||||
|
Sprites(
|
||||||
|
id = 0,
|
||||||
|
sprite = sprite.pixelData,
|
||||||
|
width = sprite.width,
|
||||||
|
height = sprite.height
|
||||||
|
)
|
||||||
|
}
|
||||||
|
database
|
||||||
|
.characterDao()
|
||||||
|
.insertSprite(*sprites.toTypedArray())
|
||||||
|
}
|
||||||
|
|
||||||
|
inputStream?.close()
|
||||||
|
context.runOnUiThread {
|
||||||
|
Toast.makeText(context, "Import successful!", Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun exportDatabase(destinationUri: Uri) {
|
||||||
|
context.lifecycleScope.launch(Dispatchers.IO) {
|
||||||
|
try {
|
||||||
|
val dbFile = File(context.getDatabasePath(roomDbName).absolutePath)
|
||||||
|
if (!dbFile.exists()) {
|
||||||
|
throw IllegalStateException("Database file does not exist!")
|
||||||
|
}
|
||||||
|
|
||||||
|
application.container.db.close()
|
||||||
|
|
||||||
|
context.contentResolver.openOutputStream(destinationUri)?.use { outputStream ->
|
||||||
|
dbFile.inputStream().use { inputStream ->
|
||||||
|
copyFile(inputStream, outputStream)
|
||||||
|
}
|
||||||
|
} ?: throw IllegalArgumentException("Unable to open destination Uri for writing")
|
||||||
|
|
||||||
|
context.runOnUiThread {
|
||||||
|
Toast.makeText(context, "Database exported successfully!", Toast.LENGTH_SHORT).show()
|
||||||
|
Toast.makeText(context, "Closing application to avoid changes.", Toast.LENGTH_LONG).show()
|
||||||
|
context.finishAffinity()
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e("ScanScreenController", "Error exporting database $e")
|
||||||
|
context.runOnUiThread {
|
||||||
|
Toast.makeText(context, "Error exporting database: ${e.message}", Toast.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun importDatabase(sourceUri: Uri) {
|
||||||
|
context.lifecycleScope.launch(Dispatchers.IO) {
|
||||||
|
try {
|
||||||
|
if (!getFileNameFromUri(sourceUri)!!.endsWith(".vbhelper")) {
|
||||||
|
context.runOnUiThread {
|
||||||
|
Toast.makeText(context, "Invalid file format", Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
return@launch
|
||||||
|
}
|
||||||
|
|
||||||
|
application.container.db.close()
|
||||||
|
|
||||||
|
val dbPath = context.getDatabasePath(roomDbName)
|
||||||
|
val shmFile = File(dbPath.parent, "$roomDbName-shm")
|
||||||
|
val walFile = File(dbPath.parent, "$roomDbName-wal")
|
||||||
|
|
||||||
|
// Delete existing database files
|
||||||
|
if (dbPath.exists()) dbPath.delete()
|
||||||
|
if (shmFile.exists()) shmFile.delete()
|
||||||
|
if (walFile.exists()) walFile.delete()
|
||||||
|
|
||||||
|
val dbFile = File(dbPath.absolutePath)
|
||||||
|
|
||||||
|
context.contentResolver.openInputStream(sourceUri)?.use { inputStream ->
|
||||||
|
dbFile.outputStream().use { outputStream ->
|
||||||
|
copyFile(inputStream, outputStream)
|
||||||
|
}
|
||||||
|
} ?: throw IllegalArgumentException("Unable to open source Uri for reading")
|
||||||
|
|
||||||
|
context.runOnUiThread {
|
||||||
|
Toast.makeText(context, "Database imported successfully!", Toast.LENGTH_SHORT).show()
|
||||||
|
Toast.makeText(context, "Reopen the app to finish import process!", Toast.LENGTH_LONG).show()
|
||||||
|
context.finishAffinity()
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e("ScanScreenController", "Error importing database $e")
|
||||||
|
context.runOnUiThread {
|
||||||
|
Toast.makeText(context, "Error importing database: ${e.message}", Toast.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getFileNameFromUri(uri: Uri): String? {
|
||||||
|
var fileName: String? = null
|
||||||
|
val cursor = context.contentResolver.query(uri, null, null, null, null)
|
||||||
|
cursor?.use {
|
||||||
|
if (it.moveToFirst()) {
|
||||||
|
val nameIndex = it.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME)
|
||||||
|
fileName = it.getString(nameIndex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fileName
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun copyFile(inputStream: InputStream, outputStream: OutputStream) {
|
||||||
|
val buffer = ByteArray(1024)
|
||||||
|
var bytesRead: Int
|
||||||
|
while (inputStream.read(buffer).also { bytesRead = it } != -1) {
|
||||||
|
outputStream.write(buffer, 0, bytesRead)
|
||||||
|
}
|
||||||
|
outputStream.flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun importApk(uri: Uri) {
|
||||||
|
context.lifecycleScope.launch(Dispatchers.IO) {
|
||||||
|
context.contentResolver.openInputStream(uri).use {
|
||||||
|
if(it == null) {
|
||||||
|
context.runOnUiThread {
|
||||||
|
Toast.makeText(
|
||||||
|
context,
|
||||||
|
"Selected file is empty!",
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
).show()
|
||||||
|
}
|
||||||
|
return@launch
|
||||||
|
}
|
||||||
|
val secrets: Secrets?
|
||||||
|
try {
|
||||||
|
secrets = secretsImporter.importSecrets(it)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
context.runOnUiThread {
|
||||||
|
Toast.makeText(context, "Secrets import failed. Please only select the official Vital Arena App 2.1.0 APK.", Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
return@launch
|
||||||
|
}
|
||||||
|
context.lifecycleScope.launch(Dispatchers.IO) {
|
||||||
|
secretsRepository.updateSecrets(secrets)
|
||||||
|
}.invokeOnCompletion {
|
||||||
|
context.runOnUiThread {
|
||||||
|
Toast.makeText(context, "Secrets successfully imported. Connections with devices are now possible.", Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+117
@@ -0,0 +1,117 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.screens.storageScreen
|
||||||
|
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.width
|
||||||
|
import androidx.compose.material3.Button
|
||||||
|
import androidx.compose.material3.Card
|
||||||
|
import androidx.compose.material3.DropdownMenu
|
||||||
|
import androidx.compose.material3.DropdownMenuItem
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableIntStateOf
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.res.painterResource
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.window.Dialog
|
||||||
|
import com.github.nacabaro.vbhelper.R
|
||||||
|
|
||||||
|
fun getAdventureTime(time: Int): String {
|
||||||
|
return when (time) {
|
||||||
|
360 -> "6 hours"
|
||||||
|
720 -> "12 hours"
|
||||||
|
1440 -> "24 hours"
|
||||||
|
else -> "Unknown"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun StorageAdventureTimeDialog(
|
||||||
|
onClickSendToAdventure: (time: Long) -> Unit,
|
||||||
|
onDismissRequest: () -> Unit
|
||||||
|
) {
|
||||||
|
val times = arrayOf(360, 720, 1440)
|
||||||
|
var expanded by remember { mutableStateOf(false) }
|
||||||
|
var itemPosition by remember { mutableIntStateOf(-1) }
|
||||||
|
|
||||||
|
Dialog(
|
||||||
|
onDismissRequest = onDismissRequest
|
||||||
|
) {
|
||||||
|
Card {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(16.dp)
|
||||||
|
) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(16.dp)
|
||||||
|
) {
|
||||||
|
Row (
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
|
modifier = Modifier
|
||||||
|
.width(256.dp)
|
||||||
|
.clickable(true) {
|
||||||
|
expanded = true
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = when (itemPosition) {
|
||||||
|
-1 -> "Choose time"
|
||||||
|
else -> getAdventureTime(times[itemPosition])
|
||||||
|
}
|
||||||
|
)
|
||||||
|
Icon(
|
||||||
|
painter = painterResource(R.drawable.baseline_single_arrow_down),
|
||||||
|
contentDescription = "Show more"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
DropdownMenu(
|
||||||
|
expanded = expanded,
|
||||||
|
onDismissRequest = { expanded = false },
|
||||||
|
modifier = Modifier
|
||||||
|
.width(256.dp)
|
||||||
|
) {
|
||||||
|
times.forEach { time ->
|
||||||
|
DropdownMenuItem(
|
||||||
|
text = { Text(getAdventureTime(time)) },
|
||||||
|
onClick = {
|
||||||
|
itemPosition = times.indexOf(time)
|
||||||
|
expanded = false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Button(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth(),
|
||||||
|
onClick = {
|
||||||
|
if (itemPosition != -1) {
|
||||||
|
onClickSendToAdventure(times[itemPosition].toLong())
|
||||||
|
onDismissRequest()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Text(text = "Send on adventure")
|
||||||
|
}
|
||||||
|
Button(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth(),
|
||||||
|
onClick = onDismissRequest
|
||||||
|
) {
|
||||||
|
Text(text = "Dismiss")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+163
@@ -0,0 +1,163 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.screens.storageScreen
|
||||||
|
|
||||||
|
import androidx.compose.foundation.Image
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.material3.Button
|
||||||
|
import androidx.compose.material3.Card
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.FilterQuality
|
||||||
|
import androidx.compose.ui.graphics.asImageBitmap
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.window.Dialog
|
||||||
|
import androidx.compose.ui.window.DialogProperties
|
||||||
|
import com.github.nacabaro.vbhelper.di.VBHelper
|
||||||
|
import com.github.nacabaro.vbhelper.dtos.CharacterDtos
|
||||||
|
import com.github.nacabaro.vbhelper.source.StorageRepository
|
||||||
|
import com.github.nacabaro.vbhelper.utils.BitmapData
|
||||||
|
import com.github.nacabaro.vbhelper.utils.getBitmap
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun StorageDialog(
|
||||||
|
characterId: Long,
|
||||||
|
onDismissRequest: () -> Unit,
|
||||||
|
onSendToBracelet: () -> Unit,
|
||||||
|
onClickSetActive: () -> Unit,
|
||||||
|
onClickSendToAdventure: (time: Long) -> Unit
|
||||||
|
) {
|
||||||
|
val coroutineScope = rememberCoroutineScope()
|
||||||
|
val application = LocalContext.current.applicationContext as VBHelper
|
||||||
|
val storageRepository = StorageRepository(application.container.db)
|
||||||
|
val character = remember { mutableStateOf<CharacterDtos.CharacterWithSprites?>(null) }
|
||||||
|
val characterSprite = remember { mutableStateOf<BitmapData?>(null) }
|
||||||
|
val characterName = remember { mutableStateOf<BitmapData?>(null) }
|
||||||
|
var onSendToAdventureClicked by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
|
LaunchedEffect(storageRepository) {
|
||||||
|
coroutineScope.launch {
|
||||||
|
character.value = storageRepository.getSingleCharacter(characterId)
|
||||||
|
characterSprite.value = BitmapData(
|
||||||
|
bitmap = character.value!!.spriteIdle,
|
||||||
|
width = character.value!!.spriteWidth,
|
||||||
|
height = character.value!!.spriteHeight
|
||||||
|
)
|
||||||
|
characterName.value = BitmapData(
|
||||||
|
bitmap = character.value!!.nameSprite,
|
||||||
|
width = character.value!!.nameSpriteWidth,
|
||||||
|
height = character.value!!.nameSpriteHeight
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Dialog(
|
||||||
|
onDismissRequest = onDismissRequest,
|
||||||
|
properties = DialogProperties(
|
||||||
|
dismissOnBackPress = true,
|
||||||
|
dismissOnClickOutside = true
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
Card(
|
||||||
|
shape = RoundedCornerShape(16.dp)
|
||||||
|
) {
|
||||||
|
Column (
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(16.dp)
|
||||||
|
) {
|
||||||
|
if (character.value != null &&
|
||||||
|
characterSprite.value != null &&
|
||||||
|
characterName.value != null
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
) {
|
||||||
|
val bitmap = remember (characterSprite.value!!) { characterSprite.value!!.getBitmap() }
|
||||||
|
val imageBitmap = remember(bitmap) { bitmap.asImageBitmap() }
|
||||||
|
val density: Float = LocalContext.current.resources.displayMetrics.density
|
||||||
|
val dpSize = (characterSprite.value!!.width * 4 / density).dp
|
||||||
|
Image(
|
||||||
|
bitmap = imageBitmap,
|
||||||
|
contentDescription = "Character image",
|
||||||
|
filterQuality = FilterQuality.None,
|
||||||
|
modifier = Modifier
|
||||||
|
.size(dpSize)
|
||||||
|
)
|
||||||
|
val nameBitmap = remember (characterName.value!!) { characterName.value!!.getBitmap() }
|
||||||
|
val nameImageBitmap = remember(nameBitmap) { nameBitmap.asImageBitmap() }
|
||||||
|
val nameDpSize = (characterName.value!!.width * 4 / density).dp
|
||||||
|
Image(
|
||||||
|
bitmap = nameImageBitmap,
|
||||||
|
contentDescription = "Character image",
|
||||||
|
filterQuality = FilterQuality.None,
|
||||||
|
modifier = Modifier
|
||||||
|
.size(nameDpSize)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Row(
|
||||||
|
horizontalArrangement = Arrangement.Center,
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
Button(
|
||||||
|
onClick = onSendToBracelet,
|
||||||
|
modifier = Modifier
|
||||||
|
.weight(1f)
|
||||||
|
) {
|
||||||
|
Text(text = "Send to watch")
|
||||||
|
}
|
||||||
|
Spacer(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(4.dp)
|
||||||
|
)
|
||||||
|
Button(
|
||||||
|
onClick = onClickSetActive,
|
||||||
|
) {
|
||||||
|
Text(text = "Set active")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Button(
|
||||||
|
onClick = {
|
||||||
|
onSendToAdventureClicked = true
|
||||||
|
},
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
Text(text = "Send on adventure")
|
||||||
|
}
|
||||||
|
Button(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth(),
|
||||||
|
onClick = onDismissRequest
|
||||||
|
) {
|
||||||
|
Text(text = "Close")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (onSendToAdventureClicked) {
|
||||||
|
StorageAdventureTimeDialog(
|
||||||
|
onClickSendToAdventure = { time ->
|
||||||
|
onClickSendToAdventure(time)
|
||||||
|
},
|
||||||
|
onDismissRequest = { onSendToAdventureClicked = false }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
+145
@@ -0,0 +1,145 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.screens.storageScreen
|
||||||
|
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.compose.foundation.gestures.Orientation
|
||||||
|
import androidx.compose.foundation.gestures.scrollable
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.lazy.grid.GridCells
|
||||||
|
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
|
||||||
|
import androidx.compose.foundation.lazy.grid.items
|
||||||
|
import androidx.compose.foundation.rememberScrollState
|
||||||
|
import androidx.compose.material3.Scaffold
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import androidx.navigation.NavController
|
||||||
|
import com.github.nacabaro.vbhelper.components.CharacterEntry
|
||||||
|
import com.github.nacabaro.vbhelper.components.TopBanner
|
||||||
|
import com.github.nacabaro.vbhelper.di.VBHelper
|
||||||
|
import com.github.nacabaro.vbhelper.dtos.CharacterDtos
|
||||||
|
import com.github.nacabaro.vbhelper.navigation.NavigationItems
|
||||||
|
import com.github.nacabaro.vbhelper.screens.adventureScreen.AdventureScreenControllerImpl
|
||||||
|
import com.github.nacabaro.vbhelper.source.StorageRepository
|
||||||
|
import com.github.nacabaro.vbhelper.utils.BitmapData
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun StorageScreen(
|
||||||
|
navController: NavController,
|
||||||
|
storageScreenController: StorageScreenControllerImpl,
|
||||||
|
adventureScreenController: AdventureScreenControllerImpl
|
||||||
|
) {
|
||||||
|
val coroutineScope = rememberCoroutineScope()
|
||||||
|
val application = LocalContext.current.applicationContext as VBHelper
|
||||||
|
val storageRepository = StorageRepository(application.container.db)
|
||||||
|
val monList = remember { mutableStateOf<List<CharacterDtos.CharacterWithSprites>>(emptyList()) }
|
||||||
|
var selectedCharacter by remember { mutableStateOf<Long?>(null) }
|
||||||
|
|
||||||
|
LaunchedEffect(storageRepository, selectedCharacter) {
|
||||||
|
coroutineScope.launch {
|
||||||
|
val characterList = storageRepository.getAllCharacters()
|
||||||
|
monList.value = characterList
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Scaffold (
|
||||||
|
topBar = {
|
||||||
|
TopBanner(
|
||||||
|
text = "My characters",
|
||||||
|
onAdventureClick = {
|
||||||
|
navController.navigate(NavigationItems.Adventure.route)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
) { contentPadding ->
|
||||||
|
if (monList.value.isEmpty()) {
|
||||||
|
Column (
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
verticalArrangement = Arrangement.Center,
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(top = contentPadding.calculateTopPadding())
|
||||||
|
.fillMaxSize()
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = "Nothing to see here",
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
modifier = Modifier
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LazyVerticalGrid(
|
||||||
|
columns = GridCells.Fixed(3),
|
||||||
|
modifier = Modifier
|
||||||
|
.scrollable(state = rememberScrollState(), orientation = Orientation.Vertical)
|
||||||
|
.padding(top = contentPadding.calculateTopPadding())
|
||||||
|
) {
|
||||||
|
items(monList.value) { index ->
|
||||||
|
CharacterEntry(
|
||||||
|
icon = BitmapData(
|
||||||
|
bitmap = index.spriteIdle,
|
||||||
|
width = index.spriteWidth,
|
||||||
|
height = index.spriteHeight
|
||||||
|
),
|
||||||
|
onClick = {
|
||||||
|
if (!index.isInAdventure) {
|
||||||
|
selectedCharacter = index.id
|
||||||
|
} else {
|
||||||
|
Toast.makeText(
|
||||||
|
application,
|
||||||
|
"This character is in an adventure",
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
).show()
|
||||||
|
navController.navigate(
|
||||||
|
NavigationItems.Adventure.route
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectedCharacter != null) {
|
||||||
|
StorageDialog(
|
||||||
|
characterId = selectedCharacter!!,
|
||||||
|
onDismissRequest = { selectedCharacter = null },
|
||||||
|
onClickSetActive = {
|
||||||
|
storageScreenController
|
||||||
|
.setActive(selectedCharacter!!) {
|
||||||
|
selectedCharacter = null
|
||||||
|
navController.navigate(NavigationItems.Home.route)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onSendToBracelet = {
|
||||||
|
navController.navigate(
|
||||||
|
NavigationItems.Scan.route.replace(
|
||||||
|
"{characterId}",
|
||||||
|
selectedCharacter.toString()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
onClickSendToAdventure = { time ->
|
||||||
|
adventureScreenController
|
||||||
|
.sendCharacterToAdventure(
|
||||||
|
characterId = selectedCharacter!!,
|
||||||
|
timeInMinutes = time
|
||||||
|
)
|
||||||
|
selectedCharacter = null
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.screens.storageScreen
|
||||||
|
|
||||||
|
interface StorageScreenController {
|
||||||
|
fun setActive(characterId: Long, onCompletion: () -> Unit)
|
||||||
|
}
|
||||||
+31
@@ -0,0 +1,31 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.screens.storageScreen
|
||||||
|
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.activity.ComponentActivity
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import com.github.nacabaro.vbhelper.di.VBHelper
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
class StorageScreenControllerImpl(
|
||||||
|
private val componentActivity: ComponentActivity
|
||||||
|
): StorageScreenController {
|
||||||
|
private val application = componentActivity.applicationContext as VBHelper
|
||||||
|
private val database = application.container.db
|
||||||
|
|
||||||
|
override fun setActive(characterId: Long, onCompletion: () -> Unit) {
|
||||||
|
componentActivity.lifecycleScope.launch(Dispatchers.IO) {
|
||||||
|
database.userCharacterDao().clearActiveCharacter()
|
||||||
|
database.userCharacterDao().setActiveCharacter(characterId)
|
||||||
|
|
||||||
|
componentActivity.runOnUiThread {
|
||||||
|
Toast.makeText(
|
||||||
|
componentActivity,
|
||||||
|
"Active character updated!",
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
).show()
|
||||||
|
onCompletion()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.source
|
||||||
|
|
||||||
|
import com.github.nacabaro.vbhelper.database.AppDatabase
|
||||||
|
import com.github.nacabaro.vbhelper.domain.items.Items
|
||||||
|
import com.github.nacabaro.vbhelper.dtos.ItemDtos
|
||||||
|
|
||||||
|
class ItemsRepository(
|
||||||
|
private val db: AppDatabase
|
||||||
|
) {
|
||||||
|
suspend fun getAllItems(): List<ItemDtos.ItemsWithQuantities> {
|
||||||
|
return db.itemDao().getAllItems()
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun getUserItems(): List<ItemDtos.ItemsWithQuantities> {
|
||||||
|
return db.itemDao().getAllUserItems()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,8 +2,6 @@ package com.github.nacabaro.vbhelper.source
|
|||||||
|
|
||||||
import com.github.nacabaro.vbhelper.database.AppDatabase
|
import com.github.nacabaro.vbhelper.database.AppDatabase
|
||||||
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.TransformationHistory
|
|
||||||
import com.github.nacabaro.vbhelper.domain.device_data.UserCharacter
|
|
||||||
import com.github.nacabaro.vbhelper.dtos.CharacterDtos
|
import com.github.nacabaro.vbhelper.dtos.CharacterDtos
|
||||||
|
|
||||||
class StorageRepository (
|
class StorageRepository (
|
||||||
@@ -13,8 +11,8 @@ class StorageRepository (
|
|||||||
return db.userCharacterDao().getAllCharacters()
|
return db.userCharacterDao().getAllCharacters()
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getSingleCharacter(id: Long): UserCharacter {
|
suspend fun getSingleCharacter(id: Long): CharacterDtos.CharacterWithSprites {
|
||||||
return db.userCharacterDao().getCharacter(id)
|
return db.userCharacterDao().getCharacterWithSprites(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getCharacterBeData(id: Long): BECharacterData {
|
suspend fun getCharacterBeData(id: Long): BECharacterData {
|
||||||
@@ -37,8 +35,7 @@ class StorageRepository (
|
|||||||
return db.userCharacterDao().deleteCharacterById(id)
|
return db.userCharacterDao().deleteCharacterById(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setActiveCharacter(id: Long) {
|
suspend fun getAdventureCharacters(): List<CharacterDtos.AdventureCharacterWithSprites> {
|
||||||
db.userCharacterDao().clearActiveCharacter()
|
return db.adventureDao().getAdventureCharacters()
|
||||||
return db.userCharacterDao().setActiveCharacter(id)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="960"
|
||||||
|
android:viewportHeight="960">
|
||||||
|
<path
|
||||||
|
android:pathData="m300.57,506.26c0,-5.08 0.1,-11.73 0.29,-19.95 0.39,-8.21 0.68,-15.45 0.88,-21.71 -0.98,1.17 -3.13,3.32 -6.45,6.45 -3.13,2.93 -6.06,5.57 -8.8,7.92l-24.05,19.36L241.02,471.65 308.49,417.97l36.37,0l0,209.44L300.57,627.41Z"
|
||||||
|
android:strokeWidth="0"
|
||||||
|
android:fillColor="#000000"/>
|
||||||
|
<path
|
||||||
|
android:pathData="m518.57,522.69q0,33.73 -7.33,57.79 -7.04,24.05 -23.17,36.96 -15.84,12.91 -42.83,12.91 -37.84,0 -55.44,-28.45 -17.6,-28.75 -17.6,-79.2 0,-34.03 7.04,-58.08 7.04,-24.05 23.17,-36.96 16.13,-12.91 42.83,-12.91 37.55,0 55.44,28.45 17.89,28.45 17.89,79.49zM416.2,522.69q0,35.79 6.16,53.97 6.16,17.89 22.88,17.89 16.43,0 22.88,-17.89 6.45,-17.89 6.45,-53.97 0,-35.79 -6.45,-53.97 -6.45,-18.19 -22.88,-18.19 -16.72,0 -22.88,18.19 -6.16,18.19 -6.16,53.97z"
|
||||||
|
android:strokeWidth="0"
|
||||||
|
android:fillColor="#000000"/>
|
||||||
|
<path
|
||||||
|
android:pathData="m600.71,449.94q0,11.73 -0.88,22.59 -0.59,10.85 -1.17,15.25l2.35,0q7.63,-12.32 19.65,-17.89 12.03,-5.57 26.69,-5.57 26.11,0 41.65,14.08 15.84,13.79 15.84,44.59l0,104.43l-43.71,0l0,-93.57q0,-34.61 -25.81,-34.61 -19.65,0 -27.28,13.79 -7.33,13.49 -7.33,39.01l0,75.39l-43.71,0l0,-222.93l43.71,0z"
|
||||||
|
android:strokeWidth="0"
|
||||||
|
android:fillColor="#000000"/>
|
||||||
|
<path
|
||||||
|
android:pathData="m360,120v-80h240v80zM480,880c-49.33,0 -95.83,-9.5 -139.5,-28.5 -43.67,-19 -81.83,-44.83 -114.5,-77.5 -32.67,-32.67 -58.5,-70.83 -77.5,-114.5 -19,-43.67 -28.5,-90.17 -28.5,-139.5 0,-49.33 9.5,-95.83 28.5,-139.5 19,-43.67 44.83,-81.83 77.5,-114.5 32.67,-32.67 70.83,-58.5 114.5,-77.5 43.67,-19 90.17,-28.5 139.5,-28.5 41.33,0 81,6.67 119,20 38,13.33 73.67,32.67 107,58l56,-56 56,56 -56,56c25.33,33.33 44.67,69 58,107 13.33,38 20,77.67 20,119 0,49.33 -9.5,95.83 -28.5,139.5 -19,43.67 -44.83,81.83 -77.5,114.5 -32.67,32.67 -70.83,58.5 -114.5,77.5 -43.67,19 -90.17,28.5 -139.5,28.5zM480,800c77.33,0 143.33,-27.33 198,-82 54.67,-54.67 82,-120.67 82,-198 0,-77.33 -27.33,-143.33 -82,-198 -54.67,-54.67 -120.67,-82 -198,-82 -77.33,0 -143.33,27.33 -198,82 -54.67,54.67 -82,120.67 -82,198 0,77.33 27.33,143.33 82,198 54.67,54.67 120.67,82 198,82z"
|
||||||
|
android:fillColor="#000000"/>
|
||||||
|
</vector>
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="960"
|
||||||
|
android:viewportHeight="960">
|
||||||
|
<path
|
||||||
|
android:pathData="m360,120v-80h240v80zM480,880c-49.33,0 -95.83,-9.5 -139.5,-28.5 -43.67,-19 -81.83,-44.83 -114.5,-77.5 -32.67,-32.67 -58.5,-70.83 -77.5,-114.5 -19,-43.67 -28.5,-90.17 -28.5,-139.5 0,-49.33 9.5,-95.83 28.5,-139.5 19,-43.67 44.83,-81.83 77.5,-114.5 32.67,-32.67 70.83,-58.5 114.5,-77.5 43.67,-19 90.17,-28.5 139.5,-28.5 41.33,0 81,6.67 119,20 38,13.33 73.67,32.67 107,58l56,-56 56,56 -56,56c25.33,33.33 44.67,69 58,107 13.33,38 20,77.67 20,119 0,49.33 -9.5,95.83 -28.5,139.5 -19,43.67 -44.83,81.83 -77.5,114.5 -32.67,32.67 -70.83,58.5 -114.5,77.5 -43.67,19 -90.17,28.5 -139.5,28.5zM480,800c77.33,0 143.33,-27.33 198,-82 54.67,-54.67 82,-120.67 82,-198 0,-77.33 -27.33,-143.33 -82,-198 -54.67,-54.67 -120.67,-82 -198,-82 -77.33,0 -143.33,27.33 -198,82 -54.67,54.67 -82,120.67 -82,198 0,77.33 27.33,143.33 82,198 54.67,54.67 120.67,82 198,82z"
|
||||||
|
android:fillColor="#000000"/>
|
||||||
|
<path
|
||||||
|
android:pathData="m287.83,506.91q0,-7.63 0.29,-19.95 0.59,-12.32 0.88,-21.71 -1.47,1.76 -6.45,6.45 -4.69,4.4 -8.8,7.92L249.7,498.99 228.29,472.3 295.75,418.62l36.37,0l0,209.44l-44.29,0z"
|
||||||
|
android:strokeWidth="0"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:strokeColor="#00000000"/>
|
||||||
|
<path
|
||||||
|
android:pathData="m386.69,628.06l0,-30.8l52.51,-53.09q15.84,-16.43 25.52,-27.28 9.68,-11.15 14.08,-19.95 4.4,-9.09 4.4,-19.36 0,-12.61 -7.04,-18.77 -6.75,-6.16 -18.48,-6.16 -12.03,0 -23.47,5.57Q422.77,463.79 410.15,474.06L386.1,445.61Q395.19,437.69 405.17,430.94 415.43,424.19 428.63,420.09 442.13,415.69 460.9,415.69q20.53,0 35.2,7.63 14.96,7.33 22.88,20.24 8.21,12.61 8.21,28.75 0,17.31 -7.04,31.68 -6.75,14.37 -19.95,28.45 -12.91,14.08 -31.39,31.09l-26.99,25.23l0,2.05l91.23,0l0,37.25z"
|
||||||
|
android:strokeWidth="0"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:strokeColor="#00000000"/>
|
||||||
|
<path
|
||||||
|
android:pathData="m606.98,450.59c0,7.82 -0.29,15.35 -0.88,22.59 -0.39,7.24 -0.78,12.32 -1.17,15.25L607.27,488.43C612.36,480.22 618.91,474.25 626.93,470.54 634.94,466.82 643.84,464.97 653.62,464.97c17.4,0 31.29,4.69 41.65,14.08 10.56,9.19 15.84,24.05 15.84,44.59L711.11,628.06L667.41,628.06l0,-93.57c0,-23.08 -8.6,-34.61 -25.81,-34.61 -13.1,0 -22.2,4.6 -27.28,13.79 -4.89,9 -7.33,22 -7.33,39.01L606.98,628.06L563.27,628.06L563.27,413.88l43.71,0z"
|
||||||
|
android:strokeWidth="0"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:strokeColor="#00000000"/>
|
||||||
|
</vector>
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="960"
|
||||||
|
android:viewportHeight="960">
|
||||||
|
<path
|
||||||
|
android:pathData="m360,120v-80h240v80zM480,880c-49.33,0 -95.83,-9.5 -139.5,-28.5 -43.67,-19 -81.83,-44.83 -114.5,-77.5 -32.67,-32.67 -58.5,-70.83 -77.5,-114.5 -19,-43.67 -28.5,-90.17 -28.5,-139.5 0,-49.33 9.5,-95.83 28.5,-139.5 19,-43.67 44.83,-81.83 77.5,-114.5 32.67,-32.67 70.83,-58.5 114.5,-77.5 43.67,-19 90.17,-28.5 139.5,-28.5 41.33,0 81,6.67 119,20 38,13.33 73.67,32.67 107,58l56,-56 56,56 -56,56c25.33,33.33 44.67,69 58,107 13.33,38 20,77.67 20,119 0,49.33 -9.5,95.83 -28.5,139.5 -19,43.67 -44.83,81.83 -77.5,114.5 -32.67,32.67 -70.83,58.5 -114.5,77.5 -43.67,19 -90.17,28.5 -139.5,28.5zM480,800c77.33,0 143.33,-27.33 198,-82 54.67,-54.67 82,-120.67 82,-198 0,-77.33 -27.33,-143.33 -82,-198 -54.67,-54.67 -120.67,-82 -198,-82 -77.33,0 -143.33,27.33 -198,82 -54.67,54.67 -82,120.67 -82,198 0,77.33 27.33,143.33 82,198 54.67,54.67 120.67,82 198,82z"
|
||||||
|
android:fillColor="#000000"/>
|
||||||
|
<path
|
||||||
|
android:pathData="m337.48,503.79q0,-11.09 0.43,-29.01 0.85,-17.92 1.28,-31.57 -2.13,2.56 -9.39,9.39 -6.83,6.4 -12.8,11.52L282.01,492.27 250.87,453.44 349,375.36L401.91,375.36L401.91,680L337.48,680Z"
|
||||||
|
android:fillColor="#000000"/>
|
||||||
|
<path
|
||||||
|
android:pathData="m594.76,485.44q27.73,0 49.49,10.67 21.76,10.67 34.13,31.15 12.8,20.48 12.8,50.77 0,49.49 -30.72,78.08Q629.75,684.27 569.59,684.27 545.7,684.27 524.36,680 503.46,675.73 487.67,667.63L487.67,612.16q15.79,8.11 37.97,14.08 22.19,5.55 41.81,5.55 28.59,0 43.52,-11.52 15.36,-11.95 15.36,-36.69 0,-46.08 -61.01,-46.08 -11.95,0 -24.75,2.56 -12.8,2.13 -21.33,4.27l-25.6,-13.65 11.52,-155.31l165.12,0l0,54.61L561.48,429.97l-5.55,59.73q7.25,-1.28 15.36,-2.56 8.53,-1.71 23.47,-1.71z"
|
||||||
|
android:fillColor="#000000"/>
|
||||||
|
</vector>
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="960"
|
||||||
|
android:viewportHeight="960">
|
||||||
|
<path
|
||||||
|
android:pathData="m360,120v-80h240v80zM480,880c-49.33,0 -95.83,-9.5 -139.5,-28.5 -43.67,-19 -81.83,-44.83 -114.5,-77.5 -32.67,-32.67 -58.5,-70.83 -77.5,-114.5 -19,-43.67 -28.5,-90.17 -28.5,-139.5 0,-49.33 9.5,-95.83 28.5,-139.5 19,-43.67 44.83,-81.83 77.5,-114.5 32.67,-32.67 70.83,-58.5 114.5,-77.5 43.67,-19 90.17,-28.5 139.5,-28.5 41.33,0 81,6.67 119,20 38,13.33 73.67,32.67 107,58l56,-56 56,56 -56,56c25.33,33.33 44.67,69 58,107 13.33,38 20,77.67 20,119 0,49.33 -9.5,95.83 -28.5,139.5 -19,43.67 -44.83,81.83 -77.5,114.5 -32.67,32.67 -70.83,58.5 -114.5,77.5 -43.67,19 -90.17,28.5 -139.5,28.5zM480,800c77.33,0 143.33,-27.33 198,-82 54.67,-54.67 82,-120.67 82,-198 0,-77.33 -27.33,-143.33 -82,-198 -54.67,-54.67 -120.67,-82 -198,-82 -77.33,0 -143.33,27.33 -198,82 -54.67,54.67 -82,120.67 -82,198 0,77.33 27.33,143.33 82,198 54.67,54.67 120.67,82 198,82z"
|
||||||
|
android:fillColor="#000000"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M239.19,624.91L239.19,594.11l52.51,-53.09q15.84,-16.43 25.52,-27.28 9.68,-11.15 14.08,-19.95 4.4,-9.09 4.4,-19.36 0,-12.61 -7.04,-18.77 -6.75,-6.16 -18.48,-6.16 -12.03,0 -23.47,5.57 -11.44,5.57 -24.05,15.84L238.6,442.46Q247.7,434.54 257.67,427.79 267.94,421.05 281.14,416.94 294.63,412.54 313.4,412.54q20.53,0 35.2,7.63 14.96,7.33 22.88,20.24 8.21,12.61 8.21,28.75 0,17.31 -7.04,31.68 -6.75,14.37 -19.95,28.45 -12.91,14.08 -31.39,31.09l-26.99,25.23l0,2.05l91.23,0L385.56,624.91Z"
|
||||||
|
android:strokeWidth="0"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:strokeColor="#00000000"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M487.35,624.91L487.35,581.5L397.88,581.5L397.88,550.7L489.7,415.47L530.47,415.47L530.47,547.18L555.7,547.18L555.7,581.5L530.47,581.5L530.47,624.91ZM487.35,497.02q0,-10.27 0.29,-17.01 0.59,-6.75 0.88,-12.03l-1.17,0Q482.36,477.95 474.74,489.39L436.9,547.18L487.35,547.18Z"
|
||||||
|
android:strokeWidth="0"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:strokeColor="#00000000"/>
|
||||||
|
<path
|
||||||
|
android:pathData="m609.48,447.47c0,7.82 -0.29,15.35 -0.88,22.59 -0.39,7.24 -0.78,12.32 -1.17,15.25L609.77,485.31C614.86,477.09 621.41,471.13 629.43,467.41 637.44,463.7 646.34,461.84 656.12,461.84c17.4,0 31.29,4.69 41.65,14.08 10.56,9.19 15.84,24.05 15.84,44.59l0,104.43L669.91,624.93l0,-93.57c0,-23.08 -8.6,-34.61 -25.81,-34.61 -13.1,0 -22.2,4.6 -27.28,13.79 -4.89,9 -7.33,22 -7.33,39.01l0,75.39L565.77,624.93L565.77,414.5l43.71,0z"
|
||||||
|
android:strokeWidth="0"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:strokeColor="#00000000"/>
|
||||||
|
</vector>
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="960"
|
||||||
|
android:viewportHeight="960">
|
||||||
|
<path
|
||||||
|
android:pathData="m360,120v-80h240v80zM480,880c-49.33,0 -95.83,-9.5 -139.5,-28.5 -43.67,-19 -81.83,-44.83 -114.5,-77.5 -32.67,-32.67 -58.5,-70.83 -77.5,-114.5 -19,-43.67 -28.5,-90.17 -28.5,-139.5 0,-49.33 9.5,-95.83 28.5,-139.5 19,-43.67 44.83,-81.83 77.5,-114.5 32.67,-32.67 70.83,-58.5 114.5,-77.5 43.67,-19 90.17,-28.5 139.5,-28.5 41.33,0 81,6.67 119,20 38,13.33 73.67,32.67 107,58l56,-56 56,56 -56,56c25.33,33.33 44.67,69 58,107 13.33,38 20,77.67 20,119 0,49.33 -9.5,95.83 -28.5,139.5 -19,43.67 -44.83,81.83 -77.5,114.5 -32.67,32.67 -70.83,58.5 -114.5,77.5 -43.67,19 -90.17,28.5 -139.5,28.5zM480,800c77.33,0 143.33,-27.33 198,-82 54.67,-54.67 82,-120.67 82,-198 0,-77.33 -27.33,-143.33 -82,-198 -54.67,-54.67 -120.67,-82 -198,-82 -77.33,0 -143.33,27.33 -198,82 -54.67,54.67 -82,120.67 -82,198 0,77.33 27.33,143.33 82,198 54.67,54.67 120.67,82 198,82z"
|
||||||
|
android:fillColor="#000000"/>
|
||||||
|
<path
|
||||||
|
android:pathData="m453.77,437.38q0,31.57 -19.2,50.35 -18.77,18.77 -46.51,25.6l0,1.28q36.69,4.27 55.47,22.19 19.2,17.92 19.2,48.21 0,26.45 -13.23,47.79Q436.71,653.7 409.4,666.07 382.52,678.02 339.85,678.02q-49.49,0 -87.89,-16.64L251.96,606.76q19.63,9.81 40.96,14.93 21.76,5.12 40.11,5.12 34.56,0 48.21,-11.95 14.08,-11.95 14.08,-33.71 0,-12.8 -6.4,-21.33Q382.52,550.87 366.31,546.6 350.52,541.91 321.93,541.91L298.89,541.91l0,-49.49l23.47,0q28.16,0 42.67,-5.12 14.93,-5.55 20.05,-14.51 5.55,-9.39 5.55,-21.33 0,-16.21 -10.24,-25.17 -9.81,-9.39 -33.28,-9.39 -21.76,0 -37.97,7.68 -15.79,7.25 -26.88,14.51L252.39,394.71q17.92,-12.8 41.81,-21.33 24.32,-8.53 57.6,-8.53 46.93,0 74.24,19.2 27.73,18.77 27.73,53.33z"
|
||||||
|
android:fillColor="#000000"/>
|
||||||
|
<path
|
||||||
|
android:pathData="m715.32,521.43q0,49.07 -10.67,84.05 -10.24,34.99 -33.71,53.76Q647.91,678.02 608.66,678.02q-55.04,0 -80.64,-41.39 -25.6,-41.81 -25.6,-115.2 0,-49.49 10.24,-84.48 10.24,-34.99 33.71,-53.76 23.47,-18.77 62.29,-18.77 54.61,0 80.64,41.39 26.03,41.39 26.03,115.63zM566.42,521.43q0,52.05 8.96,78.51 8.96,26.03 33.28,26.03 23.89,0 33.28,-26.03 9.39,-26.03 9.39,-78.51 0,-52.05 -9.39,-78.51 -9.39,-26.45 -33.28,-26.45 -24.32,0 -33.28,26.45 -8.96,26.45 -8.96,78.51z"
|
||||||
|
android:fillColor="#000000"/>
|
||||||
|
</vector>
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="960"
|
||||||
|
android:viewportHeight="960">
|
||||||
|
<path
|
||||||
|
android:pathData="m381.45,481.69q20.8,0 37.12,8 16.32,8 25.6,23.36 9.6,15.36 9.6,38.08 0,37.12 -23.04,58.56 -23.04,21.12 -68.16,21.12 -17.92,0 -33.92,-3.2 -15.68,-3.2 -27.52,-9.28v-41.6q11.84,6.08 28.48,10.56 16.64,4.16 31.36,4.16 21.44,0 32.64,-8.64 11.52,-8.96 11.52,-27.52 0,-34.56 -45.76,-34.56 -8.96,0 -18.56,1.92 -9.6,1.6 -16,3.2l-19.2,-10.24 8.64,-116.48h123.84v40.96h-81.6l-4.16,44.8q5.44,-0.96 11.52,-1.92 6.4,-1.28 17.6,-1.28z"
|
||||||
|
android:strokeWidth="0"
|
||||||
|
android:fillColor="#000000"/>
|
||||||
|
<path
|
||||||
|
android:pathData="m540.81,434.01c0,8.53 -0.32,16.75 -0.96,24.64 -0.43,7.89 -0.85,13.44 -1.28,16.64h2.56c5.55,-8.96 12.69,-15.47 21.44,-19.52 8.75,-4.05 18.45,-6.08 29.12,-6.08 18.99,0 34.13,5.12 45.44,15.36 11.52,10.03 17.28,26.24 17.28,48.64v113.92h-47.68v-102.08c0,-25.17 -9.39,-37.76 -28.16,-37.76 -14.29,0 -24.21,5.01 -29.76,15.04 -5.33,9.81 -8,24 -8,42.56v82.24h-47.68v-228.82h47.68z"
|
||||||
|
android:strokeWidth="0"
|
||||||
|
android:fillColor="#000000"/>
|
||||||
|
<path
|
||||||
|
android:pathData="m360,120v-80h240v80zM480,880c-49.33,0 -95.83,-9.5 -139.5,-28.5 -43.67,-19 -81.83,-44.83 -114.5,-77.5 -32.67,-32.67 -58.5,-70.83 -77.5,-114.5 -19,-43.67 -28.5,-90.17 -28.5,-139.5 0,-49.33 9.5,-95.83 28.5,-139.5 19,-43.67 44.83,-81.83 77.5,-114.5 32.67,-32.67 70.83,-58.5 114.5,-77.5 43.67,-19 90.17,-28.5 139.5,-28.5 41.33,0 81,6.67 119,20 38,13.33 73.67,32.67 107,58l56,-56 56,56 -56,56c25.33,33.33 44.67,69 58,107 13.33,38 20,77.67 20,119 0,49.33 -9.5,95.83 -28.5,139.5 -19,43.67 -44.83,81.83 -77.5,114.5 -32.67,32.67 -70.83,58.5 -114.5,77.5 -43.67,19 -90.17,28.5 -139.5,28.5zM480,800c77.33,0 143.33,-27.33 198,-82 54.67,-54.67 82,-120.67 82,-198 0,-77.33 -27.33,-143.33 -82,-198 -54.67,-54.67 -120.67,-82 -198,-82 -77.33,0 -143.33,27.33 -198,82 -54.67,54.67 -82,120.67 -82,198 0,77.33 27.33,143.33 82,198 54.67,54.67 120.67,82 198,82z"
|
||||||
|
android:fillColor="#000000"/>
|
||||||
|
</vector>
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="960"
|
||||||
|
android:viewportHeight="960">
|
||||||
|
<path
|
||||||
|
android:pathData="m360,120v-80h240v80zM480,880c-49.33,0 -95.83,-9.5 -139.5,-28.5 -43.67,-19 -81.83,-44.83 -114.5,-77.5 -32.67,-32.67 -58.5,-70.83 -77.5,-114.5 -19,-43.67 -28.5,-90.17 -28.5,-139.5 0,-49.33 9.5,-95.83 28.5,-139.5 19,-43.67 44.83,-81.83 77.5,-114.5 32.67,-32.67 70.83,-58.5 114.5,-77.5 43.67,-19 90.17,-28.5 139.5,-28.5 41.33,0 81,6.67 119,20 38,13.33 73.67,32.67 107,58l56,-56 56,56 -56,56c25.33,33.33 44.67,69 58,107 13.33,38 20,77.67 20,119 0,49.33 -9.5,95.83 -28.5,139.5 -19,43.67 -44.83,81.83 -77.5,114.5 -32.67,32.67 -70.83,58.5 -114.5,77.5 -43.67,19 -90.17,28.5 -139.5,28.5zM480,800c77.33,0 143.33,-27.33 198,-82 54.67,-54.67 82,-120.67 82,-198 0,-77.33 -27.33,-143.33 -82,-198 -54.67,-54.67 -120.67,-82 -198,-82 -77.33,0 -143.33,27.33 -198,82 -54.67,54.67 -82,120.67 -82,198 0,77.33 27.33,143.33 82,198 54.67,54.67 120.67,82 198,82z"
|
||||||
|
android:fillColor="#000000"/>
|
||||||
|
<path
|
||||||
|
android:pathData="m240.86,541.97q0,-26.45 3.84,-52.05 3.84,-25.6 13.23,-48.21 9.81,-23.04 26.88,-40.53 17.49,-17.92 43.95,-27.73 26.88,-10.24 64.85,-10.24 8.96,0 20.91,0.85 11.95,0.43 20.05,2.13L434.57,417.81q-8.11,-2.13 -17.92,-2.99 -9.39,-1.28 -18.77,-1.28 -37.97,0 -58.88,11.95 -20.48,11.95 -29.01,33.71 -8.53,21.33 -9.81,49.49l2.56,0Q311.26,493.76 327.05,483.52 343.26,473.28 368.86,473.28q40.11,0 63.57,25.17 23.47,25.17 23.47,71.25 0,49.49 -28.16,77.65Q400.01,675.52 352.22,675.52q-31.15,0 -56.32,-14.08Q270.73,646.93 255.79,617.49 240.86,587.62 240.86,541.97ZM350.94,623.89q18.77,0 30.72,-12.8 11.95,-13.23 11.95,-40.53 0,-22.19 -10.24,-34.99 -10.24,-12.8 -31.15,-12.8 -14.08,0 -24.75,6.4 -10.67,5.97 -16.64,15.79 -5.97,9.81 -5.97,20.05 0,14.08 5.12,27.73 5.12,13.23 15.36,22.19 10.67,8.96 25.6,8.96z"
|
||||||
|
android:fillColor="#000000"/>
|
||||||
|
<path
|
||||||
|
android:pathData="m710.62,518.93q0,49.07 -10.67,84.05 -10.24,34.99 -33.71,53.76Q643.21,675.52 603.96,675.52q-55.04,0 -80.64,-41.39 -25.6,-41.81 -25.6,-115.2 0,-49.49 10.24,-84.48 10.24,-34.99 33.71,-53.76 23.47,-18.77 62.29,-18.77 54.61,0 80.64,41.39 26.03,41.39 26.03,115.63zM561.71,518.93q0,52.05 8.96,78.51 8.96,26.03 33.28,26.03 23.89,0 33.28,-26.03 9.39,-26.03 9.39,-78.51 0,-52.05 -9.39,-78.51 -9.39,-26.45 -33.28,-26.45 -24.32,0 -33.28,26.45 -8.96,26.45 -8.96,78.51z"
|
||||||
|
android:fillColor="#000000"/>
|
||||||
|
</vector>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="960"
|
||||||
|
android:viewportHeight="960">
|
||||||
|
<path
|
||||||
|
android:pathData="m296,736 l-56,-56 240,-240 240,240 -56,56 -184,-183 -184,183ZM296,496 L240,440 480,200 720,440 664,496 480,313 296,496Z"
|
||||||
|
android:fillColor="#000000"/>
|
||||||
|
</vector>
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="960"
|
||||||
|
android:viewportHeight="960">
|
||||||
|
<path
|
||||||
|
android:pathData="m664,229.06 l56,56 -240,240 -240,-240 56,-56 184,183z"
|
||||||
|
android:fillColor="#000000"/>
|
||||||
|
<path
|
||||||
|
android:pathData="m664,434.94 l56,56 -240,240 -240,-240 56,-56 184,183z"
|
||||||
|
android:fillColor="#000000"/>
|
||||||
|
</vector>
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="960"
|
||||||
|
android:viewportHeight="960">
|
||||||
|
<path
|
||||||
|
android:pathData="m296,730.94 l-56,-56 240,-240 240,240 -56,56 -184,-183z"
|
||||||
|
android:fillColor="#000000"/>
|
||||||
|
<path
|
||||||
|
android:pathData="m296,525.06 l-56,-56 240,-240 240,240 -56,56 -184,-183z"
|
||||||
|
android:fillColor="#000000"/>
|
||||||
|
</vector>
|
||||||
@@ -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="M40,840v-160l80,-80v-240l-80,-80v-160h80v80h80v-80h80v80h80v-80h80v160l-80,80v40h240v-40l-80,-80v-160h80v80h80v-80h80v80h80v-80h80v160l-80,80v240l80,80v160L560,840v-120q0,-33 -23.5,-56.5T480,640q-33,0 -56.5,23.5T400,720v120L40,840ZM120,760h200v-40q0,-66 47,-113t113,-47q66,0 113,47t47,113v40h200v-47l-80,-80v-306l47,-47L633,280l47,47v153L280,480v-153l47,-47L153,280l47,47v306l-80,80v47ZM480,520Z"
|
||||||
|
android:fillColor="#000000"/>
|
||||||
|
</vector>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="960"
|
||||||
|
android:viewportHeight="960">
|
||||||
|
<path
|
||||||
|
android:pathData="M440,838q-121,-15 -200.5,-105.5T160,520q0,-66 26,-126.5T260,288l57,57q-38,34 -57.5,79T240,520q0,88 56,155.5T440,758v80ZM520,838v-80q87,-16 143.5,-83T720,520q0,-100 -70,-170t-170,-70h-3l44,44 -56,56 -140,-140 140,-140 56,56 -44,44h3q134,0 227,93t93,227q0,121 -79.5,211.5T520,838Z"
|
||||||
|
android:fillColor="#000000"/>
|
||||||
|
</vector>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="960"
|
||||||
|
android:viewportHeight="960">
|
||||||
|
<path
|
||||||
|
android:pathData="M480,880q-139,-35 -229.5,-159.5T160,444v-244l320,-120 320,120v244q0,152 -90.5,276.5T480,880ZM480,796q104,-33 172,-132t68,-220v-189l-240,-90 -240,90v189q0,121 68,220t172,132ZM480,480Z"
|
||||||
|
android:fillColor="#000000"/>
|
||||||
|
</vector>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="960"
|
||||||
|
android:viewportHeight="960">
|
||||||
|
<path
|
||||||
|
android:pathData="m664,332 l56,56 -240,240 -240,-240 56,-56 184,183z"
|
||||||
|
android:fillColor="#000000"/>
|
||||||
|
</vector>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="960"
|
||||||
|
android:viewportHeight="960">
|
||||||
|
<path
|
||||||
|
android:pathData="m296,628 l-56,-56 240,-240 240,240 -56,56 -184,-183z"
|
||||||
|
android:fillColor="#000000"/>
|
||||||
|
</vector>
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="960"
|
||||||
|
android:viewportHeight="960">
|
||||||
|
<path
|
||||||
|
android:pathData="m664,332 l56,56 -240,240 -240,-240 56,-56 184,183z"
|
||||||
|
android:fillColor="#000000"/>
|
||||||
|
<path
|
||||||
|
android:pathData="m664,537.89 l56,56 -240,240 -240,-240 56,-56 184,183z"
|
||||||
|
android:fillColor="#000000"/>
|
||||||
|
<path
|
||||||
|
android:pathData="m664,126.11 l56,56 -240,240 -240,-240 56,-56 184,183z"
|
||||||
|
android:fillColor="#000000"/>
|
||||||
|
</vector>
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="960"
|
||||||
|
android:viewportHeight="960">
|
||||||
|
<path
|
||||||
|
android:pathData="m296,628 l-56,-56 240,-240 240,240 -56,56 -184,-183z"
|
||||||
|
android:fillColor="#000000"/>
|
||||||
|
<path
|
||||||
|
android:pathData="m296,422.11 l-56,-56 240,-240 240,240 -56,56 -184,-183z"
|
||||||
|
android:fillColor="#000000"/>
|
||||||
|
<path
|
||||||
|
android:pathData="m296,833.89 l-56,-56 240,-240 240,240 -56,56 -184,-183z"
|
||||||
|
android:fillColor="#000000"/>
|
||||||
|
</vector>
|
||||||
@@ -1,18 +1,18 @@
|
|||||||
[versions]
|
[versions]
|
||||||
agp = "8.7.3"
|
agp = "8.7.3"
|
||||||
datastore = "1.1.1"
|
datastore = "1.1.2"
|
||||||
kotlin = "2.0.0"
|
kotlin = "2.0.0"
|
||||||
coreKtx = "1.15.0"
|
coreKtx = "1.15.0"
|
||||||
junit = "4.13.2"
|
junit = "4.13.2"
|
||||||
junitVersion = "1.2.1"
|
junitVersion = "1.2.1"
|
||||||
espressoCore = "3.6.1"
|
espressoCore = "3.6.1"
|
||||||
lifecycleRuntimeKtx = "2.8.7"
|
lifecycleRuntimeKtx = "2.8.7"
|
||||||
activityCompose = "1.9.3"
|
activityCompose = "1.10.0"
|
||||||
composeBom = "2024.04.01"
|
composeBom = "2025.01.00"
|
||||||
protobufGradlePlugin = "0.9.4"
|
protobufGradlePlugin = "0.9.4"
|
||||||
protobufJavalite = "4.27.0"
|
protobufJavalite = "4.27.0"
|
||||||
roomRuntime = "2.6.1"
|
roomRuntime = "2.6.1"
|
||||||
vbNfcReader = "0.1.0"
|
vbNfcReader = "0.2.0-SNAPSHOT"
|
||||||
dimReader = "2.1.0"
|
dimReader = "2.1.0"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
|
|||||||
Reference in New Issue
Block a user