mirror of
https://github.com/nacabaro/vbhelper.git
synced 2026-10-01 07:31:37 +00:00
Compare commits
93
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0b3ce486cb | ||
|
|
7160eb792a | ||
|
|
cb9fe8e716 | ||
|
|
540766eefb | ||
|
|
af2c852bac | ||
|
|
8dc5bbbdde | ||
|
|
aac05a3b1c | ||
|
|
b827fdccbe | ||
|
|
2586190e5c | ||
|
|
1a6753d2aa | ||
|
|
f8ec899335 | ||
|
|
b95edfa2a1 | ||
|
|
1a570170ca | ||
|
|
1566876106 | ||
|
|
019f07d827 | ||
|
|
4474e6689d | ||
|
|
fb09004d13 | ||
|
|
dd1a62d184 | ||
|
|
f96f00e05a | ||
|
|
3c17e93d0f | ||
|
|
f8ce81e932 | ||
|
|
7bb7693876 | ||
|
|
2b33042fe0 | ||
|
|
4d8dcde26e | ||
|
|
0b1eed7da9 | ||
|
|
36c3e25fd3 | ||
|
|
ac05dfc541 | ||
|
|
220a61a553 | ||
|
|
d847f600f1 | ||
|
|
f7a4039d66 | ||
|
|
1eed6a4c17 | ||
|
|
1a09e40d1d | ||
|
|
13051145b7 | ||
|
|
1a150ce7ce | ||
|
|
39ba48d867 | ||
|
|
3f324ca141 | ||
|
|
1e28a8c249 | ||
|
|
d46769b0cb | ||
|
|
fce05870c5 | ||
|
|
9f5f806786 | ||
|
|
d749af0a9c | ||
|
|
076f6bfacf | ||
|
|
a4f3b701e5 | ||
|
|
da134247b1 | ||
|
|
f7b3b7256a | ||
|
|
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 | ||
|
|
88163684ca | ||
|
|
586d6c01ef | ||
|
|
5d996edc1e | ||
|
|
074ced1082 | ||
|
|
5a1d52aa1d | ||
|
|
98bd74c544 | ||
|
|
ce1cf3eddb | ||
|
|
0bd745ac86 | ||
|
|
0d999925b5 | ||
|
|
dd893a08da | ||
|
|
c5e8236e19 | ||
|
|
cac5198488 | ||
|
|
7f22650601 | ||
|
|
1088e5259a | ||
|
|
eabf8770cc | ||
|
|
da5e6c6628 | ||
|
|
4c0e2fafd0 | ||
|
|
4529925906 | ||
|
|
c456d455ef | ||
|
|
9871f0420f | ||
|
|
f4974c8705 | ||
|
|
5d6e3743f0 | ||
|
|
e4366ce15c |
@@ -6,3 +6,7 @@
|
|||||||
local.properties
|
local.properties
|
||||||
|
|
||||||
app/src/main/res/values/keys.xml
|
app/src/main/res/values/keys.xml
|
||||||
|
|
||||||
|
app/src/test/resources/com/github/nacabaro/vbhelper/source/com.bandai.vitalbraceletarena.apk
|
||||||
|
|
||||||
|
app/src/test/resources/com/github/nacabaro/vbhelper/source/classes.dex
|
||||||
|
|||||||
+27
-1
@@ -3,6 +3,7 @@ plugins {
|
|||||||
alias(libs.plugins.kotlin.android)
|
alias(libs.plugins.kotlin.android)
|
||||||
alias(libs.plugins.kotlin.compose)
|
alias(libs.plugins.kotlin.compose)
|
||||||
id("com.google.devtools.ksp") version "2.0.21-1.0.27"
|
id("com.google.devtools.ksp") version "2.0.21-1.0.27"
|
||||||
|
id("com.google.protobuf")
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
@@ -14,7 +15,7 @@ android {
|
|||||||
minSdk = 28
|
minSdk = 28
|
||||||
targetSdk = 35
|
targetSdk = 35
|
||||||
versionCode = 1
|
versionCode = 1
|
||||||
versionName = "1.0"
|
versionName = "Alpha 0.6.1"
|
||||||
|
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
}
|
}
|
||||||
@@ -38,6 +39,29 @@ android {
|
|||||||
buildFeatures {
|
buildFeatures {
|
||||||
compose = true
|
compose = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lint {
|
||||||
|
baseline = file("lint-baseline.xml")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protobuf {
|
||||||
|
protoc {
|
||||||
|
artifact = "com.google.protobuf:protoc:4.27.0"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generates the java Protobuf-lite code for the Protobufs in this project. See
|
||||||
|
// https://github.com/google/protobuf-gradle-plugin#customizing-protobuf-compilation
|
||||||
|
// for more information.
|
||||||
|
generateProtoTasks {
|
||||||
|
all().forEach { task ->
|
||||||
|
task.builtins {
|
||||||
|
create("java") {
|
||||||
|
option("lite")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
@@ -51,6 +75,7 @@ dependencies {
|
|||||||
implementation(libs.androidx.lifecycle.runtime.ktx)
|
implementation(libs.androidx.lifecycle.runtime.ktx)
|
||||||
implementation(libs.androidx.activity.compose)
|
implementation(libs.androidx.activity.compose)
|
||||||
implementation(platform(libs.androidx.compose.bom))
|
implementation(platform(libs.androidx.compose.bom))
|
||||||
|
implementation(libs.androidx.datastore)
|
||||||
implementation(libs.androidx.ui)
|
implementation(libs.androidx.ui)
|
||||||
implementation(libs.androidx.ui.graphics)
|
implementation(libs.androidx.ui.graphics)
|
||||||
implementation(libs.androidx.ui.tooling.preview)
|
implementation(libs.androidx.ui.tooling.preview)
|
||||||
@@ -64,5 +89,6 @@ dependencies {
|
|||||||
debugImplementation(libs.androidx.ui.test.manifest)
|
debugImplementation(libs.androidx.ui.test.manifest)
|
||||||
implementation("androidx.navigation:navigation-compose:2.7.0")
|
implementation("androidx.navigation:navigation-compose:2.7.0")
|
||||||
implementation("com.google.android.material:material:1.2.0")
|
implementation("com.google.android.material:material:1.2.0")
|
||||||
|
implementation(libs.protobuf.javalite)
|
||||||
implementation("androidx.compose.material:material")
|
implementation("androidx.compose.material:material")
|
||||||
}
|
}
|
||||||
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
@@ -0,0 +1,16 @@
|
|||||||
|
package com.github.nacabaro.vbhelper
|
||||||
|
|
||||||
|
interface ActivityLifecycleListener {
|
||||||
|
fun onPause()
|
||||||
|
fun onResume()
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun noOpInstance(): ActivityLifecycleListener {
|
||||||
|
return object: ActivityLifecycleListener {
|
||||||
|
override fun onPause() {}
|
||||||
|
|
||||||
|
override fun onResume() {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,361 +1,115 @@
|
|||||||
package com.github.nacabaro.vbhelper
|
package com.github.nacabaro.vbhelper
|
||||||
|
|
||||||
import android.content.Intent
|
|
||||||
import android.nfc.NfcAdapter
|
|
||||||
import android.nfc.Tag
|
|
||||||
import android.nfc.tech.NfcA
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.provider.Settings
|
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.compose.runtime.getValue
|
|
||||||
import androidx.compose.runtime.mutableStateOf
|
|
||||||
import androidx.compose.runtime.remember
|
|
||||||
import androidx.compose.runtime.setValue
|
|
||||||
import androidx.lifecycle.lifecycleScope
|
|
||||||
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.CryptographicTransformer
|
|
||||||
import com.github.cfogrady.vbnfc.TagCommunicator
|
|
||||||
import com.github.cfogrady.vbnfc.be.BENfcCharacter
|
|
||||||
import com.github.cfogrady.vbnfc.data.DeviceType
|
|
||||||
import com.github.cfogrady.vbnfc.data.NfcCharacter
|
|
||||||
import com.github.nacabaro.vbhelper.di.VBHelper
|
import com.github.nacabaro.vbhelper.di.VBHelper
|
||||||
import com.github.nacabaro.vbhelper.domain.Dim
|
import com.github.nacabaro.vbhelper.navigation.AppNavigationHandlers
|
||||||
import com.github.nacabaro.vbhelper.domain.Sprites
|
import com.github.nacabaro.vbhelper.screens.homeScreens.HomeScreenControllerImpl
|
||||||
import com.github.nacabaro.vbhelper.domain.Character
|
import com.github.nacabaro.vbhelper.screens.itemsScreen.ItemsScreenControllerImpl
|
||||||
import com.github.nacabaro.vbhelper.domain.device_data.BECharacterData
|
import com.github.nacabaro.vbhelper.screens.scanScreen.ScanScreenControllerImpl
|
||||||
import com.github.nacabaro.vbhelper.domain.device_data.TransformationHistory
|
import com.github.nacabaro.vbhelper.screens.settingsScreen.SettingsScreenControllerImpl
|
||||||
import com.github.nacabaro.vbhelper.domain.device_data.UserCharacter
|
import com.github.nacabaro.vbhelper.screens.adventureScreen.AdventureScreenControllerImpl
|
||||||
|
import com.github.nacabaro.vbhelper.screens.cardScreen.CardScreenControllerImpl
|
||||||
|
import com.github.nacabaro.vbhelper.screens.spriteViewer.SpriteViewerControllerImpl
|
||||||
|
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 kotlinx.coroutines.flow.MutableStateFlow
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
|
|
||||||
class MainActivity : ComponentActivity() {
|
class MainActivity : ComponentActivity() {
|
||||||
private lateinit var nfcAdapter: NfcAdapter
|
private val onActivityLifecycleListeners = HashMap<String, ActivityLifecycleListener>()
|
||||||
private lateinit var deviceToCryptographicTransformers: Map<UShort, CryptographicTransformer>
|
|
||||||
|
|
||||||
private var nfcCharacter = MutableStateFlow<NfcCharacter?>(null)
|
private fun registerActivityLifecycleListener(key: String, activityLifecycleListener: ActivityLifecycleListener) {
|
||||||
|
if( onActivityLifecycleListeners[key] != null) {
|
||||||
private lateinit var activityResultLauncher: ActivityResultLauncher<Intent>
|
throw IllegalStateException("Key is already in use")
|
||||||
|
|
||||||
// EXTRACTED DIRECTLY FROM EXAMPLE APP
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
deviceToCryptographicTransformers = getMapOfCryptographicTransformers()
|
|
||||||
|
|
||||||
registerFileActivityResult()
|
|
||||||
|
|
||||||
val maybeNfcAdapter = NfcAdapter.getDefaultAdapter(this)
|
|
||||||
if (maybeNfcAdapter == null) {
|
|
||||||
Toast.makeText(this, "No NFC on device!", Toast.LENGTH_SHORT).show()
|
|
||||||
finish()
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
nfcAdapter = maybeNfcAdapter
|
onActivityLifecycleListeners[key] = activityLifecycleListener
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun unregisterActivityLifecycleListener(key: String) {
|
||||||
|
onActivityLifecycleListeners.remove(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
val application = applicationContext as VBHelper
|
||||||
|
val scanScreenController = ScanScreenControllerImpl(
|
||||||
|
application.container.dataStoreSecretsRepository.secretsFlow,
|
||||||
|
this,
|
||||||
|
this::registerActivityLifecycleListener,
|
||||||
|
this::unregisterActivityLifecycleListener
|
||||||
|
)
|
||||||
|
val settingsScreenController = SettingsScreenControllerImpl(this)
|
||||||
|
val itemsScreenController = ItemsScreenControllerImpl(this)
|
||||||
|
val adventureScreenController = AdventureScreenControllerImpl(this)
|
||||||
|
val storageScreenController = StorageScreenControllerImpl(this)
|
||||||
|
val homeScreenController = HomeScreenControllerImpl(this)
|
||||||
|
val spriteViewerController = SpriteViewerControllerImpl(this)
|
||||||
|
val cardScreenController = CardScreenControllerImpl(this)
|
||||||
|
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
enableEdgeToEdge()
|
enableEdgeToEdge()
|
||||||
|
|
||||||
setContent {
|
setContent {
|
||||||
VBHelperTheme {
|
VBHelperTheme {
|
||||||
MainApplication()
|
MainApplication(
|
||||||
|
scanScreenController = scanScreenController,
|
||||||
|
settingsScreenController = settingsScreenController,
|
||||||
|
itemsScreenController = itemsScreenController,
|
||||||
|
adventureScreenController = adventureScreenController,
|
||||||
|
homeScreenController = homeScreenController,
|
||||||
|
storageScreenController = storageScreenController,
|
||||||
|
spriteViewerController = spriteViewerController,
|
||||||
|
cardScreenController = cardScreenController
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Log.i("MainActivity", "Activity onCreated")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPause() {
|
||||||
|
super.onPause()
|
||||||
|
Log.i("MainActivity", "onPause")
|
||||||
|
for(activityListener in onActivityLifecycleListeners) {
|
||||||
|
activityListener.value.onPause()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun registerFileActivityResult() {
|
override fun onResume() {
|
||||||
activityResultLauncher = registerForActivityResult(
|
super.onResume()
|
||||||
ActivityResultContracts.StartActivityForResult()
|
Log.i("MainActivity", "Resume")
|
||||||
) {
|
for(activityListener in onActivityLifecycleListeners) {
|
||||||
lifecycleScope.launch {
|
activityListener.value.onResume()
|
||||||
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)
|
|
||||||
val dimModel = Dim(
|
|
||||||
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
|
|
||||||
)
|
|
||||||
|
|
||||||
val dimId = storageRepository
|
|
||||||
.dimDao()
|
|
||||||
.insertNewDim(dimModel)
|
|
||||||
|
|
||||||
val characters = card.characterStats.characterEntries
|
|
||||||
|
|
||||||
var spriteCounter = 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 (index == 0) {
|
|
||||||
spriteCounter += 6
|
|
||||||
} else if (index == 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() {
|
private fun MainApplication(
|
||||||
var isDoneReadingCharacter by remember { mutableStateOf(false) }
|
scanScreenController: ScanScreenControllerImpl,
|
||||||
|
settingsScreenController: SettingsScreenControllerImpl,
|
||||||
|
itemsScreenController: ItemsScreenControllerImpl,
|
||||||
|
adventureScreenController: AdventureScreenControllerImpl,
|
||||||
|
storageScreenController: StorageScreenControllerImpl,
|
||||||
|
homeScreenController: HomeScreenControllerImpl,
|
||||||
|
spriteViewerController: SpriteViewerControllerImpl,
|
||||||
|
cardScreenController: CardScreenControllerImpl
|
||||||
|
) {
|
||||||
AppNavigation(
|
AppNavigation(
|
||||||
isDoneReadingCharacter = isDoneReadingCharacter,
|
applicationNavigationHandlers = AppNavigationHandlers(
|
||||||
onClickRead = {
|
settingsScreenController,
|
||||||
handleTag {
|
scanScreenController,
|
||||||
val character = it.receiveCharacter()
|
itemsScreenController,
|
||||||
nfcCharacter.value = character
|
adventureScreenController,
|
||||||
|
storageScreenController,
|
||||||
val importStatus = addCharacterScannedIntoDatabase()
|
homeScreenController,
|
||||||
|
spriteViewerController,
|
||||||
isDoneReadingCharacter = true
|
cardScreenController
|
||||||
|
|
||||||
importStatus
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onClickScan = {
|
|
||||||
isDoneReadingCharacter = false
|
|
||||||
},
|
|
||||||
onClickImportCard = {
|
|
||||||
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
|
|
||||||
addCategory(Intent.CATEGORY_OPENABLE)
|
|
||||||
type = "*/*"
|
|
||||||
}
|
|
||||||
activityResultLauncher.launch(intent)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// EXTRACTED DIRECTLY FROM EXAMPLE APP
|
|
||||||
private fun getMapOfCryptographicTransformers(): Map<UShort, CryptographicTransformer> {
|
|
||||||
return mapOf(
|
|
||||||
Pair(DeviceType.VitalBraceletBEDeviceType,
|
|
||||||
CryptographicTransformer(readableHmacKey1 = resources.getString(R.string.password1),
|
|
||||||
readableHmacKey2 = resources.getString(R.string.password2),
|
|
||||||
aesKey = resources.getString(R.string.decryptionKey),
|
|
||||||
substitutionCipher = resources.getIntArray(R.array.substitutionArray))),
|
|
||||||
// Pair(DeviceType.VitalSeriesDeviceType,
|
|
||||||
// CryptographicTransformer(hmacKey1 = resources.getString(R.string.password1),
|
|
||||||
// hmacKey2 = resources.getString(R.string.password2),
|
|
||||||
// decryptionKey = resources.getString(R.string.decryptionKey),
|
|
||||||
// substitutionCipher = resources.getIntArray(R.array.substitutionArray))),
|
|
||||||
// Pair(DeviceType.VitalCharactersDeviceType,
|
|
||||||
// CryptographicTransformer(hmacKey1 = resources.getString(R.string.password1),
|
|
||||||
// hmacKey2 = resources.getString(R.string.password2),
|
|
||||||
// decryptionKey = resources.getString(R.string.decryptionKey),
|
|
||||||
// substitutionCipher = resources.getIntArray(R.array.substitutionArray)))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// EXTRACTED DIRECTLY FROM EXAMPLE APP
|
|
||||||
private fun showWirelessSettings() {
|
|
||||||
Toast.makeText(this, "NFC must be enabled", Toast.LENGTH_SHORT).show()
|
|
||||||
startActivity(Intent(Settings.ACTION_WIRELESS_SETTINGS))
|
|
||||||
}
|
|
||||||
|
|
||||||
// EXTRACTED DIRECTLY FROM EXAMPLE APP
|
|
||||||
private fun buildOnReadTag(handlerFunc: (TagCommunicator)->String): (Tag)->Unit {
|
|
||||||
return { tag->
|
|
||||||
val nfcData = NfcA.get(tag)
|
|
||||||
if (nfcData == null) {
|
|
||||||
runOnUiThread {
|
|
||||||
Toast.makeText(this, "Tag detected is not VB", Toast.LENGTH_SHORT).show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
nfcData.connect()
|
|
||||||
nfcData.use {
|
|
||||||
val tagCommunicator = TagCommunicator.getInstance(nfcData, deviceToCryptographicTransformers)
|
|
||||||
val successText = handlerFunc(tagCommunicator)
|
|
||||||
runOnUiThread {
|
|
||||||
Toast.makeText(this, successText, Toast.LENGTH_SHORT).show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// EXTRACTED DIRECTLY FROM EXAMPLE APP
|
|
||||||
private fun handleTag(handlerFunc: (TagCommunicator)->String) {
|
|
||||||
if (!nfcAdapter.isEnabled) {
|
|
||||||
showWirelessSettings()
|
|
||||||
} else {
|
|
||||||
val options = Bundle()
|
|
||||||
// Work around for some broken Nfc firmware implementations that poll the card too fast
|
|
||||||
options.putInt(NfcAdapter.EXTRA_READER_PRESENCE_CHECK_DELAY, 250)
|
|
||||||
nfcAdapter.enableReaderMode(this, buildOnReadTag(handlerFunc), NfcAdapter.FLAG_READER_NFC_A or NfcAdapter.FLAG_READER_SKIP_NDEF_CHECK,
|
|
||||||
options
|
|
||||||
)
|
)
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// EXTRACTED DIRECTLY FROM EXAMPLE APP
|
|
||||||
override fun onPause() {
|
|
||||||
super.onPause()
|
|
||||||
if (nfcAdapter.isEnabled) {
|
|
||||||
nfcAdapter.disableReaderMode(this)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
/*
|
|
||||||
TODO:
|
|
||||||
- Make it able to detect the different model of watches
|
|
||||||
- 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 -> com.github.nacabaro.vbhelper.domain.DeviceType.BEDevice
|
|
||||||
else -> com.github.nacabaro.vbhelper.domain.DeviceType.VBDevice
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
val characterId: Long = storageRepository
|
|
||||||
.userCharacterDao()
|
|
||||||
.insertCharacterData(characterData)
|
|
||||||
|
|
||||||
if (nfcCharacter.value is BENfcCharacter) {
|
|
||||||
val beCharacter = nfcCharacter as MutableStateFlow<BENfcCharacter?>
|
|
||||||
val extraCharacterData = BECharacterData(
|
|
||||||
id = characterId,
|
|
||||||
trainingHp = beCharacter.value!!.trainingHp.toInt(),
|
|
||||||
trainingAp = beCharacter.value!!.trainingAp.toInt(),
|
|
||||||
trainingBp = beCharacter.value!!.trainingBp.toInt(),
|
|
||||||
remainingTrainingTimeInMinutes = beCharacter.value!!.remainingTrainingTimeInMinutes.toInt(),
|
|
||||||
itemEffectActivityLevelValue = beCharacter.value!!.itemEffectActivityLevelValue.toInt(),
|
|
||||||
itemEffectMentalStateValue = beCharacter.value!!.itemEffectMentalStateValue.toInt(),
|
|
||||||
itemEffectMentalStateMinutesRemaining = beCharacter.value!!.itemEffectMentalStateMinutesRemaining.toInt(),
|
|
||||||
itemEffectActivityLevelMinutesRemaining = beCharacter.value!!.itemEffectActivityLevelMinutesRemaining.toInt(),
|
|
||||||
itemEffectVitalPointsChangeValue = beCharacter.value!!.itemEffectVitalPointsChangeValue.toInt(),
|
|
||||||
itemEffectVitalPointsChangeMinutesRemaining = beCharacter.value!!.itemEffectVitalPointsChangeMinutesRemaining.toInt(),
|
|
||||||
abilityRarity = beCharacter.value!!.abilityRarity,
|
|
||||||
abilityType = beCharacter.value!!.abilityType.toInt(),
|
|
||||||
abilityBranch = beCharacter.value!!.abilityBranch.toInt(),
|
|
||||||
abilityReset = beCharacter.value!!.abilityReset.toInt(),
|
|
||||||
rank = beCharacter.value!!.abilityReset.toInt(),
|
|
||||||
itemType = beCharacter.value!!.itemType.toInt(),
|
|
||||||
itemMultiplier = beCharacter.value!!.itemMultiplier.toInt(),
|
|
||||||
itemRemainingTime = beCharacter.value!!.itemRemainingTime.toInt(),
|
|
||||||
otp0 = "", //beCharacter.value!!.otp0.toString(),
|
|
||||||
otp1 = "", //beCharacter.value!!.otp1.toString(),
|
|
||||||
minorVersion = beCharacter.value!!.characterCreationFirmwareVersion.minorVersion.toInt(),
|
|
||||||
majorVersion = beCharacter.value!!.characterCreationFirmwareVersion.majorVersion.toInt(),
|
|
||||||
)
|
|
||||||
|
|
||||||
storageRepository
|
|
||||||
.userCharacterDao()
|
|
||||||
.insertBECharacterData(extraCharacterData)
|
|
||||||
|
|
||||||
val transformationHistoryWatch = beCharacter.value!!.transformationHistory
|
|
||||||
val domainTransformationHistory = transformationHistoryWatch.map { item ->
|
|
||||||
TransformationHistory(
|
|
||||||
monId = characterId,
|
|
||||||
toCharIndex = item.toCharIndex.toInt(),
|
|
||||||
year = item.year.toInt(),
|
|
||||||
month = item.month.toInt(),
|
|
||||||
day = item.day.toInt()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
storageRepository
|
|
||||||
.userCharacterDao()
|
|
||||||
.insertTransformationHistory(*domainTransformationHistory.toTypedArray())
|
|
||||||
} else {
|
|
||||||
return "Not implemented yet"
|
|
||||||
}
|
|
||||||
|
|
||||||
return "Done reading character!"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,48 +1,209 @@
|
|||||||
package com.github.nacabaro.vbhelper.components
|
package com.github.nacabaro.vbhelper.components
|
||||||
|
|
||||||
import android.graphics.Bitmap
|
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.Box
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.aspectRatio
|
||||||
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.material3.Card
|
import androidx.compose.material3.Card
|
||||||
|
import androidx.compose.material3.CardDefaults
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
|
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.text.style.TextAlign
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.github.nacabaro.vbhelper.utils.BitmapData
|
import com.github.nacabaro.vbhelper.utils.BitmapData
|
||||||
import java.nio.ByteBuffer
|
import com.github.nacabaro.vbhelper.utils.getBitmap
|
||||||
|
import androidx.compose.ui.graphics.Shape
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import com.github.cfogrady.vbnfc.vb.SpecialMission
|
||||||
|
import com.github.nacabaro.vbhelper.R
|
||||||
|
import com.github.nacabaro.vbhelper.domain.device_data.SpecialMissions
|
||||||
|
import com.github.nacabaro.vbhelper.utils.getObscuredBitmap
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun CharacterEntry(
|
fun CharacterEntry(
|
||||||
icon: BitmapData,
|
icon: BitmapData,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
|
obscure: Boolean = false,
|
||||||
|
disabled: Boolean = false,
|
||||||
|
shape: Shape = MaterialTheme.shapes.medium,
|
||||||
|
multiplier: Int = 4,
|
||||||
onClick: () -> Unit = { }
|
onClick: () -> Unit = { }
|
||||||
) {
|
) {
|
||||||
val bitmap = remember (icon.bitmap) {
|
val bitmap = remember (icon.bitmap) {
|
||||||
Bitmap.createBitmap(icon.width, icon.height, Bitmap.Config.RGB_565).apply {
|
if(obscure) icon.getObscuredBitmap() else icon.getBitmap()
|
||||||
copyPixelsFromBuffer(ByteBuffer.wrap(icon.bitmap))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
val imageBitmap = remember(bitmap) { bitmap.asImageBitmap() }
|
val imageBitmap = remember(bitmap) { bitmap.asImageBitmap() }
|
||||||
|
val density: Float = LocalContext.current.resources.displayMetrics.density
|
||||||
|
val dpSize = (icon.width * multiplier / density).dp
|
||||||
|
|
||||||
Card(
|
Card(
|
||||||
shape = MaterialTheme.shapes.medium,
|
shape = shape,
|
||||||
onClick = onClick,
|
onClick = when (disabled) {
|
||||||
|
true -> { {} }
|
||||||
|
false -> onClick
|
||||||
|
},
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
|
.aspectRatio(1f)
|
||||||
.padding(8.dp)
|
.padding(8.dp)
|
||||||
.size(96.dp)
|
|
||||||
) {
|
) {
|
||||||
Image(
|
Box(
|
||||||
bitmap = imageBitmap,
|
contentAlignment = Alignment.BottomCenter,
|
||||||
contentDescription = "Icon",
|
|
||||||
filterQuality = FilterQuality.None,
|
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(8.dp)
|
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
|
.padding(4.dp)
|
||||||
|
) {
|
||||||
|
Image(
|
||||||
|
bitmap = imageBitmap,
|
||||||
|
contentDescription = "Icon",
|
||||||
|
filterQuality = FilterQuality.None,
|
||||||
|
colorFilter = when (obscure) {
|
||||||
|
true -> ColorFilter.tint(color = MaterialTheme.colorScheme.secondary)
|
||||||
|
false -> null
|
||||||
|
},
|
||||||
|
modifier = Modifier
|
||||||
|
.size(dpSize)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ItemDisplay(
|
||||||
|
icon: Int,
|
||||||
|
textValue: String,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
definition: String = "",
|
||||||
|
) {
|
||||||
|
val context = LocalContext.current
|
||||||
|
Card(
|
||||||
|
modifier = modifier,
|
||||||
|
shape = androidx.compose.material.MaterialTheme.shapes.small,
|
||||||
|
onClick = {
|
||||||
|
Toast.makeText(context, definition, Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
verticalArrangement = Arrangement.Center,
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
painter = painterResource(icon),
|
||||||
|
contentDescription = "Vitals",
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize(0.5f)
|
||||||
|
.padding(8.dp)
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = textValue,
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
fontFamily = MaterialTheme.typography.titleLarge.fontFamily,
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun SpecialMissionsEntry(
|
||||||
|
specialMission: SpecialMissions,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
onClickCard: () -> Unit = { },
|
||||||
|
) {
|
||||||
|
val textValue = when (specialMission.missionType) {
|
||||||
|
SpecialMission.Type.NONE -> "No mission selected"
|
||||||
|
SpecialMission.Type.STEPS -> "Walk ${specialMission.goal} steps"
|
||||||
|
SpecialMission.Type.BATTLES -> "Battle ${specialMission.goal} times"
|
||||||
|
SpecialMission.Type.WINS -> "Win ${specialMission.goal} battles"
|
||||||
|
SpecialMission.Type.VITALS -> "Earn ${specialMission.goal} vitals"
|
||||||
|
}
|
||||||
|
|
||||||
|
val progress = if (specialMission.status == SpecialMission.Status.COMPLETED) {
|
||||||
|
specialMission.goal
|
||||||
|
} else {
|
||||||
|
specialMission.progress
|
||||||
|
}
|
||||||
|
|
||||||
|
val completion = when (specialMission.missionType) {
|
||||||
|
SpecialMission.Type.NONE -> ""
|
||||||
|
SpecialMission.Type.STEPS -> "Walked $progress steps"
|
||||||
|
SpecialMission.Type.BATTLES -> "Battled $progress times"
|
||||||
|
SpecialMission.Type.WINS -> "Won $progress battles"
|
||||||
|
SpecialMission.Type.VITALS -> "Earned $progress vitals"
|
||||||
|
}
|
||||||
|
|
||||||
|
val icon = when (specialMission.missionType) {
|
||||||
|
SpecialMission.Type.NONE -> R.drawable.baseline_free_24
|
||||||
|
SpecialMission.Type.STEPS -> R.drawable.baseline_agility_24
|
||||||
|
SpecialMission.Type.BATTLES -> R.drawable.baseline_swords_24
|
||||||
|
SpecialMission.Type.WINS -> R.drawable.baseline_trophy_24
|
||||||
|
SpecialMission.Type.VITALS -> R.drawable.baseline_vitals_24
|
||||||
|
}
|
||||||
|
|
||||||
|
val color = when (specialMission.status)
|
||||||
|
{
|
||||||
|
SpecialMission.Status.IN_PROGRESS -> MaterialTheme.colorScheme.secondary
|
||||||
|
SpecialMission.Status.COMPLETED -> MaterialTheme.colorScheme.primary
|
||||||
|
SpecialMission.Status.FAILED -> MaterialTheme.colorScheme.error
|
||||||
|
else -> MaterialTheme.colorScheme.surfaceContainerHighest
|
||||||
|
}
|
||||||
|
|
||||||
|
Card(
|
||||||
|
modifier = modifier,
|
||||||
|
shape = androidx.compose.material.MaterialTheme.shapes.small,
|
||||||
|
onClick = if (specialMission.status == SpecialMission.Status.COMPLETED || specialMission.status == SpecialMission.Status.FAILED) {
|
||||||
|
onClickCard
|
||||||
|
} else {
|
||||||
|
{ }
|
||||||
|
},
|
||||||
|
colors = CardDefaults.cardColors(
|
||||||
|
containerColor = color
|
||||||
)
|
)
|
||||||
|
|
||||||
|
) {
|
||||||
|
Row (
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(16.dp)
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
painter = painterResource(icon),
|
||||||
|
contentDescription = "Vitals",
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxHeight()
|
||||||
|
.padding(16.dp)
|
||||||
|
)
|
||||||
|
Column {
|
||||||
|
Text(
|
||||||
|
text = textValue,
|
||||||
|
fontFamily = MaterialTheme.typography.titleLarge.fontFamily,
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = completion,
|
||||||
|
fontFamily = MaterialTheme.typography.titleSmall.fontFamily,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -23,6 +23,9 @@ fun TopBanner(
|
|||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
onGearClick: (() -> Unit)? = null,
|
onGearClick: (() -> Unit)? = null,
|
||||||
onBackClick: (() -> Unit)? = null,
|
onBackClick: (() -> Unit)? = null,
|
||||||
|
onScanClick: (() -> Unit)? = null,
|
||||||
|
onAdventureClick: (() -> Unit)? = null,
|
||||||
|
onModifyClick: (() -> Unit)? = null
|
||||||
) {
|
) {
|
||||||
Box( // Use Box to overlay elements
|
Box( // Use Box to overlay elements
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
@@ -35,29 +38,62 @@ fun TopBanner(
|
|||||||
textAlign = TextAlign.Center,
|
textAlign = TextAlign.Center,
|
||||||
fontSize = 24.sp,
|
fontSize = 24.sp,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.align(Alignment.Center) // Center the text
|
.align(Alignment.Center)
|
||||||
)
|
)
|
||||||
if (onGearClick != null) {
|
if (onGearClick != null) {
|
||||||
IconButton(
|
IconButton(
|
||||||
onClick = onGearClick,
|
onClick = onGearClick,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.align(Alignment.CenterEnd) // Place gear icon at the end
|
.align(Alignment.CenterEnd)
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
painter = painterResource(R.drawable.baseline_settings_24), // Use a gear icon
|
painter = painterResource(R.drawable.baseline_settings_24),
|
||||||
contentDescription = "Settings"
|
contentDescription = "Settings"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
} else if (onAdventureClick != null) {
|
||||||
|
IconButton(
|
||||||
|
onClick = onAdventureClick,
|
||||||
|
modifier = Modifier
|
||||||
|
.align(Alignment.CenterEnd)
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
painter = painterResource(R.drawable.baseline_fort_24),
|
||||||
|
contentDescription = "Adventure"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else if (onModifyClick != null) {
|
||||||
|
IconButton(
|
||||||
|
onClick = onModifyClick,
|
||||||
|
modifier = Modifier
|
||||||
|
.align(Alignment.CenterEnd)
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
painter = painterResource(R.drawable.baseline_edit_24),
|
||||||
|
contentDescription = "Adventure"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (onBackClick != null) {
|
if (onScanClick != null) {
|
||||||
|
IconButton(
|
||||||
|
onClick = onScanClick,
|
||||||
|
modifier = Modifier
|
||||||
|
.align(Alignment.CenterStart)
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
painter = painterResource(R.drawable.baseline_nfc_24),
|
||||||
|
contentDescription = "Scan"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else if (onBackClick != null) {
|
||||||
IconButton(
|
IconButton(
|
||||||
onClick = onBackClick,
|
onClick = onBackClick,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.align(Alignment.CenterStart) // Place gear icon at the end
|
.align(Alignment.CenterStart)
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
painter = painterResource(R.drawable.baseline_arrow_back_24), // Use a gear icon
|
painter = painterResource(R.drawable.baseline_arrow_back_24),
|
||||||
contentDescription = "Settings"
|
contentDescription = "Settings"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+76
@@ -0,0 +1,76 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.components
|
||||||
|
|
||||||
|
import androidx.compose.foundation.Image
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.aspectRatio
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.lazy.LazyRow
|
||||||
|
import androidx.compose.foundation.lazy.items
|
||||||
|
import androidx.compose.material3.Card
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
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 com.github.nacabaro.vbhelper.domain.device_data.TransformationHistory
|
||||||
|
import com.github.nacabaro.vbhelper.dtos.CharacterDtos
|
||||||
|
import com.github.nacabaro.vbhelper.utils.BitmapData
|
||||||
|
import com.github.nacabaro.vbhelper.utils.getBitmap
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun TransformationHistoryCard(
|
||||||
|
transformationHistory: List<CharacterDtos.TransformationHistory>,
|
||||||
|
modifier: Modifier= Modifier
|
||||||
|
) {
|
||||||
|
Card (
|
||||||
|
shape = androidx.compose.material.MaterialTheme.shapes.small,
|
||||||
|
modifier = modifier
|
||||||
|
) {
|
||||||
|
LazyRow (
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(8.dp)
|
||||||
|
) {
|
||||||
|
items(transformationHistory) { transformation ->
|
||||||
|
TransformationHistoryItem(transformation)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun TransformationHistoryItem(
|
||||||
|
transformation: CharacterDtos.TransformationHistory
|
||||||
|
) {
|
||||||
|
val bitmapData = BitmapData(
|
||||||
|
bitmap = transformation.spriteIdle,
|
||||||
|
width = transformation.spriteWidth,
|
||||||
|
height = transformation.spriteHeight
|
||||||
|
)
|
||||||
|
val bitmap = remember (bitmapData) { bitmapData.getBitmap() }
|
||||||
|
val imageBitmap = remember(bitmap) { bitmap.asImageBitmap() }
|
||||||
|
val density: Float = LocalContext.current.resources.displayMetrics.density
|
||||||
|
val dpSize = (bitmap.width * 3 / density).dp
|
||||||
|
|
||||||
|
Box (
|
||||||
|
contentAlignment = Alignment.BottomCenter,
|
||||||
|
modifier = Modifier
|
||||||
|
.aspectRatio(1f)
|
||||||
|
.fillMaxWidth()
|
||||||
|
.size((64*3/density).dp)
|
||||||
|
) {
|
||||||
|
Image(
|
||||||
|
bitmap = imageBitmap,
|
||||||
|
contentDescription = "Transformation",
|
||||||
|
filterQuality = FilterQuality.None,
|
||||||
|
modifier = Modifier
|
||||||
|
.size(dpSize)
|
||||||
|
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
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,
|
||||||
|
s.spriteIdle1 AS spriteIdle,
|
||||||
|
s.width AS spriteWidth,
|
||||||
|
s.height AS spriteHeight,
|
||||||
|
d.isBEm as isBemCard,
|
||||||
|
a.finishesAdventure AS finishesAdventure,
|
||||||
|
a.originalDuration AS originalTimeInMinutes
|
||||||
|
FROM UserCharacter uc
|
||||||
|
JOIN CardCharacter c ON uc.charId = c.id
|
||||||
|
JOIN Sprite s ON s.id = c.spriteId
|
||||||
|
JOIN Card d ON c.cardId = 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,58 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.daos
|
||||||
|
|
||||||
|
import androidx.room.Dao
|
||||||
|
import androidx.room.Query
|
||||||
|
import com.github.nacabaro.vbhelper.dtos.CardDtos
|
||||||
|
|
||||||
|
@Dao
|
||||||
|
interface CardAdventureDao {
|
||||||
|
@Query("""
|
||||||
|
INSERT INTO
|
||||||
|
CardAdventure (cardId, characterId, steps, bossAp, bossHp, bossDp, bossBp)
|
||||||
|
SELECT
|
||||||
|
:cardId,
|
||||||
|
cc.id,
|
||||||
|
:steps,
|
||||||
|
:bossAp,
|
||||||
|
:bossHp,
|
||||||
|
:bossDp,
|
||||||
|
:bossBp
|
||||||
|
FROM
|
||||||
|
CardCharacter cc
|
||||||
|
WHERE
|
||||||
|
cc.charaIndex = :characterId AND
|
||||||
|
cc.cardId = :cardId
|
||||||
|
""")
|
||||||
|
suspend fun insertNewAdventure(
|
||||||
|
cardId: Long,
|
||||||
|
characterId: Int,
|
||||||
|
steps: Int,
|
||||||
|
bossAp: Int,
|
||||||
|
bossHp: Int,
|
||||||
|
bossDp: Int,
|
||||||
|
bossBp: Int?
|
||||||
|
)
|
||||||
|
|
||||||
|
@Query("""
|
||||||
|
SELECT
|
||||||
|
cc.nameSprite as characterName,
|
||||||
|
cc.nameWidth as characterNameWidth,
|
||||||
|
cc.nameHeight as characterNameHeight,
|
||||||
|
s.spriteIdle1 as characterIdleSprite,
|
||||||
|
s.width as characterIdleSpriteWidth,
|
||||||
|
s.height as characterIdleSpriteHeight,
|
||||||
|
ca.bossAp as characterAp,
|
||||||
|
ca.bossBp as characterBp,
|
||||||
|
ca.bossDp as characterDp,
|
||||||
|
ca.bossHp as characterHp,
|
||||||
|
ca.steps as steps
|
||||||
|
FROM CardCharacter cc
|
||||||
|
JOIN Sprite s ON cc.spriteId = s.id
|
||||||
|
JOIN CardAdventure ca ON cc.id = ca.characterId
|
||||||
|
WHERE
|
||||||
|
cc.cardId = :cardId
|
||||||
|
""")
|
||||||
|
suspend fun getAdventureForCard(
|
||||||
|
cardId: Long
|
||||||
|
): List<CardDtos.CardAdventureWithSprites>
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.daos
|
||||||
|
|
||||||
|
import androidx.room.Dao
|
||||||
|
import androidx.room.Insert
|
||||||
|
import androidx.room.OnConflictStrategy
|
||||||
|
import androidx.room.Query
|
||||||
|
import com.github.nacabaro.vbhelper.domain.card.Card
|
||||||
|
|
||||||
|
@Dao
|
||||||
|
interface CardDao {
|
||||||
|
@Insert(onConflict = OnConflictStrategy.IGNORE)
|
||||||
|
suspend fun insertNewCard(card: Card): Long
|
||||||
|
|
||||||
|
@Query("SELECT * FROM Card WHERE cardId = :id")
|
||||||
|
fun getCardByCardId(id: Int): List<Card>
|
||||||
|
|
||||||
|
@Query("SELECT * FROM Card WHERE id = :id")
|
||||||
|
fun getCardById(id: Long): Card?
|
||||||
|
|
||||||
|
@Query(
|
||||||
|
"""
|
||||||
|
SELECT ca.*
|
||||||
|
FROM Card ca
|
||||||
|
JOIN CardCharacter ch ON ca.id = ch.cardId
|
||||||
|
JOIN UserCharacter uc ON ch.id = uc.charId
|
||||||
|
WHERE uc.id = :id
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
suspend fun getCardByCharacterId(id: Long): Card
|
||||||
|
|
||||||
|
@Query("UPDATE Card SET name = :newName WHERE id = :id")
|
||||||
|
suspend fun renameCard(id: Int, newName: String)
|
||||||
|
|
||||||
|
@Query("DELETE FROM Card WHERE id = :id")
|
||||||
|
suspend fun deleteCard(id: Long)
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.daos
|
||||||
|
|
||||||
|
import androidx.room.Dao
|
||||||
|
import androidx.room.Query
|
||||||
|
|
||||||
|
@Dao
|
||||||
|
interface CardFusionsDao {
|
||||||
|
@Query("""
|
||||||
|
INSERT INTO
|
||||||
|
CardFusions (
|
||||||
|
fromCharaId,
|
||||||
|
attribute1Fusion,
|
||||||
|
attribute2Fusion,
|
||||||
|
attribute3Fusion,
|
||||||
|
attribute4Fusion
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
(SELECT id FROM CardCharacter WHERE cardId = :cardId AND charaIndex = :fromCharaId),
|
||||||
|
(SELECT id FROM CardCharacter WHERE cardId = :cardId AND charaIndex = :toCharaIdAttr1),
|
||||||
|
(SELECT id FROM CardCharacter WHERE cardId = :cardId AND charaIndex = :toCharaIdAttr2),
|
||||||
|
(SELECT id FROM CardCharacter WHERE cardId = :cardId AND charaIndex = :toCharaIdAttr3),
|
||||||
|
(SELECT id FROM CardCharacter WHERE cardId = :cardId AND charaIndex = :toCharaIdAttr4)
|
||||||
|
""")
|
||||||
|
suspend fun insertNewFusion(
|
||||||
|
cardId: Long,
|
||||||
|
fromCharaId: Int,
|
||||||
|
toCharaIdAttr1: Int,
|
||||||
|
toCharaIdAttr2: Int,
|
||||||
|
toCharaIdAttr3: Int,
|
||||||
|
toCharaIdAttr4: Int
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.daos
|
||||||
|
|
||||||
|
import androidx.room.Dao
|
||||||
|
import androidx.room.Query
|
||||||
|
import androidx.room.Upsert
|
||||||
|
import com.github.nacabaro.vbhelper.domain.card.CardProgress
|
||||||
|
|
||||||
|
@Dao
|
||||||
|
interface CardProgressDao {
|
||||||
|
@Upsert
|
||||||
|
fun updateDimProgress(vararg cardProgresses: CardProgress)
|
||||||
|
|
||||||
|
@Query(
|
||||||
|
"SELECT currentStage FROM CardProgress WHERE cardId = :cardId"
|
||||||
|
)
|
||||||
|
fun getCardProgress(cardId: Long): Int
|
||||||
|
}
|
||||||
@@ -3,26 +3,85 @@ package com.github.nacabaro.vbhelper.daos
|
|||||||
import androidx.room.Dao
|
import androidx.room.Dao
|
||||||
import androidx.room.Insert
|
import androidx.room.Insert
|
||||||
import androidx.room.Query
|
import androidx.room.Query
|
||||||
import com.github.nacabaro.vbhelper.domain.Character
|
import com.github.nacabaro.vbhelper.domain.card.CardCharacter
|
||||||
import com.github.nacabaro.vbhelper.domain.Sprites
|
import com.github.nacabaro.vbhelper.domain.characters.Sprite
|
||||||
|
import com.github.nacabaro.vbhelper.dtos.CharacterDtos
|
||||||
|
|
||||||
@Dao
|
@Dao
|
||||||
interface CharacterDao {
|
interface CharacterDao {
|
||||||
@Insert
|
@Insert
|
||||||
suspend fun insertCharacter(vararg characterData: Character)
|
suspend fun insertCharacter(vararg characterData: CardCharacter)
|
||||||
|
|
||||||
@Query("SELECT * FROM Character")
|
@Query("SELECT * FROM CardCharacter WHERE charaIndex = :monIndex AND cardId = :dimId LIMIT 1")
|
||||||
suspend fun getAllCharacters(): List<Character>
|
fun getCharacterByMonIndex(monIndex: Int, dimId: Long): CardCharacter
|
||||||
|
|
||||||
@Query("SELECT * FROM Character WHERE dimId = :dimId")
|
|
||||||
suspend fun getCharacterByDimId(dimId: Int): List<Character>
|
|
||||||
|
|
||||||
@Query("SELECT * FROM Character WHERE monIndex = :monIndex AND dimId = :dimId LIMIT 1")
|
|
||||||
fun getCharacterByMonIndex(monIndex: Int, dimId: Long): Character
|
|
||||||
|
|
||||||
@Insert
|
@Insert
|
||||||
suspend fun insertSprite(vararg sprite: Sprites)
|
suspend fun insertSprite(vararg sprite: Sprite)
|
||||||
|
|
||||||
@Query("SELECT * FROM Sprites")
|
@Query(
|
||||||
suspend fun getAllSprites(): List<Sprites>
|
"""
|
||||||
|
SELECT
|
||||||
|
d.cardId as cardId,
|
||||||
|
c.charaIndex as charId,
|
||||||
|
c.stage as stage,
|
||||||
|
c.attribute as attribute
|
||||||
|
FROM CardCharacter c
|
||||||
|
JOIN UserCharacter uc ON c.id = uc.charId
|
||||||
|
JOIN Card d ON c.cardId = d.id
|
||||||
|
WHERE c.id = :charId
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
suspend fun getCharacterInfo(charId: Long): CharacterDtos.CardCharacterInfo
|
||||||
|
|
||||||
|
@Query(
|
||||||
|
"""
|
||||||
|
INSERT INTO PossibleTransformations (charaId, requiredVitals, requiredTrophies, requiredBattles, requiredWinRate, changeTimerHours, requiredAdventureLevelCompleted, toCharaId)
|
||||||
|
SELECT
|
||||||
|
(SELECT id FROM CardCharacter WHERE charaIndex = :fromChraraIndex AND cardId = :cardId),
|
||||||
|
:requiredVitals,
|
||||||
|
:requiredTrophies,
|
||||||
|
:requiredBattles,
|
||||||
|
:requiredWinRate,
|
||||||
|
:changeTimerHours,
|
||||||
|
:requiredAdventureLevelCompleted,
|
||||||
|
(SELECT id FROM CardCharacter WHERE charaIndex = :toChraraIndex AND cardId = :cardId)
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
suspend fun insertPossibleTransformation(
|
||||||
|
fromChraraIndex: Int,
|
||||||
|
toChraraIndex: Int,
|
||||||
|
cardId: Long,
|
||||||
|
requiredVitals: Int,
|
||||||
|
requiredTrophies: Int,
|
||||||
|
requiredBattles: Int,
|
||||||
|
changeTimerHours: Int,
|
||||||
|
requiredWinRate: Int,
|
||||||
|
requiredAdventureLevelCompleted: Int
|
||||||
|
)
|
||||||
|
|
||||||
|
@Query(
|
||||||
|
"""
|
||||||
|
SELECT
|
||||||
|
pt.charaId as fromCharaId,
|
||||||
|
pt.toCharaId as charaId,
|
||||||
|
s.spriteIdle1 as spriteIdle,
|
||||||
|
s.width as spriteWidth,
|
||||||
|
s.height as spriteHeight,
|
||||||
|
d.discoveredOn as discoveredOn,
|
||||||
|
pt.requiredTrophies as requiredTrophies,
|
||||||
|
pt.requiredVitals as requiredVitals,
|
||||||
|
pt.requiredBattles as requiredBattles,
|
||||||
|
pt.requiredWinRate as requiredWinRate,
|
||||||
|
pt.changeTimerHours as changeTimerHours,
|
||||||
|
pt.requiredAdventureLevelCompleted as requiredAdventureLevelCompleted
|
||||||
|
FROM
|
||||||
|
PossibleTransformations pt
|
||||||
|
JOIN CardCharacter c on pt.toCharaId = c.id
|
||||||
|
JOIN Sprite s ON s.id = c.spriteId
|
||||||
|
LEFT JOIN Dex d ON d.id = pt.toCharaId
|
||||||
|
WHERE
|
||||||
|
c.cardId = :cardId
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
suspend fun getEvolutionRequirementsForCard(cardId: Long): List<CharacterDtos.EvolutionRequirementsWithSpritesAndObtained>
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.daos
|
||||||
|
|
||||||
|
import androidx.room.Dao
|
||||||
|
import androidx.room.Query
|
||||||
|
import com.github.nacabaro.vbhelper.dtos.CardDtos
|
||||||
|
import com.github.nacabaro.vbhelper.dtos.CharacterDtos
|
||||||
|
|
||||||
|
@Dao
|
||||||
|
interface DexDao {
|
||||||
|
@Query(
|
||||||
|
"""
|
||||||
|
INSERT OR IGNORE INTO Dex(id, discoveredOn)
|
||||||
|
VALUES (
|
||||||
|
(SELECT id FROM CardCharacter WHERE charaIndex = :charIndex AND cardId = :cardId),
|
||||||
|
:discoveredOn
|
||||||
|
)
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
fun insertCharacter(charIndex: Int, cardId: Long, discoveredOn: Long)
|
||||||
|
|
||||||
|
@Query(
|
||||||
|
"""
|
||||||
|
SELECT
|
||||||
|
c.id AS id,
|
||||||
|
s.spriteIdle1 AS spriteIdle,
|
||||||
|
s.width AS spriteWidth,
|
||||||
|
s.height AS spriteHeight,
|
||||||
|
c.nameSprite AS nameSprite,
|
||||||
|
c.nameWidth AS nameSpriteWidth,
|
||||||
|
c.nameHeight AS nameSpriteHeight,
|
||||||
|
d.discoveredOn AS discoveredOn,
|
||||||
|
c.baseHp as baseHp,
|
||||||
|
c.baseBp as baseBp,
|
||||||
|
c.baseAp as baseAp,
|
||||||
|
c.stage as stage,
|
||||||
|
c.attribute as attribute
|
||||||
|
FROM CardCharacter c
|
||||||
|
JOIN Sprite s ON c.spriteId = s.id
|
||||||
|
LEFT JOIN dex d ON c.id = d.id
|
||||||
|
WHERE c.cardId = :cardId
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
suspend fun getSingleCardProgress(cardId: Long): List<CharacterDtos.CardCharaProgress>
|
||||||
|
|
||||||
|
@Query(
|
||||||
|
"""
|
||||||
|
SELECT
|
||||||
|
c.id as cardId,
|
||||||
|
c.name as cardName,
|
||||||
|
c.logo as cardLogo,
|
||||||
|
c.logoWidth as logoWidth,
|
||||||
|
c.logoHeight as logoHeight,
|
||||||
|
(SELECT COUNT(*) FROM CardCharacter cc WHERE cc.cardId = c.id) AS totalCharacters,
|
||||||
|
(SELECT COUNT(*) FROM Dex d JOIN CardCharacter cc ON d.id = cc.id WHERE cc.cardId = c.id AND d.discoveredOn IS NOT NULL) AS obtainedCharacters
|
||||||
|
FROM Card c
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
suspend fun getCardsWithProgress(): List<CardDtos.CardProgress>
|
||||||
|
}
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
package com.github.nacabaro.vbhelper.daos
|
|
||||||
|
|
||||||
import androidx.room.Dao
|
|
||||||
import androidx.room.Insert
|
|
||||||
import androidx.room.OnConflictStrategy
|
|
||||||
import androidx.room.Query
|
|
||||||
import com.github.nacabaro.vbhelper.domain.Dim
|
|
||||||
|
|
||||||
@Dao
|
|
||||||
interface DiMDao {
|
|
||||||
@Insert(onConflict = OnConflictStrategy.IGNORE)
|
|
||||||
suspend fun insertNewDim(dim: Dim): Long
|
|
||||||
|
|
||||||
@Query("SELECT * FROM Dim")
|
|
||||||
suspend fun getAllDims(): List<Dim>
|
|
||||||
|
|
||||||
@Query("SELECT * FROM Dim WHERE dimId = :id")
|
|
||||||
fun getDimById(id: Int): Dim?
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
package com.github.nacabaro.vbhelper.daos
|
|
||||||
|
|
||||||
import androidx.room.Dao
|
|
||||||
import androidx.room.Upsert
|
|
||||||
import com.github.nacabaro.vbhelper.domain.DimProgress
|
|
||||||
|
|
||||||
@Dao
|
|
||||||
interface DiMProgressDao {
|
|
||||||
@Upsert
|
|
||||||
suspend fun updateDimProgress(vararg dimProgress: DimProgress)
|
|
||||||
}
|
|
||||||
@@ -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
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
suspend fun getItem(itemId: Long): ItemDtos.ItemsWithQuantities
|
||||||
|
|
||||||
|
@Query(
|
||||||
|
"""
|
||||||
|
UPDATE Items
|
||||||
|
SET quantity = quantity - 1
|
||||||
|
WHERE id = :itemId
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
suspend fun useItem(itemId: Long)
|
||||||
|
|
||||||
|
@Query(
|
||||||
|
"""
|
||||||
|
UPDATE Items
|
||||||
|
SET quantity = quantity + :itemAmount
|
||||||
|
WHERE id = :itemId
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
suspend fun purchaseItem(itemId: Long, itemAmount: Int)
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.daos
|
||||||
|
|
||||||
|
import androidx.room.Dao
|
||||||
|
import androidx.room.Query
|
||||||
|
|
||||||
|
@Dao
|
||||||
|
interface SpecialMissionDao {
|
||||||
|
@Query("""
|
||||||
|
UPDATE SpecialMissions SET
|
||||||
|
missionType = "NONE",
|
||||||
|
status = "UNAVAILABLE"
|
||||||
|
WHERE id = :id
|
||||||
|
""")
|
||||||
|
suspend fun clearSpecialMission(id: Long)
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.daos
|
||||||
|
|
||||||
|
import androidx.room.Dao
|
||||||
|
import androidx.room.Insert
|
||||||
|
import androidx.room.OnConflictStrategy
|
||||||
|
import androidx.room.Query
|
||||||
|
import com.github.nacabaro.vbhelper.domain.characters.Sprite
|
||||||
|
|
||||||
|
@Dao
|
||||||
|
interface SpriteDao {
|
||||||
|
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||||
|
fun insertSprite(sprite: Sprite): Long
|
||||||
|
|
||||||
|
@Query("SELECT * FROM Sprite")
|
||||||
|
suspend fun getAllSprites(): List<Sprite>
|
||||||
|
}
|
||||||
@@ -4,9 +4,14 @@ 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.card.CardCharacter
|
||||||
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.SpecialMissions
|
||||||
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.VBCharacterData
|
||||||
|
import com.github.nacabaro.vbhelper.domain.device_data.VitalsHistory
|
||||||
import com.github.nacabaro.vbhelper.dtos.CharacterDtos
|
import com.github.nacabaro.vbhelper.dtos.CharacterDtos
|
||||||
|
|
||||||
@Dao
|
@Dao
|
||||||
@@ -17,23 +22,210 @@ interface UserCharacterDao {
|
|||||||
@Insert
|
@Insert
|
||||||
fun insertBECharacterData(characterData: BECharacterData)
|
fun insertBECharacterData(characterData: BECharacterData)
|
||||||
|
|
||||||
|
@Insert
|
||||||
|
fun insertVBCharacterData(characterData: VBCharacterData)
|
||||||
|
|
||||||
|
@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)
|
||||||
|
|
||||||
@Query("SELECT * FROM TransformationHistory WHERE monId = :monId")
|
@Upsert
|
||||||
fun getTransformationHistory(monId: Int): List<TransformationHistory>
|
fun insertSpecialMissions(vararg specialMissions: SpecialMissions)
|
||||||
|
|
||||||
@Query("""
|
@Query(
|
||||||
|
"""
|
||||||
|
SELECT
|
||||||
|
c.id AS id,
|
||||||
|
s.spriteIdle1 AS spriteIdle,
|
||||||
|
s.width AS spriteWidth,
|
||||||
|
s.height AS spriteHeight,
|
||||||
|
c.charaIndex AS monIndex,
|
||||||
|
t.transformationDate AS transformationDate
|
||||||
|
FROM TransformationHistory t
|
||||||
|
JOIN CardCharacter c ON c.id = t.stageId
|
||||||
|
JOIN Sprite s ON s.id = c.spriteId
|
||||||
|
WHERE monId = :monId
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
suspend fun getTransformationHistory(monId: Long): List<CharacterDtos.TransformationHistory>?
|
||||||
|
|
||||||
|
@Query(
|
||||||
|
"""
|
||||||
SELECT
|
SELECT
|
||||||
uc.*,
|
uc.*,
|
||||||
c.sprite1 AS spriteIdle,
|
c.stage,
|
||||||
c.spritesWidth AS spriteWidth,
|
c.attribute,
|
||||||
c.spritesHeight AS spriteHeight
|
s.spriteIdle1 AS spriteIdle,
|
||||||
|
s.spriteIdle2 AS spriteIdle2,
|
||||||
|
s.width AS spriteWidth,
|
||||||
|
s.height AS spriteHeight,
|
||||||
|
c.nameSprite 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 CardCharacter c ON uc.charId = c.id
|
||||||
""")
|
JOIN Card d ON d.id = c.cardId
|
||||||
|
JOIN Sprite s ON s.id = c.spriteId
|
||||||
|
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,
|
||||||
|
s.spriteIdle1 AS spriteIdle,
|
||||||
|
s.spriteIdle2 AS spriteIdle2,
|
||||||
|
s.width AS spriteWidth,
|
||||||
|
s.height AS spriteHeight,
|
||||||
|
c.nameSprite as nameSprite,
|
||||||
|
c.nameWidth as nameSpriteWidth,
|
||||||
|
c.nameHeight as nameSpriteHeight,
|
||||||
|
d.isBEm as isBemCard,
|
||||||
|
a.characterId = uc.id as isInAdventure
|
||||||
|
FROM UserCharacter uc
|
||||||
|
JOIN CardCharacter c ON uc.charId = c.id
|
||||||
|
JOIN Card d ON c.cardId = d.id
|
||||||
|
JOIN Sprite s ON s.id = c.spriteId
|
||||||
|
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
|
||||||
|
|
||||||
|
@Query("SELECT * FROM BECharacterData WHERE id = :id")
|
||||||
|
suspend fun getBeData(id: Long): BECharacterData
|
||||||
|
|
||||||
|
@Query("SELECT * FROM VBCharacterData WHERE id = :id")
|
||||||
|
suspend fun getVbData(id: Long): VBCharacterData
|
||||||
|
|
||||||
|
@Query("SELECT * FROM SpecialMissions WHERE characterId = :id")
|
||||||
|
suspend fun getSpecialMissions(id: Long): List<SpecialMissions>
|
||||||
|
|
||||||
|
@Query(
|
||||||
|
"""
|
||||||
|
SELECT
|
||||||
|
uc.*,
|
||||||
|
c.stage,
|
||||||
|
c.attribute,
|
||||||
|
s.spriteIdle1 AS spriteIdle,
|
||||||
|
s.spriteIdle2 AS spriteIdle2,
|
||||||
|
s.width AS spriteWidth,
|
||||||
|
s.height AS spriteHeight,
|
||||||
|
c.nameSprite as nameSprite,
|
||||||
|
c.nameWidth as nameSpriteWidth,
|
||||||
|
c.nameHeight as nameSpriteHeight,
|
||||||
|
d.isBEm as isBemCard,
|
||||||
|
a.characterId as isInAdventure
|
||||||
|
FROM UserCharacter uc
|
||||||
|
JOIN CardCharacter c ON uc.charId = c.id
|
||||||
|
JOIN Card d ON c.cardId = d.id
|
||||||
|
JOIN Sprite s ON s.id = c.spriteId
|
||||||
|
LEFT JOIN Adventure a ON a.characterId = uc.id
|
||||||
|
WHERE uc.isActive = 1
|
||||||
|
LIMIT 1
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
suspend fun getActiveCharacter(): CharacterDtos.CharacterWithSprites?
|
||||||
|
|
||||||
|
@Query("DELETE FROM UserCharacter WHERE id = :id")
|
||||||
|
fun deleteCharacterById(id: Long)
|
||||||
|
|
||||||
|
@Query("UPDATE UserCharacter SET isActive = 0 WHERE isActive = 1")
|
||||||
|
fun clearActiveCharacter()
|
||||||
|
|
||||||
|
@Query("UPDATE UserCharacter SET isActive = 1 WHERE id = :id")
|
||||||
|
fun setActiveCharacter(id: Long)
|
||||||
|
|
||||||
|
@Query(
|
||||||
|
"""
|
||||||
|
SELECT c.*
|
||||||
|
FROM CardCharacter c
|
||||||
|
join UserCharacter uc on c.id = uc.charId
|
||||||
|
where uc.id = :charId
|
||||||
|
LIMIT 1
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
suspend fun getCharacterInfo(charId: Long): CardCharacter
|
||||||
|
|
||||||
|
|
||||||
|
@Query(
|
||||||
|
"""
|
||||||
|
INSERT INTO TransformationHistory(monId, stageId, transformationDate)
|
||||||
|
VALUES
|
||||||
|
(:monId,
|
||||||
|
(SELECT id FROM CardCharacter WHERE charaIndex = :stage AND cardId = :dimId),
|
||||||
|
:transformationDate)
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
fun insertTransformation(monId: Long, stage: Int, dimId: Long, transformationDate: Long)
|
||||||
|
|
||||||
|
@Upsert
|
||||||
|
fun insertVitals(vararg vitalsHistory: VitalsHistory)
|
||||||
|
|
||||||
|
@Query("""SELECT * FROM VitalsHistory WHERE charId = :charId ORDER BY id ASC""")
|
||||||
|
suspend fun getVitalsHistory(charId: Long): List<VitalsHistory>
|
||||||
|
|
||||||
|
@Query(
|
||||||
|
"""
|
||||||
|
SELECT
|
||||||
|
uc.*,
|
||||||
|
c.stage,
|
||||||
|
c.attribute,
|
||||||
|
s.spriteIdle1 AS spriteIdle,
|
||||||
|
s.spriteIdle2 AS spriteIdle2,
|
||||||
|
s.width AS spriteWidth,
|
||||||
|
s.height AS spriteHeight,
|
||||||
|
c.nameSprite as nameSprite,
|
||||||
|
c.nameWidth as nameSpriteWidth,
|
||||||
|
c.nameHeight as nameSpriteHeight,
|
||||||
|
d.isBEm as isBemCard,
|
||||||
|
a.characterId = uc.id as isInAdventure
|
||||||
|
FROM UserCharacter uc
|
||||||
|
JOIN CardCharacter c ON uc.charId = c.id
|
||||||
|
JOIN Card d ON d.id = c.cardId
|
||||||
|
JOIN Sprite s ON s.id = c.spriteId
|
||||||
|
LEFT JOIN Adventure a ON a.characterId = uc.id
|
||||||
|
WHERE uc.characterType = "BEDevice"
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
suspend fun getBECharacters(): List<CharacterDtos.CharacterWithSprites>
|
||||||
|
|
||||||
|
@Query(
|
||||||
|
"""
|
||||||
|
SELECT
|
||||||
|
uc.*,
|
||||||
|
c.stage,
|
||||||
|
c.attribute,
|
||||||
|
s.spriteIdle1 AS spriteIdle,
|
||||||
|
s.spriteIdle2 AS spriteIdle2,
|
||||||
|
s.width AS spriteWidth,
|
||||||
|
s.height AS spriteHeight,
|
||||||
|
c.nameSprite as nameSprite,
|
||||||
|
c.nameWidth as nameSpriteWidth,
|
||||||
|
c.nameHeight as nameSpriteHeight,
|
||||||
|
d.isBEm as isBemCard,
|
||||||
|
a.characterId = uc.id as isInAdventure
|
||||||
|
FROM UserCharacter uc
|
||||||
|
JOIN CardCharacter c ON uc.charId = c.id
|
||||||
|
JOIN Card d ON d.id = c.cardId
|
||||||
|
JOIN Sprite s ON s.id = c.spriteId
|
||||||
|
LEFT JOIN Adventure a ON a.characterId = uc.id
|
||||||
|
WHERE uc.characterType = "VBDevice"
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
suspend fun getVBDimCharacters(): List<CharacterDtos.CharacterWithSprites>
|
||||||
}
|
}
|
||||||
@@ -2,29 +2,67 @@ 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.CardAdventureDao
|
||||||
import com.github.nacabaro.vbhelper.daos.CharacterDao
|
import com.github.nacabaro.vbhelper.daos.CharacterDao
|
||||||
import com.github.nacabaro.vbhelper.daos.DiMDao
|
import com.github.nacabaro.vbhelper.daos.DexDao
|
||||||
|
import com.github.nacabaro.vbhelper.daos.CardDao
|
||||||
|
import com.github.nacabaro.vbhelper.daos.CardFusionsDao
|
||||||
|
import com.github.nacabaro.vbhelper.daos.CardProgressDao
|
||||||
|
import com.github.nacabaro.vbhelper.daos.ItemDao
|
||||||
|
import com.github.nacabaro.vbhelper.daos.SpecialMissionDao
|
||||||
|
import com.github.nacabaro.vbhelper.daos.SpriteDao
|
||||||
import com.github.nacabaro.vbhelper.daos.UserCharacterDao
|
import com.github.nacabaro.vbhelper.daos.UserCharacterDao
|
||||||
import com.github.nacabaro.vbhelper.domain.Character
|
import com.github.nacabaro.vbhelper.domain.card.Background
|
||||||
import com.github.nacabaro.vbhelper.domain.Dim
|
import com.github.nacabaro.vbhelper.domain.card.CardCharacter
|
||||||
import com.github.nacabaro.vbhelper.domain.Sprites
|
import com.github.nacabaro.vbhelper.domain.card.Card
|
||||||
|
import com.github.nacabaro.vbhelper.domain.card.CardAdventure
|
||||||
|
import com.github.nacabaro.vbhelper.domain.card.CardFusions
|
||||||
|
import com.github.nacabaro.vbhelper.domain.card.CardProgress
|
||||||
|
import com.github.nacabaro.vbhelper.domain.card.PossibleTransformations
|
||||||
|
import com.github.nacabaro.vbhelper.domain.characters.Sprite
|
||||||
|
import com.github.nacabaro.vbhelper.domain.characters.Adventure
|
||||||
|
import com.github.nacabaro.vbhelper.domain.characters.Dex
|
||||||
import com.github.nacabaro.vbhelper.domain.device_data.BECharacterData
|
import com.github.nacabaro.vbhelper.domain.device_data.BECharacterData
|
||||||
|
import com.github.nacabaro.vbhelper.domain.device_data.SpecialMissions
|
||||||
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.device_data.VBCharacterData
|
||||||
|
import com.github.nacabaro.vbhelper.domain.device_data.VitalsHistory
|
||||||
|
import com.github.nacabaro.vbhelper.domain.items.Items
|
||||||
|
|
||||||
@Database(
|
@Database(
|
||||||
version = 1,
|
version = 1,
|
||||||
entities = [
|
entities = [
|
||||||
Dim::class,
|
Card::class,
|
||||||
Character::class,
|
CardProgress::class,
|
||||||
Sprites::class,
|
CardCharacter::class,
|
||||||
|
CardAdventure::class,
|
||||||
|
CardFusions::class,
|
||||||
|
Sprite::class,
|
||||||
UserCharacter::class,
|
UserCharacter::class,
|
||||||
BECharacterData::class,
|
BECharacterData::class,
|
||||||
TransformationHistory::class
|
VBCharacterData::class,
|
||||||
|
SpecialMissions::class,
|
||||||
|
TransformationHistory::class,
|
||||||
|
VitalsHistory::class,
|
||||||
|
Dex::class,
|
||||||
|
Items::class,
|
||||||
|
Adventure::class,
|
||||||
|
Background::class,
|
||||||
|
PossibleTransformations::class
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
abstract class AppDatabase : RoomDatabase() {
|
abstract class AppDatabase : RoomDatabase() {
|
||||||
abstract fun dimDao(): DiMDao
|
abstract fun cardDao(): CardDao
|
||||||
|
abstract fun cardProgressDao(): CardProgressDao
|
||||||
abstract fun characterDao(): CharacterDao
|
abstract fun characterDao(): CharacterDao
|
||||||
abstract fun userCharacterDao(): UserCharacterDao
|
abstract fun userCharacterDao(): UserCharacterDao
|
||||||
|
abstract fun dexDao(): DexDao
|
||||||
|
abstract fun itemDao(): ItemDao
|
||||||
|
abstract fun adventureDao(): AdventureDao
|
||||||
|
abstract fun spriteDao(): SpriteDao
|
||||||
|
abstract fun specialMissionDao(): SpecialMissionDao
|
||||||
|
abstract fun cardAdventureDao(): CardAdventureDao
|
||||||
|
abstract fun cardFusionsDao(): CardFusionsDao
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
package com.github.nacabaro.vbhelper.di
|
package com.github.nacabaro.vbhelper.di
|
||||||
|
|
||||||
import com.github.nacabaro.vbhelper.database.AppDatabase
|
import com.github.nacabaro.vbhelper.database.AppDatabase
|
||||||
|
import com.github.nacabaro.vbhelper.source.DataStoreSecretsRepository
|
||||||
|
|
||||||
interface AppContainer {
|
interface AppContainer {
|
||||||
val db: AppDatabase
|
val db: AppDatabase
|
||||||
|
val dataStoreSecretsRepository: DataStoreSecretsRepository
|
||||||
}
|
}
|
||||||
@@ -1,14 +1,33 @@
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import androidx.datastore.core.DataStore
|
||||||
|
import androidx.datastore.dataStore
|
||||||
import androidx.room.Room
|
import androidx.room.Room
|
||||||
import com.github.nacabaro.vbhelper.database.AppDatabase
|
import com.github.nacabaro.vbhelper.database.AppDatabase
|
||||||
import com.github.nacabaro.vbhelper.di.AppContainer
|
import com.github.nacabaro.vbhelper.di.AppContainer
|
||||||
|
import com.github.nacabaro.vbhelper.source.DataStoreSecretsRepository
|
||||||
|
import com.github.nacabaro.vbhelper.source.SecretsSerializer
|
||||||
|
import com.github.nacabaro.vbhelper.source.proto.Secrets
|
||||||
|
|
||||||
|
private const val SECRETS_DATA_STORE_NAME = "secrets.pb"
|
||||||
|
|
||||||
|
val Context.secretsStore: DataStore<Secrets> by dataStore(
|
||||||
|
fileName = SECRETS_DATA_STORE_NAME,
|
||||||
|
serializer = SecretsSerializer
|
||||||
|
)
|
||||||
|
|
||||||
class DefaultAppContainer(private val context: Context) : AppContainer {
|
class DefaultAppContainer(private val context: Context) : AppContainer {
|
||||||
|
|
||||||
override val db: AppDatabase by lazy {
|
override val db: AppDatabase by lazy {
|
||||||
Room.databaseBuilder(
|
Room.databaseBuilder(
|
||||||
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)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,33 +0,0 @@
|
|||||||
package com.github.nacabaro.vbhelper.domain
|
|
||||||
|
|
||||||
import androidx.room.Entity
|
|
||||||
import androidx.room.PrimaryKey
|
|
||||||
import androidx.room.ForeignKey
|
|
||||||
|
|
||||||
@Entity(
|
|
||||||
foreignKeys = [
|
|
||||||
ForeignKey(
|
|
||||||
entity = Dim::class,
|
|
||||||
parentColumns = ["id"],
|
|
||||||
childColumns = ["dimId"],
|
|
||||||
onDelete = ForeignKey.CASCADE
|
|
||||||
)
|
|
||||||
]
|
|
||||||
)
|
|
||||||
data class Character (
|
|
||||||
@PrimaryKey(autoGenerate = true) val id: Long = 0,
|
|
||||||
val dimId: Long,
|
|
||||||
val monIndex: Int,
|
|
||||||
val name: ByteArray,
|
|
||||||
val stage: Int, // These should be replaced with enums
|
|
||||||
val attribute: Int, // This one too
|
|
||||||
val baseHp: Int,
|
|
||||||
val baseBp: Int,
|
|
||||||
val baseAp: Int,
|
|
||||||
val sprite1: ByteArray,
|
|
||||||
val sprite2: ByteArray,
|
|
||||||
val nameWidth: Int,
|
|
||||||
val nameHeight: Int,
|
|
||||||
val spritesWidth: Int,
|
|
||||||
val spritesHeight: Int
|
|
||||||
)
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
package com.github.nacabaro.vbhelper.domain
|
|
||||||
|
|
||||||
import androidx.room.Entity
|
|
||||||
import androidx.room.ForeignKey
|
|
||||||
import androidx.room.PrimaryKey
|
|
||||||
|
|
||||||
@Entity(
|
|
||||||
foreignKeys = [
|
|
||||||
ForeignKey(
|
|
||||||
entity = User::class,
|
|
||||||
parentColumns = ["id"],
|
|
||||||
childColumns = ["userId"],
|
|
||||||
onDelete = ForeignKey.CASCADE
|
|
||||||
),
|
|
||||||
ForeignKey(
|
|
||||||
entity = Dim::class,
|
|
||||||
parentColumns = ["id"],
|
|
||||||
childColumns = ["dimId"],
|
|
||||||
onDelete = ForeignKey.CASCADE
|
|
||||||
)
|
|
||||||
]
|
|
||||||
)
|
|
||||||
data class DimProgress(
|
|
||||||
@PrimaryKey val dimId: Int,
|
|
||||||
@PrimaryKey val userId: Int,
|
|
||||||
val currentStage: Int,
|
|
||||||
val unlocked: Boolean
|
|
||||||
)
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
package com.github.nacabaro.vbhelper.domain
|
|
||||||
|
|
||||||
import androidx.room.Entity
|
|
||||||
import androidx.room.PrimaryKey
|
|
||||||
|
|
||||||
@Entity
|
|
||||||
data class Sprites(
|
|
||||||
@PrimaryKey(autoGenerate = true) val id : Int,
|
|
||||||
val sprite: ByteArray,
|
|
||||||
val width: Int,
|
|
||||||
val height: Int
|
|
||||||
)
|
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.domain.card
|
||||||
|
|
||||||
|
import androidx.room.Entity
|
||||||
|
import androidx.room.ForeignKey
|
||||||
|
import androidx.room.PrimaryKey
|
||||||
|
|
||||||
|
@Entity(
|
||||||
|
foreignKeys = [
|
||||||
|
ForeignKey(
|
||||||
|
entity = Card::class,
|
||||||
|
parentColumns = ["id"],
|
||||||
|
childColumns = ["cardId"],
|
||||||
|
onDelete = ForeignKey.CASCADE
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
data class Background (
|
||||||
|
@PrimaryKey(autoGenerate = true) val id: Long,
|
||||||
|
val cardId: Long,
|
||||||
|
val background: ByteArray,
|
||||||
|
val backgroundWidth: Int,
|
||||||
|
val backgroundHeight: Int
|
||||||
|
)
|
||||||
+5
-5
@@ -1,17 +1,17 @@
|
|||||||
package com.github.nacabaro.vbhelper.domain
|
package com.github.nacabaro.vbhelper.domain.card
|
||||||
|
|
||||||
import android.icu.text.ListFormatter.Width
|
|
||||||
import androidx.room.Entity
|
import androidx.room.Entity
|
||||||
import androidx.room.PrimaryKey
|
import androidx.room.PrimaryKey
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
data class Dim(
|
data class Card(
|
||||||
@PrimaryKey(autoGenerate = true)
|
@PrimaryKey(autoGenerate = true)
|
||||||
val id: Long = 0,
|
val id: Long = 0,
|
||||||
val dimId: Int,
|
val cardId: Int,
|
||||||
val logo: ByteArray,
|
val logo: ByteArray,
|
||||||
val logoWidth: Int,
|
val logoWidth: Int,
|
||||||
val logoHeight: Int,
|
val logoHeight: Int,
|
||||||
val name: String,
|
val name: String,
|
||||||
val stageCount: Int
|
val stageCount: Int,
|
||||||
|
val isBEm: Boolean
|
||||||
)
|
)
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.domain.card
|
||||||
|
|
||||||
|
import androidx.room.Entity
|
||||||
|
import androidx.room.ForeignKey
|
||||||
|
import androidx.room.PrimaryKey
|
||||||
|
|
||||||
|
@Entity(
|
||||||
|
foreignKeys = [
|
||||||
|
ForeignKey(
|
||||||
|
entity = CardCharacter::class,
|
||||||
|
parentColumns = ["id"],
|
||||||
|
childColumns = ["characterId"],
|
||||||
|
onDelete = ForeignKey.CASCADE
|
||||||
|
),
|
||||||
|
ForeignKey(
|
||||||
|
entity = Card::class,
|
||||||
|
parentColumns = ["id"],
|
||||||
|
childColumns = ["cardId"],
|
||||||
|
onDelete = ForeignKey.CASCADE
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
data class CardAdventure(
|
||||||
|
@PrimaryKey(autoGenerate = true) val id: Long = 0,
|
||||||
|
val cardId: Long,
|
||||||
|
val characterId: Long,
|
||||||
|
val steps: Int,
|
||||||
|
val bossHp: Int,
|
||||||
|
val bossAp: Int,
|
||||||
|
val bossDp: Int,
|
||||||
|
val bossBp: Int?
|
||||||
|
)
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.domain.card
|
||||||
|
|
||||||
|
import androidx.room.Entity
|
||||||
|
import androidx.room.ForeignKey
|
||||||
|
import androidx.room.PrimaryKey
|
||||||
|
import com.github.cfogrady.vbnfc.data.NfcCharacter
|
||||||
|
import com.github.nacabaro.vbhelper.domain.characters.Sprite
|
||||||
|
|
||||||
|
@Entity(
|
||||||
|
foreignKeys = [
|
||||||
|
ForeignKey(
|
||||||
|
entity = Card::class,
|
||||||
|
parentColumns = ["id"],
|
||||||
|
childColumns = ["cardId"],
|
||||||
|
onDelete = ForeignKey.CASCADE
|
||||||
|
),
|
||||||
|
ForeignKey(
|
||||||
|
entity = Sprite::class,
|
||||||
|
parentColumns = ["id"],
|
||||||
|
childColumns = ["spriteId"],
|
||||||
|
onDelete = ForeignKey.CASCADE
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Character represents a character on a 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 CardCharacter (
|
||||||
|
@PrimaryKey(autoGenerate = true) val id: Long = 0,
|
||||||
|
val cardId: Long,
|
||||||
|
val spriteId: Long,
|
||||||
|
val charaIndex: Int,
|
||||||
|
val stage: Int, // These should be replaced with enums
|
||||||
|
val attribute: NfcCharacter.Attribute, // This one too
|
||||||
|
val baseHp: Int,
|
||||||
|
val baseBp: Int,
|
||||||
|
val baseAp: Int,
|
||||||
|
val nameSprite: ByteArray,
|
||||||
|
val nameWidth: Int,
|
||||||
|
val nameHeight: Int
|
||||||
|
)
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.domain.card
|
||||||
|
|
||||||
|
import androidx.room.Entity
|
||||||
|
import androidx.room.ForeignKey
|
||||||
|
import androidx.room.PrimaryKey
|
||||||
|
|
||||||
|
@Entity(
|
||||||
|
foreignKeys = [
|
||||||
|
ForeignKey(
|
||||||
|
entity = CardCharacter::class,
|
||||||
|
parentColumns = ["id"],
|
||||||
|
childColumns = ["fromCharaId"],
|
||||||
|
onDelete = ForeignKey.CASCADE
|
||||||
|
),
|
||||||
|
ForeignKey(
|
||||||
|
entity = CardCharacter::class,
|
||||||
|
parentColumns = ["id"],
|
||||||
|
childColumns = ["attribute1Fusion"],
|
||||||
|
onDelete = ForeignKey.CASCADE
|
||||||
|
),
|
||||||
|
ForeignKey(
|
||||||
|
entity = CardCharacter::class,
|
||||||
|
parentColumns = ["id"],
|
||||||
|
childColumns = ["attribute2Fusion"],
|
||||||
|
onDelete = ForeignKey.CASCADE
|
||||||
|
),
|
||||||
|
ForeignKey(
|
||||||
|
entity = CardCharacter::class,
|
||||||
|
parentColumns = ["id"],
|
||||||
|
childColumns = ["attribute3Fusion"],
|
||||||
|
onDelete = ForeignKey.CASCADE
|
||||||
|
),
|
||||||
|
ForeignKey(
|
||||||
|
entity = CardCharacter::class,
|
||||||
|
parentColumns = ["id"],
|
||||||
|
childColumns = ["attribute4Fusion"],
|
||||||
|
onDelete = ForeignKey.CASCADE
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
data class CardFusions(
|
||||||
|
@PrimaryKey(autoGenerate = true) val id: Long,
|
||||||
|
val fromCharaId: Long,
|
||||||
|
val attribute1Fusion: Long?,
|
||||||
|
val attribute2Fusion: Long?,
|
||||||
|
val attribute3Fusion: Long?,
|
||||||
|
val attribute4Fusion: Long?
|
||||||
|
)
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.domain.card
|
||||||
|
|
||||||
|
import androidx.room.Entity
|
||||||
|
import androidx.room.ForeignKey
|
||||||
|
import androidx.room.PrimaryKey
|
||||||
|
|
||||||
|
@Entity(
|
||||||
|
foreignKeys = [
|
||||||
|
ForeignKey(
|
||||||
|
entity = Card::class,
|
||||||
|
parentColumns = ["id"],
|
||||||
|
childColumns = ["cardId"],
|
||||||
|
onDelete = ForeignKey.CASCADE
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
data class CardProgress(
|
||||||
|
@PrimaryKey val cardId: Long,
|
||||||
|
val currentStage: Int,
|
||||||
|
val unlocked: Boolean
|
||||||
|
)
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.domain.card
|
||||||
|
|
||||||
|
import androidx.room.Entity
|
||||||
|
import androidx.room.ForeignKey
|
||||||
|
import androidx.room.PrimaryKey
|
||||||
|
|
||||||
|
@Entity(
|
||||||
|
foreignKeys = [
|
||||||
|
ForeignKey(
|
||||||
|
entity = CardCharacter::class,
|
||||||
|
parentColumns = ["id"],
|
||||||
|
childColumns = ["charaId"],
|
||||||
|
onDelete = ForeignKey.CASCADE
|
||||||
|
),
|
||||||
|
ForeignKey(
|
||||||
|
entity = CardCharacter::class,
|
||||||
|
parentColumns = ["id"],
|
||||||
|
childColumns = ["toCharaId"],
|
||||||
|
onDelete = ForeignKey.CASCADE
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
data class PossibleTransformations (
|
||||||
|
@PrimaryKey(autoGenerate = true) val id: Long = 0,
|
||||||
|
var charaId: Long,
|
||||||
|
val requiredVitals: Int,
|
||||||
|
val requiredTrophies: Int,
|
||||||
|
val requiredBattles: Int,
|
||||||
|
val requiredWinRate: Int,
|
||||||
|
val changeTimerHours: Int,
|
||||||
|
val requiredAdventureLevelCompleted: Int,
|
||||||
|
val toCharaId: Long?
|
||||||
|
)
|
||||||
@@ -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
|
||||||
|
)
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.domain.characters
|
||||||
|
|
||||||
|
import androidx.room.Entity
|
||||||
|
import androidx.room.ForeignKey
|
||||||
|
import androidx.room.PrimaryKey
|
||||||
|
import com.github.nacabaro.vbhelper.domain.card.CardCharacter
|
||||||
|
|
||||||
|
@Entity(
|
||||||
|
foreignKeys = [
|
||||||
|
ForeignKey(
|
||||||
|
entity = CardCharacter::class,
|
||||||
|
parentColumns = ["id"],
|
||||||
|
childColumns = ["id"],
|
||||||
|
onDelete = ForeignKey.CASCADE
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
data class Dex(
|
||||||
|
@PrimaryKey val id: Long = 0,
|
||||||
|
val discoveredOn: Long
|
||||||
|
)
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.domain.characters
|
||||||
|
|
||||||
|
import androidx.room.Entity
|
||||||
|
import androidx.room.PrimaryKey
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
data class Sprite(
|
||||||
|
@PrimaryKey(autoGenerate = true) val id : Int = 0,
|
||||||
|
val spriteIdle1: ByteArray,
|
||||||
|
val spriteIdle2: ByteArray,
|
||||||
|
val spriteWalk1: ByteArray,
|
||||||
|
val spriteWalk2: ByteArray,
|
||||||
|
val spriteRun1: ByteArray,
|
||||||
|
val spriteRun2: ByteArray,
|
||||||
|
val spriteTrain1: ByteArray,
|
||||||
|
val spriteTrain2: ByteArray,
|
||||||
|
val spriteHappy: ByteArray,
|
||||||
|
val spriteSleep: ByteArray,
|
||||||
|
val spriteAttack: ByteArray,
|
||||||
|
val spriteDodge: ByteArray,
|
||||||
|
val width: Int,
|
||||||
|
val height: 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,
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.domain.device_data
|
||||||
|
|
||||||
|
import androidx.room.Entity
|
||||||
|
import androidx.room.ForeignKey
|
||||||
|
import androidx.room.PrimaryKey
|
||||||
|
import com.github.cfogrady.vbnfc.vb.SpecialMission
|
||||||
|
|
||||||
|
@Entity(
|
||||||
|
foreignKeys = [
|
||||||
|
ForeignKey(
|
||||||
|
entity = UserCharacter::class,
|
||||||
|
parentColumns = ["id"],
|
||||||
|
childColumns = ["characterId"],
|
||||||
|
onDelete = ForeignKey.CASCADE
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
data class SpecialMissions (
|
||||||
|
@PrimaryKey(autoGenerate = true) var id: Long = 0,
|
||||||
|
var characterId: Long,
|
||||||
|
var goal: Int,
|
||||||
|
val watchId: Int,
|
||||||
|
val progress: Int,
|
||||||
|
val status: SpecialMission.Status,
|
||||||
|
val timeElapsedInMinutes: Int,
|
||||||
|
val timeLimitInMinutes: Int,
|
||||||
|
val missionType: SpecialMission.Type
|
||||||
|
)
|
||||||
+9
-4
@@ -3,6 +3,7 @@ package com.github.nacabaro.vbhelper.domain.device_data
|
|||||||
import androidx.room.Entity
|
import androidx.room.Entity
|
||||||
import androidx.room.ForeignKey
|
import androidx.room.ForeignKey
|
||||||
import androidx.room.PrimaryKey
|
import androidx.room.PrimaryKey
|
||||||
|
import com.github.nacabaro.vbhelper.domain.card.CardCharacter
|
||||||
|
|
||||||
@Entity(
|
@Entity(
|
||||||
foreignKeys = [
|
foreignKeys = [
|
||||||
@@ -11,14 +12,18 @@ import androidx.room.PrimaryKey
|
|||||||
parentColumns = ["id"],
|
parentColumns = ["id"],
|
||||||
childColumns = ["monId"],
|
childColumns = ["monId"],
|
||||||
onDelete = ForeignKey.CASCADE
|
onDelete = ForeignKey.CASCADE
|
||||||
|
),
|
||||||
|
ForeignKey(
|
||||||
|
entity = CardCharacter::class,
|
||||||
|
parentColumns = ["id"],
|
||||||
|
childColumns = ["stageId"],
|
||||||
|
onDelete = ForeignKey.CASCADE
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
data class TransformationHistory (
|
data class TransformationHistory (
|
||||||
@PrimaryKey(autoGenerate = true) val id: Long = 0,
|
@PrimaryKey(autoGenerate = true) val id: Long = 0,
|
||||||
val monId: Long,
|
val monId: Long,
|
||||||
val toCharIndex: Int,
|
val stageId: Long,
|
||||||
val year: Int,
|
val transformationDate: Long
|
||||||
val month: Int,
|
|
||||||
val day: Int
|
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -4,26 +4,26 @@ import androidx.room.Entity
|
|||||||
import androidx.room.ForeignKey
|
import androidx.room.ForeignKey
|
||||||
import androidx.room.PrimaryKey
|
import androidx.room.PrimaryKey
|
||||||
import com.github.cfogrady.vbnfc.data.NfcCharacter
|
import com.github.cfogrady.vbnfc.data.NfcCharacter
|
||||||
import com.github.nacabaro.vbhelper.domain.DeviceType
|
import com.github.nacabaro.vbhelper.utils.DeviceType
|
||||||
import com.github.nacabaro.vbhelper.domain.Character
|
import com.github.nacabaro.vbhelper.domain.card.CardCharacter
|
||||||
|
|
||||||
@Entity(
|
@Entity(
|
||||||
foreignKeys = [
|
foreignKeys = [
|
||||||
ForeignKey(
|
ForeignKey(
|
||||||
entity = Character::class,
|
entity = CardCharacter::class,
|
||||||
parentColumns = ["id"],
|
parentColumns = ["id"],
|
||||||
childColumns = ["charId"],
|
childColumns = ["charId"],
|
||||||
onDelete = ForeignKey.CASCADE
|
onDelete = ForeignKey.CASCADE
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
/**
|
||||||
|
* 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 mood: Int,
|
var mood: Int,
|
||||||
var vitalPoints: Int,
|
var vitalPoints: Int,
|
||||||
var transformationCountdown: Int,
|
var transformationCountdown: Int,
|
||||||
@@ -35,5 +35,6 @@ data class UserCharacter (
|
|||||||
var totalBattlesLost: Int,
|
var totalBattlesLost: Int,
|
||||||
var activityLevel: Int,
|
var activityLevel: Int,
|
||||||
var heartRateCurrent: Int,
|
var heartRateCurrent: Int,
|
||||||
var characterType: DeviceType
|
var characterType: DeviceType,
|
||||||
|
var isActive: Boolean
|
||||||
)
|
)
|
||||||
+19
-2
@@ -1,4 +1,21 @@
|
|||||||
package com.github.nacabaro.vbhelper.domain.device_data
|
package com.github.nacabaro.vbhelper.domain.device_data
|
||||||
|
|
||||||
class VBCharacterData {
|
import androidx.room.Entity
|
||||||
}
|
import androidx.room.ForeignKey
|
||||||
|
import androidx.room.PrimaryKey
|
||||||
|
|
||||||
|
@Entity(
|
||||||
|
foreignKeys = [
|
||||||
|
ForeignKey(
|
||||||
|
entity = UserCharacter::class,
|
||||||
|
parentColumns = ["id"],
|
||||||
|
childColumns = ["id"],
|
||||||
|
onDelete = ForeignKey.CASCADE
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
data class VBCharacterData (
|
||||||
|
@PrimaryKey val id: Long,
|
||||||
|
val generation: Int,
|
||||||
|
val totalTrophies: Int
|
||||||
|
)
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.domain.device_data
|
||||||
|
|
||||||
|
import androidx.room.Entity
|
||||||
|
import androidx.room.ForeignKey
|
||||||
|
import androidx.room.PrimaryKey
|
||||||
|
|
||||||
|
@Entity(
|
||||||
|
foreignKeys = [
|
||||||
|
ForeignKey(
|
||||||
|
entity = UserCharacter::class,
|
||||||
|
parentColumns = ["id"],
|
||||||
|
childColumns = ["charId"],
|
||||||
|
onDelete = ForeignKey.CASCADE
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
data class VitalsHistory (
|
||||||
|
@PrimaryKey(autoGenerate = true) val id: Long = 0,
|
||||||
|
val charId: Long,
|
||||||
|
val year: Int,
|
||||||
|
val month: Int,
|
||||||
|
val day: Int,
|
||||||
|
val vitalPoints: Int
|
||||||
|
)
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.domain.items
|
||||||
|
|
||||||
|
import androidx.room.Entity
|
||||||
|
import androidx.room.PrimaryKey
|
||||||
|
|
||||||
|
enum class ItemType {
|
||||||
|
VBITEM,
|
||||||
|
BEITEM,
|
||||||
|
UNIVERSAL,
|
||||||
|
SPECIALMISSION
|
||||||
|
}
|
||||||
|
|
||||||
|
@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,
|
||||||
|
val itemType: ItemType
|
||||||
|
)
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.dtos
|
||||||
|
|
||||||
|
object CardDtos {
|
||||||
|
data class CardProgress (
|
||||||
|
val cardId: Long,
|
||||||
|
val cardName: String,
|
||||||
|
val cardLogo: ByteArray,
|
||||||
|
val logoWidth: Int,
|
||||||
|
val logoHeight: Int,
|
||||||
|
val totalCharacters: Int,
|
||||||
|
val obtainedCharacters: Int,
|
||||||
|
)
|
||||||
|
|
||||||
|
data class CardAdventureWithSprites (
|
||||||
|
val characterName: ByteArray,
|
||||||
|
val characterNameWidth: Int,
|
||||||
|
val characterNameHeight: Int,
|
||||||
|
val characterIdleSprite: ByteArray,
|
||||||
|
val characterIdleSpriteWidth: Int,
|
||||||
|
val characterIdleSpriteHeight: Int,
|
||||||
|
val characterAp: Int,
|
||||||
|
val characterBp: Int?,
|
||||||
|
val characterDp: Int,
|
||||||
|
val characterHp: Int,
|
||||||
|
val steps: Int,
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,8 +1,7 @@
|
|||||||
package com.github.nacabaro.vbhelper.dtos
|
package com.github.nacabaro.vbhelper.dtos
|
||||||
|
|
||||||
import androidx.room.PrimaryKey
|
|
||||||
import com.github.cfogrady.vbnfc.data.NfcCharacter
|
import com.github.cfogrady.vbnfc.data.NfcCharacter
|
||||||
import com.github.nacabaro.vbhelper.domain.DeviceType
|
import com.github.nacabaro.vbhelper.utils.DeviceType
|
||||||
|
|
||||||
|
|
||||||
object CharacterDtos {
|
object CharacterDtos {
|
||||||
@@ -12,7 +11,74 @@ object CharacterDtos {
|
|||||||
var stage: Int,
|
var stage: Int,
|
||||||
var attribute: NfcCharacter.Attribute,
|
var attribute: NfcCharacter.Attribute,
|
||||||
var ageInDays: Int,
|
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 spriteIdle2: ByteArray,
|
||||||
|
val spriteWidth: Int,
|
||||||
|
val spriteHeight: Int,
|
||||||
|
val nameSprite: ByteArray,
|
||||||
|
val nameSpriteWidth: Int,
|
||||||
|
val nameSpriteHeight: Int,
|
||||||
|
val isBemCard: Boolean,
|
||||||
|
val isInAdventure: Boolean
|
||||||
|
)
|
||||||
|
|
||||||
|
data class CardCharacterInfo(
|
||||||
|
val cardId: Long,
|
||||||
|
val charId: Int,
|
||||||
|
val stage: Int,
|
||||||
|
val attribute: NfcCharacter.Attribute
|
||||||
|
)
|
||||||
|
|
||||||
|
data class TransformationHistory(
|
||||||
|
val id: Long,
|
||||||
|
val spriteIdle: ByteArray,
|
||||||
|
val spriteWidth: Int,
|
||||||
|
val spriteHeight: Int,
|
||||||
|
val monIndex: Int,
|
||||||
|
val transformationDate: Long
|
||||||
|
)
|
||||||
|
|
||||||
|
data class CardCharaProgress(
|
||||||
|
val id: Long,
|
||||||
|
val spriteIdle: ByteArray,
|
||||||
|
val spriteWidth: Int,
|
||||||
|
val spriteHeight: Int,
|
||||||
|
val nameSprite: ByteArray,
|
||||||
|
val nameSpriteWidth: Int,
|
||||||
|
val nameSpriteHeight: Int,
|
||||||
|
val discoveredOn: Long?,
|
||||||
|
val baseHp: Int,
|
||||||
|
val baseBp: Int,
|
||||||
|
val baseAp: Int,
|
||||||
|
val stage: Int,
|
||||||
|
val attribute: NfcCharacter.Attribute,
|
||||||
|
)
|
||||||
|
|
||||||
|
data class CardProgress(
|
||||||
|
val id: Long,
|
||||||
|
val spriteIdle: ByteArray,
|
||||||
|
val spriteWidth: Int,
|
||||||
|
val spriteHeight: Int,
|
||||||
|
)
|
||||||
|
|
||||||
|
data class AdventureCharacterWithSprites(
|
||||||
|
var id: Long = 0,
|
||||||
|
var charId: Long,
|
||||||
|
var stage: Int,
|
||||||
|
var attribute: NfcCharacter.Attribute,
|
||||||
|
var ageInDays: Int,
|
||||||
var mood: Int,
|
var mood: Int,
|
||||||
var vitalPoints: Int,
|
var vitalPoints: Int,
|
||||||
var transformationCountdown: Int,
|
var transformationCountdown: Int,
|
||||||
@@ -27,6 +93,24 @@ object CharacterDtos {
|
|||||||
var characterType: DeviceType,
|
var characterType: DeviceType,
|
||||||
val spriteIdle: ByteArray,
|
val spriteIdle: ByteArray,
|
||||||
val spriteWidth: Int,
|
val spriteWidth: Int,
|
||||||
val spriteHeight: Int
|
val spriteHeight: Int,
|
||||||
|
val isBemCard: Boolean,
|
||||||
|
val finishesAdventure: Long,
|
||||||
|
val originalTimeInMinutes: Long
|
||||||
|
)
|
||||||
|
|
||||||
|
data class EvolutionRequirementsWithSpritesAndObtained(
|
||||||
|
val charaId: Long,
|
||||||
|
val fromCharaId: Long,
|
||||||
|
val spriteIdle: ByteArray,
|
||||||
|
val spriteWidth: Int,
|
||||||
|
val spriteHeight: Int,
|
||||||
|
val discoveredOn: Long?,
|
||||||
|
val requiredTrophies: Int,
|
||||||
|
val requiredVitals: Int,
|
||||||
|
val requiredBattles: Int,
|
||||||
|
val requiredWinRate: Int,
|
||||||
|
val changeTimerHours: Int,
|
||||||
|
val requiredAdventureLevelCompleted: Int
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.dtos
|
||||||
|
|
||||||
|
import com.github.nacabaro.vbhelper.domain.items.ItemType
|
||||||
|
|
||||||
|
|
||||||
|
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,
|
||||||
|
val itemType: ItemType
|
||||||
|
)
|
||||||
|
|
||||||
|
data class PurchasedItem(
|
||||||
|
val itemId: Long,
|
||||||
|
val itemName: String,
|
||||||
|
val itemDescription: String,
|
||||||
|
val itemIcon: Int,
|
||||||
|
val itemLength: Int,
|
||||||
|
val itemAmount: Int,
|
||||||
|
val itemType: ItemType
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,28 +1,60 @@
|
|||||||
package com.github.nacabaro.vbhelper.navigation
|
package com.github.nacabaro.vbhelper.navigation
|
||||||
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import androidx.compose.animation.core.tween
|
||||||
|
import androidx.compose.animation.fadeIn
|
||||||
|
import androidx.compose.animation.fadeOut
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.navigation.compose.NavHost
|
import androidx.navigation.compose.NavHost
|
||||||
import androidx.navigation.compose.composable
|
import androidx.navigation.compose.composable
|
||||||
import androidx.navigation.compose.rememberNavController
|
import androidx.navigation.compose.rememberNavController
|
||||||
|
import com.github.nacabaro.vbhelper.di.VBHelper
|
||||||
import com.github.nacabaro.vbhelper.screens.BattlesScreen
|
import com.github.nacabaro.vbhelper.screens.BattlesScreen
|
||||||
import com.github.nacabaro.vbhelper.screens.DexScreen
|
import com.github.nacabaro.vbhelper.screens.cardScreen.CardsScreen
|
||||||
import com.github.nacabaro.vbhelper.screens.DiMScreen
|
import com.github.nacabaro.vbhelper.screens.cardScreen.CardViewScreen
|
||||||
import com.github.nacabaro.vbhelper.screens.HomeScreen
|
import com.github.nacabaro.vbhelper.screens.homeScreens.HomeScreen
|
||||||
import com.github.nacabaro.vbhelper.screens.ScanScreen
|
import com.github.nacabaro.vbhelper.screens.itemsScreen.ItemsScreen
|
||||||
import com.github.nacabaro.vbhelper.screens.SettingsScreen
|
import com.github.nacabaro.vbhelper.screens.scanScreen.ScanScreen
|
||||||
import com.github.nacabaro.vbhelper.screens.SpriteViewer
|
import com.github.nacabaro.vbhelper.screens.scanScreen.ScanScreenControllerImpl
|
||||||
import com.github.nacabaro.vbhelper.screens.StorageScreen
|
import com.github.nacabaro.vbhelper.screens.settingsScreen.SettingsScreen
|
||||||
|
import com.github.nacabaro.vbhelper.screens.spriteViewer.SpriteViewer
|
||||||
|
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.cardScreen.CardAdventureScreen
|
||||||
|
import com.github.nacabaro.vbhelper.screens.cardScreen.CardScreenControllerImpl
|
||||||
|
import com.github.nacabaro.vbhelper.screens.settingsScreen.CreditsScreen
|
||||||
|
import com.github.nacabaro.vbhelper.screens.spriteViewer.SpriteViewerControllerImpl
|
||||||
|
import com.github.nacabaro.vbhelper.screens.storageScreen.StorageScreenControllerImpl
|
||||||
|
import com.github.nacabaro.vbhelper.source.StorageRepository
|
||||||
|
|
||||||
|
data class AppNavigationHandlers(
|
||||||
|
val settingsScreenController: SettingsScreenControllerImpl,
|
||||||
|
val scanScreenController: ScanScreenControllerImpl,
|
||||||
|
val itemsScreenController: ItemsScreenControllerImpl,
|
||||||
|
val adventureScreenController: AdventureScreenControllerImpl,
|
||||||
|
val storageScreenController: StorageScreenControllerImpl,
|
||||||
|
val homeScreenController: HomeScreenControllerImpl,
|
||||||
|
val spriteViewerController: SpriteViewerControllerImpl,
|
||||||
|
val cardScreenController: CardScreenControllerImpl
|
||||||
|
)
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun AppNavigation(
|
fun AppNavigation(
|
||||||
onClickRead: () -> Unit,
|
applicationNavigationHandlers: AppNavigationHandlers,
|
||||||
onClickScan: () -> Unit,
|
|
||||||
onClickImportCard: () -> Unit,
|
|
||||||
isDoneReadingCharacter: Boolean
|
|
||||||
) {
|
) {
|
||||||
val navController = rememberNavController()
|
val navController = rememberNavController()
|
||||||
|
|
||||||
@@ -33,52 +65,127 @@ fun AppNavigation(
|
|||||||
) { contentPadding ->
|
) { contentPadding ->
|
||||||
NavHost(
|
NavHost(
|
||||||
navController = navController,
|
navController = navController,
|
||||||
startDestination = BottomNavItem.Home.route,
|
startDestination = NavigationItems.Home.route,
|
||||||
|
enterTransition = {
|
||||||
|
fadeIn(
|
||||||
|
animationSpec = tween(200)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
exitTransition = {
|
||||||
|
fadeOut(
|
||||||
|
animationSpec = tween(200)
|
||||||
|
)
|
||||||
|
},
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(contentPadding)
|
.padding(contentPadding)
|
||||||
|
|
||||||
) {
|
) {
|
||||||
composable(BottomNavItem.Battles.route) {
|
composable(NavigationItems.Battles.route) {
|
||||||
BattlesScreen()
|
BattlesScreen()
|
||||||
}
|
}
|
||||||
composable(BottomNavItem.Home.route) {
|
composable(NavigationItems.Home.route) {
|
||||||
HomeScreen(
|
HomeScreen(
|
||||||
navController = navController
|
navController = navController,
|
||||||
|
homeScreenController = applicationNavigationHandlers.homeScreenController
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
composable(BottomNavItem.Storage.route) {
|
composable(NavigationItems.Storage.route) {
|
||||||
StorageScreen()
|
StorageScreen(
|
||||||
|
navController = navController,
|
||||||
|
adventureScreenController = applicationNavigationHandlers.adventureScreenController,
|
||||||
|
storageScreenController = applicationNavigationHandlers.storageScreenController
|
||||||
|
)
|
||||||
}
|
}
|
||||||
composable(BottomNavItem.Scan.route) {
|
composable(NavigationItems.Scan.route) {
|
||||||
onClickScan()
|
val characterIdString = it.arguments?.getString("characterId")
|
||||||
|
var characterId by remember { mutableStateOf(characterIdString?.toLongOrNull()) }
|
||||||
|
Log.d("ScanScreen", "characterId: $characterId")
|
||||||
|
val launchedFromHomeScreen = (characterIdString?.toLongOrNull() == null)
|
||||||
|
|
||||||
|
if (characterId == null) {
|
||||||
|
val context = LocalContext.current.applicationContext as VBHelper
|
||||||
|
val storageRepository = StorageRepository(context.container.db)
|
||||||
|
|
||||||
|
LaunchedEffect(characterId) {
|
||||||
|
if (characterId == null) {
|
||||||
|
val characterData = storageRepository.getActiveCharacter()
|
||||||
|
if (characterData != null) {
|
||||||
|
characterId = characterData.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ScanScreen(
|
ScanScreen(
|
||||||
navController = navController,
|
navController = navController,
|
||||||
onClickRead = onClickRead,
|
scanScreenController = applicationNavigationHandlers.scanScreenController,
|
||||||
isDoneReadingCharacter = isDoneReadingCharacter
|
characterId = characterId,
|
||||||
|
launchedFromHomeScreen = launchedFromHomeScreen
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
composable(BottomNavItem.Dex.route) {
|
composable(NavigationItems.Dex.route) {
|
||||||
DexScreen(
|
CardsScreen(
|
||||||
navController = navController
|
navController = navController,
|
||||||
|
cardScreenController = applicationNavigationHandlers.cardScreenController
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
composable(BottomNavItem.Settings.route) {
|
composable(NavigationItems.Settings.route) {
|
||||||
SettingsScreen(
|
SettingsScreen(
|
||||||
navController = navController,
|
navController = navController,
|
||||||
onClickImportCard = onClickImportCard
|
settingsScreenController = applicationNavigationHandlers.settingsScreenController
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
composable(BottomNavItem.Viewer.route) {
|
composable(NavigationItems.Viewer.route) {
|
||||||
SpriteViewer(
|
SpriteViewer(
|
||||||
|
navController = navController,
|
||||||
|
spriteViewerController = applicationNavigationHandlers.spriteViewerController
|
||||||
|
)
|
||||||
|
}
|
||||||
|
composable(NavigationItems.CardView.route) {
|
||||||
|
val cardId = it.arguments?.getString("cardId")
|
||||||
|
if (cardId != null) {
|
||||||
|
CardViewScreen(
|
||||||
|
navController = navController,
|
||||||
|
cardId = cardId.toLong()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
composable(NavigationItems.Items.route) {
|
||||||
|
ItemsScreen(
|
||||||
navController = navController
|
navController = navController
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
composable(BottomNavItem.CardView.route) {
|
composable(NavigationItems.ApplyItem.route) {
|
||||||
val dimId = it.arguments?.getString("dimId")
|
val itemId = it.arguments?.getString("itemId")
|
||||||
Log.d("dimId", dimId.toString())
|
if (itemId != null) {
|
||||||
if (dimId != null) {
|
ChooseCharacterScreen(
|
||||||
DiMScreen(
|
itemsScreenController = applicationNavigationHandlers
|
||||||
|
.itemsScreenController,
|
||||||
navController = navController,
|
navController = navController,
|
||||||
dimId = dimId.toInt()
|
itemId = itemId.toLong()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
composable(NavigationItems.Adventure.route) {
|
||||||
|
AdventureScreen(
|
||||||
|
navController = navController,
|
||||||
|
storageScreenController = applicationNavigationHandlers
|
||||||
|
.adventureScreenController
|
||||||
|
)
|
||||||
|
}
|
||||||
|
composable(NavigationItems.Credits.route) {
|
||||||
|
CreditsScreen(
|
||||||
|
navController = navController
|
||||||
|
)
|
||||||
|
}
|
||||||
|
composable(NavigationItems.CardAdventure.route) {
|
||||||
|
val cardId = it.arguments?.getString("cardId")
|
||||||
|
if (cardId != null) {
|
||||||
|
CardAdventureScreen(
|
||||||
|
navController = navController,
|
||||||
|
cardId = cardId.toLong(),
|
||||||
|
cardScreenController = applicationNavigationHandlers
|
||||||
|
.cardScreenController
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
package com.github.nacabaro.vbhelper.navigation
|
|
||||||
|
|
||||||
import com.github.nacabaro.vbhelper.R
|
|
||||||
|
|
||||||
sealed class BottomNavItem (
|
|
||||||
var route: String,
|
|
||||||
var icon: Int,
|
|
||||||
var label: String
|
|
||||||
) {
|
|
||||||
object Scan : BottomNavItem("Scan", R.drawable.baseline_nfc_24, "Scan")
|
|
||||||
object Battles : BottomNavItem("Battle", R.drawable.baseline_swords_24, "Battle")
|
|
||||||
object Home : BottomNavItem("Home", R.drawable.baseline_cottage_24, "Home")
|
|
||||||
object Dex : BottomNavItem("Dex", R.drawable.baseline_menu_book_24, "Dex")
|
|
||||||
object Storage : BottomNavItem("Storage", R.drawable.baseline_catching_pokemon_24, "Storage")
|
|
||||||
object Settings : BottomNavItem("Settings", R.drawable.baseline_settings_24, "Settings")
|
|
||||||
object Viewer : BottomNavItem("Viewer", R.drawable.baseline_image_24, "Viewer")
|
|
||||||
object CardView : BottomNavItem("Card/{dimId}", R.drawable.baseline_image_24, "Card")
|
|
||||||
}
|
|
||||||
@@ -13,12 +13,13 @@ import androidx.navigation.compose.currentBackStackEntryAsState
|
|||||||
@Composable
|
@Composable
|
||||||
fun BottomNavigationBar(navController: NavController) {
|
fun BottomNavigationBar(navController: NavController) {
|
||||||
val items = listOf(
|
val items = listOf(
|
||||||
BottomNavItem.Scan,
|
NavigationItems.Items,
|
||||||
BottomNavItem.Battles,
|
NavigationItems.Battles,
|
||||||
BottomNavItem.Home,
|
NavigationItems.Home,
|
||||||
BottomNavItem.Dex,
|
NavigationItems.Dex,
|
||||||
BottomNavItem.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
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.navigation
|
||||||
|
|
||||||
|
import com.github.nacabaro.vbhelper.R
|
||||||
|
|
||||||
|
sealed class NavigationItems (
|
||||||
|
var route: String,
|
||||||
|
var icon: Int,
|
||||||
|
var label: String
|
||||||
|
) {
|
||||||
|
object Scan : NavigationItems("Scan/{characterId}", R.drawable.baseline_nfc_24, "Scan")
|
||||||
|
object Battles : NavigationItems("Battle", R.drawable.baseline_swords_24, "Battle")
|
||||||
|
object Home : NavigationItems("Home", R.drawable.baseline_cottage_24, "Home")
|
||||||
|
object Dex : NavigationItems("Dex", R.drawable.baseline_menu_book_24, "Dex")
|
||||||
|
object CardAdventure : NavigationItems("CardAdventure/{cardId}", R.drawable.baseline_fort_24, "Card adventure")
|
||||||
|
object Storage : NavigationItems("Storage", R.drawable.baseline_catching_pokemon_24, "Storage")
|
||||||
|
object Settings : NavigationItems("Settings", R.drawable.baseline_settings_24, "Settings")
|
||||||
|
object Viewer : NavigationItems("Viewer", R.drawable.baseline_image_24, "Viewer")
|
||||||
|
object CardView : NavigationItems("Card/{cardId}", R.drawable.baseline_image_24, "Card")
|
||||||
|
object Items : NavigationItems("Items", R.drawable.baseline_data_24, "Items")
|
||||||
|
object MyItems : NavigationItems("MyItems", R.drawable.baseline_data_24, "My items")
|
||||||
|
object ItemsStore : NavigationItems("ItemsStore", R.drawable.baseline_data_24, "Items store")
|
||||||
|
object ApplyItem : NavigationItems("ApplyItem/{itemId}", R.drawable.baseline_data_24, "Apply item")
|
||||||
|
object Adventure : NavigationItems("Adventure", R.drawable.baseline_fort_24, "Adventure")
|
||||||
|
object Credits : NavigationItems("Credits", R.drawable.baseline_data_24, "Credits")
|
||||||
|
}
|
||||||
@@ -1,9 +1,33 @@
|
|||||||
package com.github.nacabaro.vbhelper.screens
|
package com.github.nacabaro.vbhelper.screens
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import com.github.nacabaro.vbhelper.components.TopBanner
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun BattlesScreen() {
|
fun BattlesScreen() {
|
||||||
Text("Battles Screen")
|
Scaffold (
|
||||||
|
topBar = {
|
||||||
|
TopBanner(
|
||||||
|
text = "Online battles"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
) { contentPadding ->
|
||||||
|
Column(
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
verticalArrangement = Arrangement.Center,
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(top = contentPadding.calculateTopPadding())
|
||||||
|
.fillMaxSize()
|
||||||
|
) {
|
||||||
|
Text("Coming soon")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,81 +0,0 @@
|
|||||||
package com.github.nacabaro.vbhelper.screens
|
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
|
||||||
import androidx.compose.foundation.layout.padding
|
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
|
||||||
import androidx.compose.foundation.lazy.items
|
|
||||||
import androidx.compose.material3.Scaffold
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
|
||||||
import androidx.compose.runtime.mutableStateOf
|
|
||||||
import androidx.compose.runtime.remember
|
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
|
||||||
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.utils.BitmapData
|
|
||||||
import com.github.nacabaro.vbhelper.components.DexDiMEntry
|
|
||||||
import com.github.nacabaro.vbhelper.components.TopBanner
|
|
||||||
import com.github.nacabaro.vbhelper.di.VBHelper
|
|
||||||
import com.github.nacabaro.vbhelper.domain.Dim
|
|
||||||
import com.github.nacabaro.vbhelper.navigation.BottomNavItem
|
|
||||||
import com.github.nacabaro.vbhelper.source.DexRepository
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun DexScreen(
|
|
||||||
navController: NavController
|
|
||||||
) {
|
|
||||||
val coroutineScope = rememberCoroutineScope()
|
|
||||||
val application = LocalContext.current.applicationContext as VBHelper
|
|
||||||
val dexRepository = DexRepository(application.container.db)
|
|
||||||
|
|
||||||
val dimList = remember { mutableStateOf<List<Dim>>(emptyList()) }
|
|
||||||
|
|
||||||
LaunchedEffect(dexRepository) {
|
|
||||||
coroutineScope.launch {
|
|
||||||
val newDimList = dexRepository.getAllDims()
|
|
||||||
dimList.value = newDimList // Replace the entire list atomically
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Scaffold (
|
|
||||||
topBar = {
|
|
||||||
TopBanner(
|
|
||||||
text = "Discovered Digimon",
|
|
||||||
onGearClick = {
|
|
||||||
navController.navigate(BottomNavItem.Viewer.route)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
) { contentPadding ->
|
|
||||||
LazyColumn (
|
|
||||||
modifier = Modifier
|
|
||||||
.padding(top = contentPadding.calculateTopPadding())
|
|
||||||
) {
|
|
||||||
items(dimList.value) {
|
|
||||||
DexDiMEntry(
|
|
||||||
name = it.name,
|
|
||||||
logo = BitmapData(
|
|
||||||
bitmap = it.logo,
|
|
||||||
width = it.logoWidth,
|
|
||||||
height = it.logoHeight
|
|
||||||
),
|
|
||||||
onClick = {
|
|
||||||
navController
|
|
||||||
.navigate(
|
|
||||||
BottomNavItem
|
|
||||||
.CardView.route
|
|
||||||
.replace("{dimId}", "${it.id}")
|
|
||||||
)
|
|
||||||
},
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.padding(8.dp)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
package com.github.nacabaro.vbhelper.screens
|
|
||||||
|
|
||||||
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.mutableStateOf
|
|
||||||
import androidx.compose.runtime.remember
|
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
|
||||||
import androidx.compose.ui.platform.LocalContext
|
|
||||||
import androidx.navigation.NavController
|
|
||||||
import com.github.nacabaro.vbhelper.utils.BitmapData
|
|
||||||
import com.github.nacabaro.vbhelper.components.CharacterEntry
|
|
||||||
import com.github.nacabaro.vbhelper.components.TopBanner
|
|
||||||
import com.github.nacabaro.vbhelper.domain.Character
|
|
||||||
import com.github.nacabaro.vbhelper.di.VBHelper
|
|
||||||
import com.github.nacabaro.vbhelper.source.DexRepository
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun DiMScreen(
|
|
||||||
navController: NavController,
|
|
||||||
dimId: Int
|
|
||||||
) {
|
|
||||||
val coroutineScope = rememberCoroutineScope()
|
|
||||||
val application = LocalContext.current.applicationContext as VBHelper
|
|
||||||
val dexRepository = DexRepository(application.container.db)
|
|
||||||
|
|
||||||
val characterList = remember { mutableStateOf<List<Character>>(emptyList()) }
|
|
||||||
|
|
||||||
LaunchedEffect(dexRepository) {
|
|
||||||
coroutineScope.launch {
|
|
||||||
val newCharacterList = dexRepository.getCharactersByDimId(dimId)
|
|
||||||
characterList.value = newCharacterList
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Scaffold (
|
|
||||||
topBar = {
|
|
||||||
TopBanner(
|
|
||||||
text = "Discovered Digimon",
|
|
||||||
onBackClick = {
|
|
||||||
navController.popBackStack()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
) { contentPadding ->
|
|
||||||
LazyVerticalGrid(
|
|
||||||
columns = GridCells.Fixed(3),
|
|
||||||
contentPadding = contentPadding
|
|
||||||
) {
|
|
||||||
items(characterList.value) { character ->
|
|
||||||
CharacterEntry(
|
|
||||||
onClick = { },
|
|
||||||
icon = BitmapData(
|
|
||||||
bitmap = character.sprite1,
|
|
||||||
width = character.spritesWidth,
|
|
||||||
height = character.spritesHeight
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
package com.github.nacabaro.vbhelper.screens
|
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Box
|
|
||||||
import androidx.compose.foundation.layout.padding
|
|
||||||
import androidx.compose.material3.Scaffold
|
|
||||||
import androidx.compose.material3.Text
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import androidx.navigation.NavController
|
|
||||||
import com.github.nacabaro.vbhelper.components.TopBanner
|
|
||||||
import com.github.nacabaro.vbhelper.navigation.BottomNavItem
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun HomeScreen(
|
|
||||||
navController: NavController
|
|
||||||
) {
|
|
||||||
Scaffold (
|
|
||||||
topBar = {
|
|
||||||
TopBanner(
|
|
||||||
text = "VB Helper",
|
|
||||||
onGearClick = {
|
|
||||||
navController.navigate(BottomNavItem.Settings.route)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
) { contentPadding ->
|
|
||||||
Box (
|
|
||||||
modifier = Modifier
|
|
||||||
.padding(top = contentPadding.calculateTopPadding())
|
|
||||||
) {
|
|
||||||
Text("Home Screen")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,108 +0,0 @@
|
|||||||
package com.github.nacabaro.vbhelper.screens
|
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
|
||||||
import androidx.compose.foundation.layout.Column
|
|
||||||
import androidx.compose.foundation.layout.Spacer
|
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
|
||||||
import androidx.compose.foundation.layout.height
|
|
||||||
import androidx.compose.foundation.layout.padding
|
|
||||||
import androidx.compose.material3.Button
|
|
||||||
import androidx.compose.material3.Scaffold
|
|
||||||
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.Alignment
|
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
|
||||||
import androidx.compose.ui.unit.dp
|
|
||||||
import androidx.compose.ui.unit.sp
|
|
||||||
import androidx.navigation.NavController
|
|
||||||
import androidx.navigation.compose.rememberNavController
|
|
||||||
import com.github.nacabaro.vbhelper.components.TopBanner
|
|
||||||
import com.github.nacabaro.vbhelper.navigation.BottomNavItem
|
|
||||||
import com.github.nacabaro.vbhelper.screens.scanScreen.ReadingCharacterScreen
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun ScanScreen(
|
|
||||||
navController: NavController,
|
|
||||||
onClickRead: () -> Unit,
|
|
||||||
isDoneReadingCharacter: Boolean
|
|
||||||
) {
|
|
||||||
var readingScreen by remember { mutableStateOf(false) }
|
|
||||||
|
|
||||||
if (isDoneReadingCharacter) {
|
|
||||||
readingScreen = false
|
|
||||||
navController.navigate(BottomNavItem.Home.route)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (readingScreen) {
|
|
||||||
ReadingCharacterScreen { readingScreen = false }
|
|
||||||
} else {
|
|
||||||
ChooseConnectOption(
|
|
||||||
onClickRead = {
|
|
||||||
readingScreen = true
|
|
||||||
onClickRead()
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun ChooseConnectOption(
|
|
||||||
onClickRead: () -> Unit,
|
|
||||||
) {
|
|
||||||
Scaffold(
|
|
||||||
topBar = { TopBanner(text = "Scan a Vital Bracelet") }
|
|
||||||
) { contentPadding ->
|
|
||||||
Column(
|
|
||||||
verticalArrangement = Arrangement.Center,
|
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxSize()
|
|
||||||
.padding(contentPadding)
|
|
||||||
) {
|
|
||||||
ScanButton(
|
|
||||||
text = "Vital Bracelet to App",
|
|
||||||
onClick = onClickRead
|
|
||||||
)
|
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
|
||||||
ScanButton(
|
|
||||||
text = "App to Vital Bracelet",
|
|
||||||
onClick = {}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun ScanButton(
|
|
||||||
text: String,
|
|
||||||
onClick: () -> Unit,
|
|
||||||
modifier: Modifier = Modifier
|
|
||||||
) {
|
|
||||||
Button(
|
|
||||||
onClick = onClick,
|
|
||||||
modifier = modifier
|
|
||||||
) {
|
|
||||||
Text(
|
|
||||||
text = text,
|
|
||||||
fontSize = 16.sp,
|
|
||||||
modifier = Modifier
|
|
||||||
.padding(4.dp)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Preview(showBackground = true)
|
|
||||||
@Composable
|
|
||||||
fun ScanScreenPreview() {
|
|
||||||
ScanScreen(
|
|
||||||
navController = rememberNavController(),
|
|
||||||
onClickRead = { },
|
|
||||||
isDoneReadingCharacter = false
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,160 +0,0 @@
|
|||||||
package com.github.nacabaro.vbhelper.screens
|
|
||||||
|
|
||||||
import android.util.Log
|
|
||||||
import androidx.compose.foundation.Image
|
|
||||||
import androidx.compose.foundation.gestures.Orientation
|
|
||||||
import androidx.compose.foundation.gestures.scrollable
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
|
||||||
import androidx.compose.foundation.layout.Column
|
|
||||||
import androidx.compose.foundation.layout.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.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.mutableStateListOf
|
|
||||||
import androidx.compose.runtime.mutableStateOf
|
|
||||||
import androidx.compose.runtime.remember
|
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
|
||||||
import androidx.compose.runtime.setValue
|
|
||||||
import androidx.compose.ui.Alignment
|
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
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 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.source.StorageRepository
|
|
||||||
import com.github.nacabaro.vbhelper.utils.BitmapData
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun StorageScreen() {
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.d("StorageScreen", "monList: $monList")
|
|
||||||
|
|
||||||
Scaffold (
|
|
||||||
topBar = { TopBanner(text = "My Digimon") }
|
|
||||||
) { contentPadding ->
|
|
||||||
if (monList.value.isEmpty()) {
|
|
||||||
Column (
|
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
|
||||||
verticalArrangement = Arrangement.Center,
|
|
||||||
modifier = Modifier
|
|
||||||
.padding(contentPadding)
|
|
||||||
.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
|
|
||||||
),
|
|
||||||
modifier = Modifier
|
|
||||||
.padding(8.dp)
|
|
||||||
.size(96.dp)
|
|
||||||
|
|
||||||
)
|
|
||||||
|
|
||||||
if (selectedCharacter != null) {
|
|
||||||
StorageDialog(
|
|
||||||
characterId = selectedCharacter!!,
|
|
||||||
onDismissRequest = { selectedCharacter = null }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun StorageDialog(
|
|
||||||
characterId: Long,
|
|
||||||
onDismissRequest: () -> 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)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
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)
|
||||||
|
}
|
||||||
+101
@@ -0,0 +1,101 @@
|
|||||||
|
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,
|
||||||
|
itemType = randomItem.itemType
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
+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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+122
@@ -0,0 +1,122 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.screens.cardScreen
|
||||||
|
|
||||||
|
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.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.layout.width
|
||||||
|
import androidx.compose.material3.Card
|
||||||
|
import androidx.compose.material3.CardColors
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.contentColorFor
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.ColorFilter
|
||||||
|
import androidx.compose.ui.graphics.FilterQuality
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import com.github.nacabaro.vbhelper.dtos.CardDtos
|
||||||
|
import com.github.nacabaro.vbhelper.utils.BitmapData
|
||||||
|
import com.github.nacabaro.vbhelper.utils.getImageBitmap
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun CardAdventureEntry(
|
||||||
|
cardAdventureEntry: CardDtos.CardAdventureWithSprites,
|
||||||
|
obscure: Boolean
|
||||||
|
) {
|
||||||
|
val charaImageBitmapData = BitmapData(
|
||||||
|
bitmap = cardAdventureEntry.characterIdleSprite,
|
||||||
|
width = cardAdventureEntry.characterIdleSpriteWidth,
|
||||||
|
height = cardAdventureEntry.characterIdleSpriteHeight
|
||||||
|
).getImageBitmap(
|
||||||
|
context = LocalContext.current,
|
||||||
|
multiplier = 4,
|
||||||
|
obscure = obscure
|
||||||
|
)
|
||||||
|
|
||||||
|
val nameImageBitmapData = BitmapData(
|
||||||
|
bitmap = cardAdventureEntry.characterName,
|
||||||
|
width = cardAdventureEntry.characterNameWidth,
|
||||||
|
height = cardAdventureEntry.characterNameHeight
|
||||||
|
).getImageBitmap(
|
||||||
|
context = LocalContext.current,
|
||||||
|
multiplier = 3,
|
||||||
|
obscure = obscure
|
||||||
|
)
|
||||||
|
|
||||||
|
Card (
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(8.dp)
|
||||||
|
) {
|
||||||
|
Row (
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(8.dp)
|
||||||
|
){
|
||||||
|
Card (
|
||||||
|
colors = CardColors(
|
||||||
|
containerColor = MaterialTheme.colorScheme.surfaceVariant,
|
||||||
|
contentColor = MaterialTheme.colorScheme.contentColorFor(
|
||||||
|
backgroundColor = MaterialTheme.colorScheme.surfaceVariant,
|
||||||
|
),
|
||||||
|
disabledContainerColor = MaterialTheme.colorScheme.surfaceVariant,
|
||||||
|
disabledContentColor = MaterialTheme.colorScheme.contentColorFor(
|
||||||
|
backgroundColor = MaterialTheme.colorScheme.surfaceVariant,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
Image(
|
||||||
|
bitmap = charaImageBitmapData.imageBitmap,
|
||||||
|
contentDescription = "Icon",
|
||||||
|
modifier = Modifier
|
||||||
|
.size(charaImageBitmapData.dpWidth)
|
||||||
|
.padding(8.dp),
|
||||||
|
colorFilter = when (obscure) {
|
||||||
|
true -> ColorFilter.tint(color = MaterialTheme.colorScheme.secondary)
|
||||||
|
false -> null
|
||||||
|
},
|
||||||
|
filterQuality = FilterQuality.None
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.padding(8.dp))
|
||||||
|
|
||||||
|
Column {
|
||||||
|
if (!obscure) {
|
||||||
|
Image(
|
||||||
|
bitmap = nameImageBitmapData.imageBitmap,
|
||||||
|
contentDescription = "Icon",
|
||||||
|
modifier = Modifier
|
||||||
|
.width(nameImageBitmapData.dpWidth)
|
||||||
|
.height(nameImageBitmapData.dpHeight),
|
||||||
|
filterQuality = FilterQuality.None
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.padding(4.dp))
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = "HP: ${cardAdventureEntry.characterHp}, DP: ${cardAdventureEntry.characterDp}, AP: ${cardAdventureEntry.characterAp}"
|
||||||
|
)
|
||||||
|
if (cardAdventureEntry.characterBp != null) {
|
||||||
|
Text(text = "BP: ${cardAdventureEntry.characterBp}")
|
||||||
|
}
|
||||||
|
Text(text = "Steps: ${cardAdventureEntry.steps}")
|
||||||
|
} else {
|
||||||
|
Text(text = "????????????????")
|
||||||
|
Text(
|
||||||
|
text = "HP: -, BP: -, AP: -"
|
||||||
|
)
|
||||||
|
if (cardAdventureEntry.characterBp != null) {
|
||||||
|
Text(text = "DP: -")
|
||||||
|
}
|
||||||
|
Text(text = "Steps: -")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+63
@@ -0,0 +1,63 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.screens.cardScreen
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.rememberScrollState
|
||||||
|
import androidx.compose.foundation.verticalScroll
|
||||||
|
import androidx.compose.material3.Scaffold
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.navigation.NavController
|
||||||
|
import com.github.nacabaro.vbhelper.components.TopBanner
|
||||||
|
import com.github.nacabaro.vbhelper.dtos.CardDtos
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun CardAdventureScreen(
|
||||||
|
navController: NavController,
|
||||||
|
cardScreenController: CardScreenControllerImpl,
|
||||||
|
cardId: Long
|
||||||
|
) {
|
||||||
|
val cardAdventureMissions = remember { mutableStateOf(emptyList<CardDtos.CardAdventureWithSprites>()) }
|
||||||
|
var currentCardAdventure = remember { 0 }
|
||||||
|
|
||||||
|
LaunchedEffect(cardId) {
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
cardAdventureMissions.value =
|
||||||
|
cardScreenController
|
||||||
|
.getCardAdventureMissions(cardId)
|
||||||
|
|
||||||
|
currentCardAdventure =
|
||||||
|
cardScreenController
|
||||||
|
.getCardProgress(cardId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Scaffold (
|
||||||
|
topBar = {
|
||||||
|
TopBanner(
|
||||||
|
text = "Adventure missions",
|
||||||
|
onBackClick = {
|
||||||
|
navController.popBackStack()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
) { contentPadding ->
|
||||||
|
Column (
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(top = contentPadding.calculateTopPadding())
|
||||||
|
.verticalScroll(state = rememberScrollState())
|
||||||
|
) {
|
||||||
|
cardAdventureMissions.value.mapIndexed { index, it ->
|
||||||
|
CardAdventureEntry(
|
||||||
|
cardAdventureEntry = it,
|
||||||
|
obscure = index > currentCardAdventure
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,109 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.screens.cardScreen
|
||||||
|
|
||||||
|
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.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.Delete
|
||||||
|
import androidx.compose.material.icons.filled.Edit
|
||||||
|
import androidx.compose.material3.Card
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.IconButton
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
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 com.github.nacabaro.vbhelper.utils.BitmapData
|
||||||
|
import com.github.nacabaro.vbhelper.utils.getBitmap
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun CardEntry(
|
||||||
|
name: String,
|
||||||
|
logo: BitmapData,
|
||||||
|
obtainedCharacters: Int,
|
||||||
|
totalCharacters: Int,
|
||||||
|
onClick: () -> Unit,
|
||||||
|
displayModify: Boolean,
|
||||||
|
onClickModify: () -> Unit,
|
||||||
|
onClickDelete: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier
|
||||||
|
) {
|
||||||
|
val bitmap = remember (logo.bitmap) { logo.getBitmap() }
|
||||||
|
val imageBitmap = remember(bitmap) { bitmap.asImageBitmap() }
|
||||||
|
val density: Float = LocalContext.current.resources.displayMetrics.density
|
||||||
|
val dpSize = (logo.width * 4 / density).dp
|
||||||
|
|
||||||
|
Card (
|
||||||
|
shape = MaterialTheme.shapes.medium,
|
||||||
|
modifier = modifier,
|
||||||
|
onClick = if (!displayModify) {
|
||||||
|
onClick
|
||||||
|
} else {
|
||||||
|
{ }
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Row (
|
||||||
|
horizontalArrangement = Arrangement.Start,
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(8.dp)
|
||||||
|
) {
|
||||||
|
Image (
|
||||||
|
bitmap = imageBitmap,
|
||||||
|
contentDescription = name,
|
||||||
|
filterQuality = FilterQuality.None,
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(8.dp)
|
||||||
|
.size(dpSize)
|
||||||
|
)
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(8.dp)
|
||||||
|
.weight(1f)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = name,
|
||||||
|
modifier = Modifier
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = "$obtainedCharacters of $totalCharacters characters obtained",
|
||||||
|
fontFamily = MaterialTheme.typography.labelSmall.fontFamily,
|
||||||
|
fontSize = MaterialTheme.typography.labelSmall.fontSize,
|
||||||
|
modifier = Modifier
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (displayModify) {
|
||||||
|
Row (
|
||||||
|
modifier = Modifier,
|
||||||
|
horizontalArrangement = Arrangement.End,
|
||||||
|
) {
|
||||||
|
IconButton(
|
||||||
|
onClick = onClickModify
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.Edit,
|
||||||
|
contentDescription = "Edit"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
IconButton(
|
||||||
|
onClick = onClickDelete
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.Delete,
|
||||||
|
contentDescription = "Delete"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+10
@@ -0,0 +1,10 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.screens.cardScreen
|
||||||
|
|
||||||
|
import com.github.nacabaro.vbhelper.dtos.CardDtos
|
||||||
|
|
||||||
|
interface CardScreenController {
|
||||||
|
fun renameCard(cardId: Long, newName: String, onRenamed: (String) -> Unit)
|
||||||
|
fun deleteCard(cardId: Long, onDeleted: () -> Unit)
|
||||||
|
suspend fun getCardAdventureMissions(cardId: Long): List<CardDtos.CardAdventureWithSprites>
|
||||||
|
suspend fun getCardProgress(cardId: Long): Int
|
||||||
|
}
|
||||||
+46
@@ -0,0 +1,46 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.screens.cardScreen
|
||||||
|
|
||||||
|
import androidx.activity.ComponentActivity
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import com.github.nacabaro.vbhelper.di.VBHelper
|
||||||
|
import com.github.nacabaro.vbhelper.dtos.CardDtos
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
class CardScreenControllerImpl(
|
||||||
|
private val componentActivity: ComponentActivity,
|
||||||
|
) : CardScreenController {
|
||||||
|
private val application = componentActivity.applicationContext as VBHelper
|
||||||
|
private val database = application.container.db
|
||||||
|
|
||||||
|
override fun renameCard(cardId: Long, newName: String, onRenamed: (String) -> Unit) {
|
||||||
|
componentActivity.lifecycleScope.launch {
|
||||||
|
database
|
||||||
|
.cardDao()
|
||||||
|
.renameCard(cardId.toInt(), newName)
|
||||||
|
|
||||||
|
onRenamed(newName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun deleteCard(cardId: Long, onDeleted: () -> Unit) {
|
||||||
|
componentActivity.lifecycleScope.launch {
|
||||||
|
database
|
||||||
|
.cardDao()
|
||||||
|
.deleteCard(cardId)
|
||||||
|
|
||||||
|
onDeleted()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun getCardAdventureMissions(cardId: Long): List<CardDtos.CardAdventureWithSprites> {
|
||||||
|
return database
|
||||||
|
.cardAdventureDao()
|
||||||
|
.getAdventureForCard(cardId)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun getCardProgress(cardId: Long): Int {
|
||||||
|
return database
|
||||||
|
.cardProgressDao()
|
||||||
|
.getCardProgress(cardId)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.screens.cardScreen
|
||||||
|
|
||||||
|
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.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.navigation.NavController
|
||||||
|
import com.github.nacabaro.vbhelper.utils.BitmapData
|
||||||
|
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.cardScreen.dialogs.DexCharaDetailsDialog
|
||||||
|
import com.github.nacabaro.vbhelper.source.DexRepository
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun CardViewScreen(
|
||||||
|
navController: NavController,
|
||||||
|
cardId: Long
|
||||||
|
) {
|
||||||
|
val coroutineScope = rememberCoroutineScope()
|
||||||
|
val application = LocalContext.current.applicationContext as VBHelper
|
||||||
|
val dexRepository = DexRepository(application.container.db)
|
||||||
|
|
||||||
|
val characterList = remember { mutableStateOf<List<CharacterDtos.CardCharaProgress>>(emptyList()) }
|
||||||
|
val cardPossibleTransformations = remember { mutableStateOf<List<CharacterDtos.EvolutionRequirementsWithSpritesAndObtained>>(emptyList()) }
|
||||||
|
|
||||||
|
val selectedCharacter = remember { mutableStateOf<CharacterDtos.CardCharaProgress?>(null) }
|
||||||
|
|
||||||
|
LaunchedEffect(dexRepository) {
|
||||||
|
coroutineScope.launch {
|
||||||
|
val newCharacterList = dexRepository.getCharactersByCardId(cardId)
|
||||||
|
characterList.value = newCharacterList
|
||||||
|
|
||||||
|
val newCardPossibleTransformations = dexRepository.getCardPossibleTransformations(cardId)
|
||||||
|
cardPossibleTransformations.value = newCardPossibleTransformations
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Scaffold (
|
||||||
|
topBar = {
|
||||||
|
TopBanner(
|
||||||
|
text = "Discovered characters",
|
||||||
|
onBackClick = {
|
||||||
|
navController.popBackStack()
|
||||||
|
},
|
||||||
|
onAdventureClick = {
|
||||||
|
navController
|
||||||
|
.navigate(route = NavigationItems
|
||||||
|
.CardAdventure
|
||||||
|
.route
|
||||||
|
.replace(
|
||||||
|
"{cardId}",
|
||||||
|
cardId.toString()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
) { contentPadding ->
|
||||||
|
LazyVerticalGrid(
|
||||||
|
columns = GridCells.Fixed(3),
|
||||||
|
contentPadding = contentPadding
|
||||||
|
) {
|
||||||
|
items(characterList.value) { character ->
|
||||||
|
CharacterEntry(
|
||||||
|
onClick = {
|
||||||
|
selectedCharacter.value = character
|
||||||
|
},
|
||||||
|
obscure = character.discoveredOn == null,
|
||||||
|
icon = BitmapData(
|
||||||
|
bitmap = character.spriteIdle,
|
||||||
|
width = character.spriteWidth,
|
||||||
|
height = character.spriteHeight,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectedCharacter.value != null) {
|
||||||
|
DexCharaDetailsDialog(
|
||||||
|
currentChara = selectedCharacter.value!!,
|
||||||
|
possibleTransformations = cardPossibleTransformations.value,
|
||||||
|
obscure = selectedCharacter.value!!.discoveredOn == null,
|
||||||
|
onClickClose = {
|
||||||
|
selectedCharacter.value = null
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,145 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.screens.cardScreen
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.items
|
||||||
|
import androidx.compose.material3.Scaffold
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
|
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.utils.BitmapData
|
||||||
|
import com.github.nacabaro.vbhelper.components.TopBanner
|
||||||
|
import com.github.nacabaro.vbhelper.di.VBHelper
|
||||||
|
import com.github.nacabaro.vbhelper.dtos.CardDtos
|
||||||
|
import com.github.nacabaro.vbhelper.navigation.NavigationItems
|
||||||
|
import com.github.nacabaro.vbhelper.screens.cardScreen.dialogs.CardDeleteDialog
|
||||||
|
import com.github.nacabaro.vbhelper.screens.cardScreen.dialogs.CardRenameDialog
|
||||||
|
import com.github.nacabaro.vbhelper.source.DexRepository
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun CardsScreen(
|
||||||
|
navController: NavController,
|
||||||
|
cardScreenController: CardScreenControllerImpl
|
||||||
|
) {
|
||||||
|
val coroutineScope = rememberCoroutineScope()
|
||||||
|
val application = LocalContext.current.applicationContext as VBHelper
|
||||||
|
val dexRepository = DexRepository(application.container.db)
|
||||||
|
val cardList = remember { mutableStateOf<List<CardDtos.CardProgress>>(emptyList()) }
|
||||||
|
|
||||||
|
val selectedCard = remember { mutableStateOf<CardDtos.CardProgress?>(null) }
|
||||||
|
var clickedDelete by remember { mutableStateOf(false) }
|
||||||
|
var clickedRename by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
|
var modifyCards by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
|
LaunchedEffect(dexRepository) {
|
||||||
|
coroutineScope.launch {
|
||||||
|
val newDimList = dexRepository.getAllDims()
|
||||||
|
cardList.value = newDimList
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Scaffold (
|
||||||
|
topBar = {
|
||||||
|
TopBanner(
|
||||||
|
text = "My cards",
|
||||||
|
onModifyClick = {
|
||||||
|
modifyCards = !modifyCards
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
) { contentPadding ->
|
||||||
|
LazyColumn (
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(top = contentPadding.calculateTopPadding())
|
||||||
|
) {
|
||||||
|
items(cardList.value) {
|
||||||
|
CardEntry(
|
||||||
|
name = it.cardName,
|
||||||
|
logo = BitmapData(
|
||||||
|
bitmap = it.cardLogo,
|
||||||
|
width = it.logoWidth,
|
||||||
|
height = it.logoHeight
|
||||||
|
),
|
||||||
|
onClick = {
|
||||||
|
navController
|
||||||
|
.navigate(
|
||||||
|
NavigationItems
|
||||||
|
.CardView.route
|
||||||
|
.replace("{cardId}", "${it.cardId}")
|
||||||
|
)
|
||||||
|
},
|
||||||
|
obtainedCharacters = it.obtainedCharacters,
|
||||||
|
totalCharacters = it.totalCharacters,
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(8.dp),
|
||||||
|
displayModify = modifyCards,
|
||||||
|
onClickModify = {
|
||||||
|
selectedCard.value = it
|
||||||
|
clickedRename = true
|
||||||
|
},
|
||||||
|
onClickDelete = {
|
||||||
|
selectedCard.value = it
|
||||||
|
clickedDelete = true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clickedRename) {
|
||||||
|
CardRenameDialog(
|
||||||
|
onDismiss = {
|
||||||
|
clickedRename = false
|
||||||
|
selectedCard.value = null
|
||||||
|
},
|
||||||
|
onRename = { newName ->
|
||||||
|
Log.d("CardsScreen", "New name: $newName")
|
||||||
|
Log.d("CardsScreen", "Card: ${selectedCard.value.toString()}")
|
||||||
|
cardScreenController
|
||||||
|
.renameCard(
|
||||||
|
cardId = selectedCard.value!!.cardId,
|
||||||
|
newName = newName,
|
||||||
|
onRenamed = {
|
||||||
|
clickedRename = false
|
||||||
|
selectedCard.value = null
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
currentName = selectedCard.value!!.cardName
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clickedDelete) {
|
||||||
|
CardDeleteDialog(
|
||||||
|
cardName = selectedCard.value!!.cardName,
|
||||||
|
onDismiss = {
|
||||||
|
clickedDelete = false
|
||||||
|
selectedCard.value = null
|
||||||
|
},
|
||||||
|
onConfirm = {
|
||||||
|
cardScreenController
|
||||||
|
.deleteCard(
|
||||||
|
cardId = selectedCard.value!!.cardId,
|
||||||
|
onDeleted = {
|
||||||
|
clickedDelete = false
|
||||||
|
selectedCard.value = null
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
+51
@@ -0,0 +1,51 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.screens.cardScreen.dialogs
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.material3.Button
|
||||||
|
import androidx.compose.material3.Card
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.window.Dialog
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun CardDeleteDialog(
|
||||||
|
cardName: String,
|
||||||
|
onDismiss: () -> Unit,
|
||||||
|
onConfirm: () -> Unit
|
||||||
|
) {
|
||||||
|
Dialog(
|
||||||
|
onDismissRequest = onDismiss
|
||||||
|
|
||||||
|
) {
|
||||||
|
Card ( ) {
|
||||||
|
Column (
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(16.dp)
|
||||||
|
) {
|
||||||
|
Text(text = "Are you sure you want to delete $cardName. This action will also delete all the characters raised from this card.")
|
||||||
|
Spacer(modifier = Modifier.padding(8.dp))
|
||||||
|
Row {
|
||||||
|
Button(
|
||||||
|
onClick = {
|
||||||
|
onDismiss()
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Text(text = "Confirm")
|
||||||
|
}
|
||||||
|
Button(
|
||||||
|
onClick = {
|
||||||
|
onConfirm()
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Text(text = "Delete")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+52
@@ -0,0 +1,52 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.screens.cardScreen.dialogs
|
||||||
|
|
||||||
|
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.material3.TextField
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.window.Dialog
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun CardRenameDialog(
|
||||||
|
onDismiss: () -> Unit,
|
||||||
|
onRename: (String) -> Unit,
|
||||||
|
currentName: String
|
||||||
|
) {
|
||||||
|
var cardName by remember { mutableStateOf(currentName) }
|
||||||
|
|
||||||
|
Dialog(
|
||||||
|
onDismissRequest = onDismiss
|
||||||
|
|
||||||
|
) {
|
||||||
|
Card ( ) {
|
||||||
|
Column (
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(16.dp)
|
||||||
|
) {
|
||||||
|
TextField(
|
||||||
|
value = cardName,
|
||||||
|
onValueChange = { cardName = it }
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.padding(8.dp))
|
||||||
|
Button(
|
||||||
|
onClick = {
|
||||||
|
onRename(cardName)
|
||||||
|
onDismiss()
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Text(text = "Rename")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+215
@@ -0,0 +1,215 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.screens.cardScreen.dialogs
|
||||||
|
|
||||||
|
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.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.layout.width
|
||||||
|
import androidx.compose.material3.Button
|
||||||
|
import androidx.compose.material3.Card
|
||||||
|
import androidx.compose.material3.CardColors
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.contentColorFor
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.ColorFilter
|
||||||
|
import androidx.compose.ui.graphics.FilterQuality
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.window.Dialog
|
||||||
|
import com.github.nacabaro.vbhelper.dtos.CharacterDtos
|
||||||
|
import com.github.nacabaro.vbhelper.utils.BitmapData
|
||||||
|
import com.github.nacabaro.vbhelper.utils.getImageBitmap
|
||||||
|
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun DexCharaDetailsDialog(
|
||||||
|
currentChara: CharacterDtos.CardCharaProgress,
|
||||||
|
possibleTransformations: List<CharacterDtos.EvolutionRequirementsWithSpritesAndObtained>,
|
||||||
|
obscure: Boolean,
|
||||||
|
onClickClose: () -> Unit
|
||||||
|
) {
|
||||||
|
val nameMultiplier = 3
|
||||||
|
val charaMultiplier = 4
|
||||||
|
|
||||||
|
val currentCharaPossibleTransformations = possibleTransformations.filter { it.fromCharaId == currentChara.id }
|
||||||
|
|
||||||
|
val romanNumeralsStage = when (currentChara.stage) {
|
||||||
|
1 -> "II"
|
||||||
|
2 -> "III"
|
||||||
|
3 -> "IV"
|
||||||
|
4 -> "V"
|
||||||
|
5 -> "VI"
|
||||||
|
6 -> "VII"
|
||||||
|
else -> "I"
|
||||||
|
}
|
||||||
|
|
||||||
|
val charaBitmapData = BitmapData(
|
||||||
|
bitmap = currentChara.spriteIdle,
|
||||||
|
width = currentChara.spriteWidth,
|
||||||
|
height = currentChara.spriteHeight
|
||||||
|
)
|
||||||
|
val charaImageBitmapData = charaBitmapData.getImageBitmap(
|
||||||
|
context = LocalContext.current,
|
||||||
|
multiplier = charaMultiplier,
|
||||||
|
obscure = obscure
|
||||||
|
)
|
||||||
|
|
||||||
|
val nameBitmapData = BitmapData(
|
||||||
|
bitmap = currentChara.nameSprite,
|
||||||
|
width = currentChara.nameSpriteWidth,
|
||||||
|
height = currentChara.nameSpriteHeight
|
||||||
|
)
|
||||||
|
val nameImageBitmapData = nameBitmapData.getImageBitmap(
|
||||||
|
context = LocalContext.current,
|
||||||
|
multiplier = nameMultiplier,
|
||||||
|
obscure = obscure
|
||||||
|
)
|
||||||
|
|
||||||
|
Dialog(
|
||||||
|
onDismissRequest = onClickClose
|
||||||
|
) {
|
||||||
|
Card (
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
Column (
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(16.dp)
|
||||||
|
) {
|
||||||
|
Row (
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
modifier = Modifier
|
||||||
|
) {
|
||||||
|
Card (
|
||||||
|
colors = CardColors(
|
||||||
|
containerColor = MaterialTheme.colorScheme.surfaceVariant,
|
||||||
|
contentColor = MaterialTheme.colorScheme.contentColorFor(
|
||||||
|
backgroundColor = MaterialTheme.colorScheme.surfaceVariant,
|
||||||
|
),
|
||||||
|
disabledContainerColor = MaterialTheme.colorScheme.surfaceVariant,
|
||||||
|
disabledContentColor = MaterialTheme.colorScheme.contentColorFor(
|
||||||
|
backgroundColor = MaterialTheme.colorScheme.surfaceVariant,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
Image(
|
||||||
|
bitmap = charaImageBitmapData.imageBitmap,
|
||||||
|
contentDescription = "Icon",
|
||||||
|
modifier = Modifier
|
||||||
|
.size(charaImageBitmapData.dpWidth)
|
||||||
|
.padding(8.dp),
|
||||||
|
colorFilter = when (obscure) {
|
||||||
|
true -> ColorFilter.tint(color = MaterialTheme.colorScheme.secondary)
|
||||||
|
false -> null
|
||||||
|
},
|
||||||
|
filterQuality = FilterQuality.None
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Spacer(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(16.dp)
|
||||||
|
)
|
||||||
|
if (!obscure) {
|
||||||
|
Column {
|
||||||
|
Image(
|
||||||
|
bitmap = nameImageBitmapData.imageBitmap,
|
||||||
|
contentDescription = "Icon",
|
||||||
|
modifier = Modifier
|
||||||
|
.width(nameImageBitmapData.dpWidth)
|
||||||
|
.height(nameImageBitmapData.dpHeight),
|
||||||
|
filterQuality = FilterQuality.None
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.padding(4.dp))
|
||||||
|
if (currentChara.baseHp != 65535) {
|
||||||
|
Text(
|
||||||
|
text = "HP: ${currentChara.baseHp}, BP: ${currentChara.baseBp}, AP: ${currentChara.baseAp}"
|
||||||
|
)
|
||||||
|
Text(text = "Stg: ${romanNumeralsStage}, Atr: ${currentChara.attribute.toString().substring(0, 2)}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Column {
|
||||||
|
Text(text = "????????????????")
|
||||||
|
Spacer(modifier = Modifier.padding(4.dp))
|
||||||
|
Text(text = "Stg: -, Atr: -")
|
||||||
|
Text(text = "HP: -, BP: -, AP: -")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Spacer(modifier = Modifier.padding(16.dp))
|
||||||
|
Column {
|
||||||
|
currentCharaPossibleTransformations.map {
|
||||||
|
val selectedCharaBitmap = BitmapData(
|
||||||
|
bitmap = it.spriteIdle,
|
||||||
|
width = it.spriteWidth,
|
||||||
|
height = it.spriteHeight
|
||||||
|
)
|
||||||
|
val selectedCharaImageBitmap = selectedCharaBitmap.getImageBitmap(
|
||||||
|
context = LocalContext.current,
|
||||||
|
multiplier = 4,
|
||||||
|
obscure = it.discoveredOn == null
|
||||||
|
)
|
||||||
|
|
||||||
|
Card (
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(vertical = 8.dp)
|
||||||
|
) {
|
||||||
|
Row (
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
Card (
|
||||||
|
colors = CardColors(
|
||||||
|
containerColor = MaterialTheme.colorScheme.surfaceVariant,
|
||||||
|
contentColor = MaterialTheme.colorScheme.contentColorFor(
|
||||||
|
backgroundColor = MaterialTheme.colorScheme.surfaceVariant,
|
||||||
|
),
|
||||||
|
disabledContainerColor = MaterialTheme.colorScheme.surfaceVariant,
|
||||||
|
disabledContentColor = MaterialTheme.colorScheme.contentColorFor(
|
||||||
|
backgroundColor = MaterialTheme.colorScheme.surfaceVariant,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
Image(
|
||||||
|
bitmap = selectedCharaImageBitmap.imageBitmap,
|
||||||
|
contentDescription = "Icon",
|
||||||
|
modifier = Modifier
|
||||||
|
.size(selectedCharaImageBitmap.dpWidth)
|
||||||
|
.padding(8.dp),
|
||||||
|
colorFilter = when (it.discoveredOn == null) {
|
||||||
|
true -> ColorFilter.tint(color = MaterialTheme.colorScheme.secondary)
|
||||||
|
false -> null
|
||||||
|
},
|
||||||
|
filterQuality = FilterQuality.None
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Spacer(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(16.dp)
|
||||||
|
)
|
||||||
|
Column {
|
||||||
|
Text("Tr: ${it.requiredTrophies}; Bt: ${it.requiredBattles}; Vr: ${it.requiredVitals}; Wr: ${it.requiredWinRate}%; Ct: ${it.changeTimerHours}h")
|
||||||
|
Text("AdvLvl ${it.requiredAdventureLevelCompleted + 1}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Button(
|
||||||
|
onClick = onClickClose
|
||||||
|
) {
|
||||||
|
Text("Close")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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 = "Application should work now with the original VB and the VH."
|
||||||
|
)
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,181 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.screens.homeScreens
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.material3.Button
|
||||||
|
import androidx.compose.material3.Card
|
||||||
|
import androidx.compose.material3.Scaffold
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.window.Dialog
|
||||||
|
import androidx.navigation.NavController
|
||||||
|
import com.github.nacabaro.vbhelper.components.TopBanner
|
||||||
|
import com.github.nacabaro.vbhelper.di.VBHelper
|
||||||
|
import com.github.nacabaro.vbhelper.utils.DeviceType
|
||||||
|
import com.github.nacabaro.vbhelper.domain.device_data.BECharacterData
|
||||||
|
import com.github.nacabaro.vbhelper.domain.device_data.SpecialMissions
|
||||||
|
import com.github.nacabaro.vbhelper.domain.device_data.VBCharacterData
|
||||||
|
import com.github.nacabaro.vbhelper.dtos.CharacterDtos
|
||||||
|
import com.github.nacabaro.vbhelper.dtos.ItemDtos
|
||||||
|
import com.github.nacabaro.vbhelper.navigation.NavigationItems
|
||||||
|
import com.github.nacabaro.vbhelper.screens.homeScreens.screens.BEBEmHomeScreen
|
||||||
|
import com.github.nacabaro.vbhelper.screens.homeScreens.screens.BEDiMHomeScreen
|
||||||
|
import com.github.nacabaro.vbhelper.screens.homeScreens.screens.VBDiMHomeScreen
|
||||||
|
import com.github.nacabaro.vbhelper.screens.itemsScreen.ObtainedItemDialog
|
||||||
|
import com.github.nacabaro.vbhelper.source.StorageRepository
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun HomeScreen(
|
||||||
|
navController: NavController,
|
||||||
|
homeScreenController: HomeScreenControllerImpl
|
||||||
|
) {
|
||||||
|
val application = LocalContext.current.applicationContext as VBHelper
|
||||||
|
val storageRepository = StorageRepository(application.container.db)
|
||||||
|
val activeMon = remember { mutableStateOf<CharacterDtos.CharacterWithSprites?>(null) }
|
||||||
|
val transformationHistory = remember { mutableStateOf<List<CharacterDtos.TransformationHistory>?>(null) }
|
||||||
|
val beData = remember { mutableStateOf<BECharacterData?>(null) }
|
||||||
|
val vbData = remember { mutableStateOf<VBCharacterData?>(null) }
|
||||||
|
val vbSpecialMissions = remember { mutableStateOf<List<SpecialMissions>>(emptyList()) }
|
||||||
|
var adventureMissionsFinished by rememberSaveable { mutableStateOf(false) }
|
||||||
|
var betaWarning by rememberSaveable { mutableStateOf(true) }
|
||||||
|
var collectedItem by remember { mutableStateOf<ItemDtos.PurchasedItem?>(null) }
|
||||||
|
|
||||||
|
LaunchedEffect(storageRepository, activeMon, collectedItem) {
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
activeMon.value = storageRepository.getActiveCharacter()
|
||||||
|
if (activeMon.value != null && activeMon.value!!.characterType == DeviceType.BEDevice) {
|
||||||
|
beData.value = storageRepository.getCharacterBeData(activeMon.value!!.id)
|
||||||
|
transformationHistory.value = storageRepository.getTransformationHistory(activeMon.value!!.id)
|
||||||
|
} else if (activeMon.value != null && activeMon.value!!.characterType == DeviceType.VBDevice) {
|
||||||
|
vbData.value = storageRepository.getCharacterVbData(activeMon.value!!.id)
|
||||||
|
vbSpecialMissions.value = storageRepository.getSpecialMissions(activeMon.value!!.id)
|
||||||
|
transformationHistory.value = storageRepository.getTransformationHistory(activeMon.value!!.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LaunchedEffect(true) {
|
||||||
|
homeScreenController
|
||||||
|
.didAdventureMissionsFinish {
|
||||||
|
adventureMissionsFinished = it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Scaffold (
|
||||||
|
topBar = {
|
||||||
|
TopBanner(
|
||||||
|
text = "VB Helper",
|
||||||
|
onScanClick = {
|
||||||
|
navController.navigate(NavigationItems.Scan.route)
|
||||||
|
},
|
||||||
|
onGearClick = {
|
||||||
|
navController.navigate(NavigationItems.Settings.route)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
) { contentPadding ->
|
||||||
|
if (activeMon.value == null || (beData.value == null && vbData.value == null) || transformationHistory.value == null) {
|
||||||
|
Log.d("TetTet", "Something is null")
|
||||||
|
Column (
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
verticalArrangement = Arrangement.Center,
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(top = contentPadding.calculateTopPadding())
|
||||||
|
) {
|
||||||
|
Text(text = "Nothing to see here")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.d("TetTet", "Something is not null")
|
||||||
|
if (activeMon.value!!.isBemCard) {
|
||||||
|
BEBEmHomeScreen(
|
||||||
|
activeMon = activeMon.value!!,
|
||||||
|
beData = beData.value!!,
|
||||||
|
transformationHistory = transformationHistory.value!!,
|
||||||
|
contentPadding = contentPadding
|
||||||
|
)
|
||||||
|
} else if (!activeMon.value!!.isBemCard && activeMon.value!!.characterType == DeviceType.BEDevice) {
|
||||||
|
BEDiMHomeScreen(
|
||||||
|
activeMon = activeMon.value!!,
|
||||||
|
beData = beData.value!!,
|
||||||
|
transformationHistory = transformationHistory.value!!,
|
||||||
|
contentPadding = contentPadding
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
VBDiMHomeScreen(
|
||||||
|
activeMon = activeMon.value!!,
|
||||||
|
vbData = vbData.value!!,
|
||||||
|
transformationHistory = transformationHistory.value!!,
|
||||||
|
contentPadding = contentPadding,
|
||||||
|
specialMissions = vbSpecialMissions.value,
|
||||||
|
homeScreenController = homeScreenController,
|
||||||
|
onClickCollect = { item ->
|
||||||
|
collectedItem = item
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (collectedItem != null) {
|
||||||
|
ObtainedItemDialog(
|
||||||
|
obtainedItem = collectedItem!!,
|
||||||
|
onClickDismiss = {
|
||||||
|
collectedItem = null
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
+9
@@ -0,0 +1,9 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.screens.homeScreens
|
||||||
|
|
||||||
|
import com.github.cfogrady.vbnfc.vb.SpecialMission
|
||||||
|
import com.github.nacabaro.vbhelper.dtos.ItemDtos
|
||||||
|
|
||||||
|
interface HomeScreenController {
|
||||||
|
fun didAdventureMissionsFinish(onCompletion: (Boolean) -> Unit)
|
||||||
|
fun clearSpecialMission(missionId: Long, missionCompletion: SpecialMission.Status, onCleared: (ItemDtos.PurchasedItem?) -> Unit)
|
||||||
|
}
|
||||||
+72
@@ -0,0 +1,72 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.screens.homeScreens
|
||||||
|
|
||||||
|
import androidx.activity.ComponentActivity
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import com.github.cfogrady.vbnfc.vb.SpecialMission
|
||||||
|
import com.github.nacabaro.vbhelper.di.VBHelper
|
||||||
|
import com.github.nacabaro.vbhelper.dtos.ItemDtos
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import java.time.Instant
|
||||||
|
import kotlin.math.roundToInt
|
||||||
|
import kotlin.random.Random
|
||||||
|
|
||||||
|
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())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun clearSpecialMission(missionId: Long, missionCompletion: SpecialMission.Status, onCleared: (ItemDtos.PurchasedItem?) -> Unit) {
|
||||||
|
componentActivity.lifecycleScope.launch {
|
||||||
|
database
|
||||||
|
.specialMissionDao()
|
||||||
|
.clearSpecialMission(missionId)
|
||||||
|
|
||||||
|
if (missionCompletion == SpecialMission.Status.COMPLETED) {
|
||||||
|
val randomItem = database
|
||||||
|
.itemDao()
|
||||||
|
.getAllItems()
|
||||||
|
.random()
|
||||||
|
|
||||||
|
val randomItemAmount = (Random.nextFloat() * 5).roundToInt()
|
||||||
|
|
||||||
|
database
|
||||||
|
.itemDao()
|
||||||
|
.purchaseItem(
|
||||||
|
itemId = randomItem.id,
|
||||||
|
itemAmount = randomItemAmount
|
||||||
|
)
|
||||||
|
|
||||||
|
val purchasedItem = ItemDtos.PurchasedItem(
|
||||||
|
itemId = randomItem.id,
|
||||||
|
itemName = randomItem.name,
|
||||||
|
itemDescription = randomItem.description,
|
||||||
|
itemIcon = randomItem.itemIcon,
|
||||||
|
itemLength = randomItem.itemLength,
|
||||||
|
itemAmount = randomItemAmount,
|
||||||
|
itemType = randomItem.itemType
|
||||||
|
)
|
||||||
|
|
||||||
|
onCleared(purchasedItem)
|
||||||
|
} else {
|
||||||
|
onCleared(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+221
@@ -0,0 +1,221 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.screens.homeScreens.screens
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.aspectRatio
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.rememberScrollState
|
||||||
|
import androidx.compose.foundation.verticalScroll
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import com.github.nacabaro.vbhelper.R
|
||||||
|
import com.github.nacabaro.vbhelper.components.CharacterEntry
|
||||||
|
import com.github.nacabaro.vbhelper.components.ItemDisplay
|
||||||
|
import com.github.nacabaro.vbhelper.components.TransformationHistoryCard
|
||||||
|
import com.github.nacabaro.vbhelper.screens.itemsScreen.getIconResource
|
||||||
|
import com.github.nacabaro.vbhelper.domain.device_data.BECharacterData
|
||||||
|
import com.github.nacabaro.vbhelper.dtos.CharacterDtos
|
||||||
|
import com.github.nacabaro.vbhelper.screens.itemsScreen.ItemsScreenControllerImpl
|
||||||
|
import com.github.nacabaro.vbhelper.utils.BitmapData
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun BEBEmHomeScreen(
|
||||||
|
activeMon: CharacterDtos.CharacterWithSprites,
|
||||||
|
beData: BECharacterData,
|
||||||
|
transformationHistory: List<CharacterDtos.TransformationHistory>,
|
||||||
|
contentPadding: PaddingValues
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(top = contentPadding.calculateTopPadding())
|
||||||
|
.verticalScroll(state = rememberScrollState())
|
||||||
|
) {
|
||||||
|
Row (
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
CharacterEntry(
|
||||||
|
icon = BitmapData(
|
||||||
|
bitmap = activeMon.spriteIdle,
|
||||||
|
width = activeMon.spriteWidth,
|
||||||
|
height = activeMon.spriteHeight
|
||||||
|
),
|
||||||
|
multiplier = 8,
|
||||||
|
shape = androidx.compose.material.MaterialTheme.shapes.small,
|
||||||
|
modifier = Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.aspectRatio(1f)
|
||||||
|
)
|
||||||
|
Column (
|
||||||
|
modifier = Modifier
|
||||||
|
.weight(0.5f)
|
||||||
|
.aspectRatio(0.5f)
|
||||||
|
) {
|
||||||
|
ItemDisplay(
|
||||||
|
icon = R.drawable.baseline_vitals_24,
|
||||||
|
textValue = activeMon.vitalPoints.toString(),
|
||||||
|
definition = "Vitals",
|
||||||
|
modifier = Modifier
|
||||||
|
.weight(0.5f)
|
||||||
|
.aspectRatio(1f)
|
||||||
|
.padding(8.dp)
|
||||||
|
)
|
||||||
|
ItemDisplay(
|
||||||
|
icon = R.drawable.baseline_trophy_24,
|
||||||
|
textValue = activeMon.trophies.toString(),
|
||||||
|
definition = "Trophies",
|
||||||
|
modifier = Modifier
|
||||||
|
.weight(0.5f)
|
||||||
|
.aspectRatio(1f)
|
||||||
|
.padding(8.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Row (
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
ItemDisplay(
|
||||||
|
icon = R.drawable.baseline_mood_24,
|
||||||
|
textValue = activeMon.mood.toString(),
|
||||||
|
definition = "Mood",
|
||||||
|
modifier = Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.aspectRatio(1f)
|
||||||
|
.padding(8.dp)
|
||||||
|
)
|
||||||
|
val timeInHours = (beData.remainingTrainingTimeInMinutes / 60)
|
||||||
|
ItemDisplay(
|
||||||
|
icon = R.drawable.baseline_timer_24,
|
||||||
|
textValue = "$timeInHours h",
|
||||||
|
definition = "Training limit",
|
||||||
|
modifier = Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.aspectRatio(1f)
|
||||||
|
.padding(8.dp)
|
||||||
|
)
|
||||||
|
ItemDisplay(
|
||||||
|
icon = R.drawable.baseline_rank_24,
|
||||||
|
textValue = beData.rank.toString(),
|
||||||
|
definition = "Rank",
|
||||||
|
modifier = Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.aspectRatio(1f)
|
||||||
|
.padding(8.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Row (
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
val transformationCountdownInHours = activeMon.transformationCountdown / 60
|
||||||
|
ItemDisplay(
|
||||||
|
icon = R.drawable.baseline_next_24,
|
||||||
|
textValue = when (transformationCountdownInHours) {
|
||||||
|
0 -> "${activeMon.transformationCountdown} m"
|
||||||
|
else -> "$transformationCountdownInHours h"
|
||||||
|
},
|
||||||
|
definition = "Next timer",
|
||||||
|
modifier = Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.aspectRatio(1f)
|
||||||
|
.padding(8.dp)
|
||||||
|
)
|
||||||
|
ItemDisplay(
|
||||||
|
icon = R.drawable.baseline_swords_24,
|
||||||
|
textValue = when {
|
||||||
|
activeMon.totalBattlesLost == 0 -> "0.00 %"
|
||||||
|
else -> {
|
||||||
|
val battleWinPercentage = activeMon.totalBattlesWon.toFloat() / (activeMon.totalBattlesWon + activeMon.totalBattlesLost).toFloat()
|
||||||
|
String.format(Locale.getDefault(), "%.2f", battleWinPercentage * 100) + " %" // Specify locale
|
||||||
|
}
|
||||||
|
},
|
||||||
|
definition = "Total battle win %",
|
||||||
|
modifier = Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.aspectRatio(1f)
|
||||||
|
.padding(8.dp)
|
||||||
|
)
|
||||||
|
ItemDisplay(
|
||||||
|
icon = R.drawable.baseline_swords_24,
|
||||||
|
textValue = when {
|
||||||
|
activeMon.totalBattlesLost == 0 -> "0.00 %"
|
||||||
|
else -> {
|
||||||
|
val battleWinPercentage = activeMon.currentPhaseBattlesWon.toFloat() / (activeMon.currentPhaseBattlesWon + activeMon.currentPhaseBattlesLost).toFloat()
|
||||||
|
String.format(Locale.getDefault(), "%.2f", battleWinPercentage * 100) + " %" // Specify locale
|
||||||
|
}
|
||||||
|
},
|
||||||
|
definition = "Current phase win %",
|
||||||
|
modifier = Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.aspectRatio(1f)
|
||||||
|
.padding(8.dp)
|
||||||
|
)
|
||||||
|
if (beData.itemRemainingTime != 0) {
|
||||||
|
ItemDisplay(
|
||||||
|
icon = getIconResource(beData.itemType),
|
||||||
|
textValue = "${beData.itemRemainingTime} m",
|
||||||
|
definition = when (beData.itemType) {
|
||||||
|
ItemsScreenControllerImpl.ItemTypes.PPTraining.id -> "PP Training"
|
||||||
|
ItemsScreenControllerImpl.ItemTypes.HPTraining.id -> "HP Training"
|
||||||
|
ItemsScreenControllerImpl.ItemTypes.APTraining.id -> "AP Training"
|
||||||
|
ItemsScreenControllerImpl.ItemTypes.BPTraining.id -> "BP Training"
|
||||||
|
ItemsScreenControllerImpl.ItemTypes.AllTraining.id -> "All Training"
|
||||||
|
else -> ""
|
||||||
|
},
|
||||||
|
modifier = Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.aspectRatio(1f)
|
||||||
|
.padding(8.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Row (
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
TransformationHistoryCard(
|
||||||
|
transformationHistory = transformationHistory,
|
||||||
|
modifier = Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.padding(8.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Row (
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
ItemDisplay(
|
||||||
|
icon = R.drawable.baseline_health_24,
|
||||||
|
textValue = "+${beData.trainingHp}",
|
||||||
|
definition = "Training HP",
|
||||||
|
modifier = Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.aspectRatio(1f)
|
||||||
|
.padding(8.dp)
|
||||||
|
)
|
||||||
|
ItemDisplay(
|
||||||
|
icon = R.drawable.baseline_agility_24,
|
||||||
|
textValue = "+${beData.trainingBp}",
|
||||||
|
definition = "Training BP",
|
||||||
|
modifier = Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.aspectRatio(1f)
|
||||||
|
.padding(8.dp)
|
||||||
|
)
|
||||||
|
ItemDisplay(
|
||||||
|
icon = R.drawable.baseline_attack_24,
|
||||||
|
textValue = "+${beData.trainingAp}",
|
||||||
|
definition = "Training AP",
|
||||||
|
modifier = Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.aspectRatio(1f)
|
||||||
|
.padding(8.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+201
@@ -0,0 +1,201 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.screens.homeScreens.screens
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.aspectRatio
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.rememberScrollState
|
||||||
|
import androidx.compose.foundation.verticalScroll
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import java.util.Locale
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import com.github.nacabaro.vbhelper.R
|
||||||
|
import com.github.nacabaro.vbhelper.components.CharacterEntry
|
||||||
|
import com.github.nacabaro.vbhelper.components.ItemDisplay
|
||||||
|
import com.github.nacabaro.vbhelper.components.TransformationHistoryCard
|
||||||
|
import com.github.nacabaro.vbhelper.screens.itemsScreen.getIconResource
|
||||||
|
import com.github.nacabaro.vbhelper.domain.device_data.BECharacterData
|
||||||
|
import com.github.nacabaro.vbhelper.dtos.CharacterDtos
|
||||||
|
import com.github.nacabaro.vbhelper.screens.itemsScreen.ItemsScreenControllerImpl
|
||||||
|
import com.github.nacabaro.vbhelper.utils.BitmapData
|
||||||
|
import kotlin.text.format
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun BEDiMHomeScreen(
|
||||||
|
activeMon: CharacterDtos.CharacterWithSprites,
|
||||||
|
beData: BECharacterData,
|
||||||
|
transformationHistory: List<CharacterDtos.TransformationHistory>,
|
||||||
|
contentPadding: PaddingValues
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(top = contentPadding.calculateTopPadding())
|
||||||
|
.verticalScroll(state = rememberScrollState())
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
CharacterEntry(
|
||||||
|
icon = BitmapData(
|
||||||
|
bitmap = activeMon.spriteIdle,
|
||||||
|
width = activeMon.spriteWidth,
|
||||||
|
height = activeMon.spriteHeight
|
||||||
|
),
|
||||||
|
multiplier = 8,
|
||||||
|
shape = androidx.compose.material.MaterialTheme.shapes.small,
|
||||||
|
modifier = Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.aspectRatio(1f)
|
||||||
|
)
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.weight(0.5f)
|
||||||
|
.aspectRatio(0.5f)
|
||||||
|
) {
|
||||||
|
ItemDisplay(
|
||||||
|
icon = R.drawable.baseline_vitals_24,
|
||||||
|
textValue = activeMon.vitalPoints.toString(),
|
||||||
|
definition = "Vitals",
|
||||||
|
modifier = Modifier
|
||||||
|
.weight(0.5f)
|
||||||
|
.aspectRatio(1f)
|
||||||
|
.padding(8.dp)
|
||||||
|
)
|
||||||
|
ItemDisplay(
|
||||||
|
icon = R.drawable.baseline_trophy_24,
|
||||||
|
textValue = activeMon.trophies.toString(),
|
||||||
|
definition = "Trophies",
|
||||||
|
modifier = Modifier
|
||||||
|
.weight(0.5f)
|
||||||
|
.aspectRatio(1f)
|
||||||
|
.padding(8.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
ItemDisplay(
|
||||||
|
icon = R.drawable.baseline_mood_24,
|
||||||
|
textValue = activeMon.mood.toString(),
|
||||||
|
definition = "Mood",
|
||||||
|
modifier = Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.aspectRatio(1f)
|
||||||
|
.padding(8.dp)
|
||||||
|
)
|
||||||
|
val timeInHours = (beData.remainingTrainingTimeInMinutes / 60)
|
||||||
|
ItemDisplay(
|
||||||
|
icon = R.drawable.baseline_timer_24,
|
||||||
|
textValue = "$timeInHours h",
|
||||||
|
definition = "Training limit",
|
||||||
|
modifier = Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.aspectRatio(1f)
|
||||||
|
.padding(8.dp)
|
||||||
|
)
|
||||||
|
// Maybe get rid of this?
|
||||||
|
ItemDisplay(
|
||||||
|
icon = R.drawable.baseline_rank_24,
|
||||||
|
textValue = beData.rank.toString(),
|
||||||
|
definition = "Rank",
|
||||||
|
modifier = Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.aspectRatio(1f)
|
||||||
|
.padding(8.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
val transformationCountdownInHours = activeMon.transformationCountdown / 60
|
||||||
|
ItemDisplay(
|
||||||
|
icon = R.drawable.baseline_next_24,
|
||||||
|
textValue = when (transformationCountdownInHours) {
|
||||||
|
0 -> "${activeMon.transformationCountdown} m"
|
||||||
|
else -> "$transformationCountdownInHours h"
|
||||||
|
},
|
||||||
|
definition = "Next timer",
|
||||||
|
modifier = Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.aspectRatio(1f)
|
||||||
|
.padding(8.dp)
|
||||||
|
)
|
||||||
|
ItemDisplay(
|
||||||
|
icon = R.drawable.baseline_swords_24,
|
||||||
|
textValue = when {
|
||||||
|
activeMon.totalBattlesLost == 0 -> "0.00 %"
|
||||||
|
else -> {
|
||||||
|
val battleWinPercentage =
|
||||||
|
activeMon.totalBattlesWon.toFloat() / (activeMon.totalBattlesWon + activeMon.totalBattlesLost).toFloat()
|
||||||
|
String.format(
|
||||||
|
Locale.getDefault(),
|
||||||
|
"%.2f",
|
||||||
|
battleWinPercentage * 100
|
||||||
|
) + " %" // Specify locale
|
||||||
|
}
|
||||||
|
},
|
||||||
|
definition = "Total battle win %",
|
||||||
|
modifier = Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.aspectRatio(1f)
|
||||||
|
.padding(8.dp)
|
||||||
|
)
|
||||||
|
ItemDisplay(
|
||||||
|
icon = R.drawable.baseline_swords_24,
|
||||||
|
textValue = when {
|
||||||
|
activeMon.totalBattlesLost == 0 -> "0.00 %"
|
||||||
|
else -> {
|
||||||
|
val battleWinPercentage =
|
||||||
|
activeMon.currentPhaseBattlesWon.toFloat() / (activeMon.currentPhaseBattlesWon + activeMon.currentPhaseBattlesLost).toFloat()
|
||||||
|
String.format(
|
||||||
|
Locale.getDefault(),
|
||||||
|
"%.2f",
|
||||||
|
battleWinPercentage * 100
|
||||||
|
) + " %" // Specify locale
|
||||||
|
}
|
||||||
|
},
|
||||||
|
definition = "Current phase win %",
|
||||||
|
modifier = Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.aspectRatio(1f)
|
||||||
|
.padding(8.dp)
|
||||||
|
)
|
||||||
|
if (beData.itemRemainingTime != 0) {
|
||||||
|
ItemDisplay(
|
||||||
|
icon = getIconResource(beData.itemType),
|
||||||
|
textValue = "${beData.itemRemainingTime} m",
|
||||||
|
definition = when (beData.itemType) {
|
||||||
|
ItemsScreenControllerImpl.ItemTypes.PPTraining.id -> "PP Training"
|
||||||
|
ItemsScreenControllerImpl.ItemTypes.HPTraining.id -> "HP Training"
|
||||||
|
ItemsScreenControllerImpl.ItemTypes.APTraining.id -> "AP Training"
|
||||||
|
ItemsScreenControllerImpl.ItemTypes.BPTraining.id -> "BP Training"
|
||||||
|
ItemsScreenControllerImpl.ItemTypes.AllTraining.id -> "All Training"
|
||||||
|
else -> ""
|
||||||
|
},
|
||||||
|
modifier = Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.aspectRatio(1f)
|
||||||
|
.padding(8.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
TransformationHistoryCard(
|
||||||
|
transformationHistory = transformationHistory,
|
||||||
|
modifier = Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.padding(8.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+189
@@ -0,0 +1,189 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.screens.homeScreens.screens
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.aspectRatio
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.rememberScrollState
|
||||||
|
import androidx.compose.foundation.verticalScroll
|
||||||
|
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.unit.sp
|
||||||
|
import com.github.nacabaro.vbhelper.R
|
||||||
|
import com.github.nacabaro.vbhelper.components.CharacterEntry
|
||||||
|
import com.github.nacabaro.vbhelper.components.ItemDisplay
|
||||||
|
import com.github.nacabaro.vbhelper.components.SpecialMissionsEntry
|
||||||
|
import com.github.nacabaro.vbhelper.components.TransformationHistoryCard
|
||||||
|
import com.github.nacabaro.vbhelper.domain.device_data.SpecialMissions
|
||||||
|
import com.github.nacabaro.vbhelper.domain.device_data.VBCharacterData
|
||||||
|
import com.github.nacabaro.vbhelper.dtos.CharacterDtos
|
||||||
|
import com.github.nacabaro.vbhelper.dtos.ItemDtos
|
||||||
|
import com.github.nacabaro.vbhelper.screens.homeScreens.HomeScreenControllerImpl
|
||||||
|
import com.github.nacabaro.vbhelper.utils.BitmapData
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun VBDiMHomeScreen(
|
||||||
|
activeMon: CharacterDtos.CharacterWithSprites,
|
||||||
|
vbData: VBCharacterData,
|
||||||
|
specialMissions: List<SpecialMissions>,
|
||||||
|
homeScreenController: HomeScreenControllerImpl,
|
||||||
|
transformationHistory: List<CharacterDtos.TransformationHistory>,
|
||||||
|
contentPadding: PaddingValues,
|
||||||
|
onClickCollect: (ItemDtos.PurchasedItem?) -> Unit
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(top = contentPadding.calculateTopPadding())
|
||||||
|
.verticalScroll(state = rememberScrollState())
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
CharacterEntry(
|
||||||
|
icon = BitmapData(
|
||||||
|
bitmap = activeMon.spriteIdle,
|
||||||
|
width = activeMon.spriteWidth,
|
||||||
|
height = activeMon.spriteHeight
|
||||||
|
),
|
||||||
|
multiplier = 8,
|
||||||
|
shape = androidx.compose.material.MaterialTheme.shapes.small,
|
||||||
|
modifier = Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.aspectRatio(1f)
|
||||||
|
)
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.weight(0.5f)
|
||||||
|
.aspectRatio(0.5f)
|
||||||
|
) {
|
||||||
|
ItemDisplay(
|
||||||
|
icon = R.drawable.baseline_vitals_24,
|
||||||
|
textValue = activeMon.vitalPoints.toString(),
|
||||||
|
definition = "Vitals",
|
||||||
|
modifier = Modifier
|
||||||
|
.weight(0.5f)
|
||||||
|
.aspectRatio(1f)
|
||||||
|
.padding(8.dp)
|
||||||
|
)
|
||||||
|
ItemDisplay(
|
||||||
|
icon = R.drawable.baseline_trophy_24,
|
||||||
|
textValue = activeMon.trophies.toString(),
|
||||||
|
definition = "Trophies",
|
||||||
|
modifier = Modifier
|
||||||
|
.weight(0.5f)
|
||||||
|
.aspectRatio(1f)
|
||||||
|
.padding(8.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
ItemDisplay(
|
||||||
|
icon = R.drawable.baseline_mood_24,
|
||||||
|
textValue = activeMon.mood.toString(),
|
||||||
|
definition = "Mood",
|
||||||
|
modifier = Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.aspectRatio(1f)
|
||||||
|
.padding(8.dp)
|
||||||
|
)
|
||||||
|
val transformationCountdownInHours = activeMon.transformationCountdown / 60
|
||||||
|
ItemDisplay(
|
||||||
|
icon = R.drawable.baseline_next_24,
|
||||||
|
textValue = when (transformationCountdownInHours) {
|
||||||
|
0 -> "${activeMon.transformationCountdown} m"
|
||||||
|
else -> "$transformationCountdownInHours h"
|
||||||
|
},
|
||||||
|
definition = "Next timer",
|
||||||
|
modifier = Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.aspectRatio(1f)
|
||||||
|
.padding(8.dp)
|
||||||
|
)
|
||||||
|
ItemDisplay(
|
||||||
|
icon = R.drawable.baseline_swords_24,
|
||||||
|
textValue = when {
|
||||||
|
activeMon.totalBattlesLost == 0 -> "0.00 %"
|
||||||
|
else -> {
|
||||||
|
val battleWinPercentage =
|
||||||
|
activeMon.totalBattlesWon.toFloat() / (activeMon.totalBattlesWon + activeMon.totalBattlesLost).toFloat()
|
||||||
|
String.format(
|
||||||
|
Locale.getDefault(),
|
||||||
|
"%.2f",
|
||||||
|
battleWinPercentage * 100
|
||||||
|
) + " %" // Specify locale
|
||||||
|
}
|
||||||
|
},
|
||||||
|
definition = "Total battle win %",
|
||||||
|
modifier = Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.aspectRatio(1f)
|
||||||
|
.padding(8.dp)
|
||||||
|
)
|
||||||
|
ItemDisplay(
|
||||||
|
icon = R.drawable.baseline_swords_24,
|
||||||
|
textValue = when {
|
||||||
|
activeMon.totalBattlesLost == 0 -> "0.00 %"
|
||||||
|
else -> {
|
||||||
|
val battleWinPercentage =
|
||||||
|
activeMon.currentPhaseBattlesWon.toFloat() / (activeMon.currentPhaseBattlesWon + activeMon.currentPhaseBattlesLost).toFloat()
|
||||||
|
String.format(
|
||||||
|
Locale.getDefault(),
|
||||||
|
"%.2f",
|
||||||
|
battleWinPercentage * 100
|
||||||
|
) + " %" // Specify locale
|
||||||
|
}
|
||||||
|
},
|
||||||
|
definition = "Current phase win %",
|
||||||
|
modifier = Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.aspectRatio(1f)
|
||||||
|
.padding(8.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
TransformationHistoryCard(
|
||||||
|
transformationHistory = transformationHistory,
|
||||||
|
modifier = Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.padding(8.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Row (
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(16.dp)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = "Special missions",
|
||||||
|
fontSize = 24.sp
|
||||||
|
)
|
||||||
|
}
|
||||||
|
for (mission in specialMissions) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
SpecialMissionsEntry(
|
||||||
|
specialMission = mission,
|
||||||
|
modifier = Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.padding(8.dp),
|
||||||
|
) {
|
||||||
|
homeScreenController
|
||||||
|
.clearSpecialMission(mission.id, mission.status, onClickCollect)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+107
@@ -0,0 +1,107 @@
|
|||||||
|
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.domain.items.ItemType
|
||||||
|
import com.github.nacabaro.vbhelper.dtos.CharacterDtos
|
||||||
|
import com.github.nacabaro.vbhelper.dtos.ItemDtos
|
||||||
|
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) }
|
||||||
|
var selectedItem by remember { mutableStateOf<ItemDtos.ItemsWithQuantities?>(null) }
|
||||||
|
|
||||||
|
LaunchedEffect(storageRepository) {
|
||||||
|
coroutineScope.launch {
|
||||||
|
selectedItem = storageRepository.getItem(itemId)
|
||||||
|
when (selectedItem?.itemType) {
|
||||||
|
ItemType.BEITEM -> {
|
||||||
|
characterList.value = storageRepository.getBECharacters()
|
||||||
|
}
|
||||||
|
ItemType.VBITEM -> {
|
||||||
|
characterList.value = storageRepository.getVBCharacters()
|
||||||
|
}
|
||||||
|
ItemType.SPECIALMISSION-> {
|
||||||
|
characterList.value = storageRepository.getVBCharacters()
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
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)
|
||||||
|
}
|
||||||
+201
@@ -0,0 +1,201 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.screens.itemsScreen
|
||||||
|
|
||||||
|
import androidx.activity.ComponentActivity
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import com.github.cfogrady.vbnfc.vb.SpecialMission
|
||||||
|
import com.github.nacabaro.vbhelper.domain.device_data.SpecialMissions
|
||||||
|
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.domain.device_data.VBCharacterData
|
||||||
|
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),
|
||||||
|
Step8k(9),
|
||||||
|
Step4k(10),
|
||||||
|
Vitals1000(11),
|
||||||
|
Vitals250(12),
|
||||||
|
Battle20(13),
|
||||||
|
Battle5(14),
|
||||||
|
Win10(15),
|
||||||
|
Win4(16)
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
var beCharacterData: BECharacterData? = null
|
||||||
|
var vbCharacterData: VBCharacterData? = null
|
||||||
|
|
||||||
|
if (characterData.characterType == DeviceType.BEDevice) {
|
||||||
|
beCharacterData = database.userCharacterDao().getBeData(characterId)
|
||||||
|
} else if (characterData.characterType == DeviceType.VBDevice) {
|
||||||
|
vbCharacterData = database.userCharacterDao().getVbData(characterId)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
item.itemIcon in 1 .. 5 &&
|
||||||
|
characterData.characterType == DeviceType.BEDevice &&
|
||||||
|
beCharacterData != null
|
||||||
|
) {
|
||||||
|
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 &&
|
||||||
|
characterData.characterType == DeviceType.BEDevice &&
|
||||||
|
beCharacterData != null
|
||||||
|
) {
|
||||||
|
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)
|
||||||
|
|
||||||
|
} else if (item.itemIcon in ItemTypes.Step8k.id .. ItemTypes.Win4.id &&
|
||||||
|
characterData.characterType == DeviceType.VBDevice &&
|
||||||
|
vbCharacterData != null
|
||||||
|
) {
|
||||||
|
applySpecialMission(item.itemIcon, item.itemLength, characterId)
|
||||||
|
}
|
||||||
|
|
||||||
|
consumeItem(item.id)
|
||||||
|
|
||||||
|
context.runOnUiThread {
|
||||||
|
onCompletion()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun applySpecialMission(itemIcon: Int, itemLength: Int, characterId: Long) {
|
||||||
|
// Hello, it's me, naca! No! I don't like this, I'll see how I can improve it later on...
|
||||||
|
val specialMissionType = when (itemIcon) {
|
||||||
|
ItemTypes.Step8k.id -> SpecialMission.Type.STEPS
|
||||||
|
ItemTypes.Step4k.id -> SpecialMission.Type.STEPS
|
||||||
|
ItemTypes.Vitals1000.id -> SpecialMission.Type.VITALS
|
||||||
|
ItemTypes.Vitals250.id -> SpecialMission.Type.VITALS
|
||||||
|
ItemTypes.Battle20.id -> SpecialMission.Type.BATTLES
|
||||||
|
ItemTypes.Battle5.id -> SpecialMission.Type.BATTLES
|
||||||
|
ItemTypes.Win10.id -> SpecialMission.Type.WINS
|
||||||
|
ItemTypes.Win4.id -> SpecialMission.Type.WINS
|
||||||
|
else -> SpecialMission.Type.NONE
|
||||||
|
}
|
||||||
|
|
||||||
|
val specialMissionGoal = when (itemIcon) {
|
||||||
|
ItemTypes.Step8k.id -> 8000
|
||||||
|
ItemTypes.Step4k.id -> 4000
|
||||||
|
ItemTypes.Vitals1000.id -> 1000
|
||||||
|
ItemTypes.Vitals250.id -> 250
|
||||||
|
ItemTypes.Battle20.id -> 20
|
||||||
|
ItemTypes.Battle5.id -> 5
|
||||||
|
ItemTypes.Win10.id -> 10
|
||||||
|
ItemTypes.Win4.id -> 4
|
||||||
|
else -> 0
|
||||||
|
}
|
||||||
|
|
||||||
|
val availableSpecialMissions = database
|
||||||
|
.userCharacterDao()
|
||||||
|
.getSpecialMissions(characterId)
|
||||||
|
|
||||||
|
var firstUnavailableMissionSlot: Long = 0
|
||||||
|
var watchId = 0
|
||||||
|
|
||||||
|
for ((index, mission) in availableSpecialMissions.withIndex()) {
|
||||||
|
if (
|
||||||
|
mission.status == SpecialMission.Status.UNAVAILABLE
|
||||||
|
) {
|
||||||
|
firstUnavailableMissionSlot = mission.id
|
||||||
|
watchId = index + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val newSpecialMission = SpecialMissions(
|
||||||
|
id = firstUnavailableMissionSlot,
|
||||||
|
characterId = characterId,
|
||||||
|
missionType = specialMissionType,
|
||||||
|
goal = specialMissionGoal,
|
||||||
|
timeLimitInMinutes = itemLength,
|
||||||
|
watchId = watchId,
|
||||||
|
status = SpecialMission.Status.AVAILABLE,
|
||||||
|
progress = 0,
|
||||||
|
timeElapsedInMinutes = 0
|
||||||
|
)
|
||||||
|
|
||||||
|
database
|
||||||
|
.userCharacterDao()
|
||||||
|
.insertSpecialMissions(newSpecialMission)
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun getItem(itemId: Long): ItemDtos.ItemsWithQuantities {
|
||||||
|
return database
|
||||||
|
.itemDao()
|
||||||
|
.getItem(itemId)
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend 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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+2
-1
@@ -15,11 +15,12 @@ import com.github.nacabaro.vbhelper.components.TopBanner
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ReadingCharacterScreen(
|
fun ReadingCharacterScreen(
|
||||||
|
topBannerText: String,
|
||||||
onClickCancel: () -> Unit,
|
onClickCancel: () -> Unit,
|
||||||
) {
|
) {
|
||||||
Scaffold (
|
Scaffold (
|
||||||
topBar = {
|
topBar = {
|
||||||
TopBanner("Reading Character")
|
TopBanner(topBannerText)
|
||||||
}
|
}
|
||||||
) { innerPadding ->
|
) { innerPadding ->
|
||||||
Column (
|
Column (
|
||||||
|
|||||||
@@ -0,0 +1,335 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.screens.scanScreen
|
||||||
|
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.material3.Button
|
||||||
|
import androidx.compose.material3.Scaffold
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.DisposableEffect
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
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.tooling.preview.Preview
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
|
import androidx.navigation.NavController
|
||||||
|
import androidx.navigation.compose.rememberNavController
|
||||||
|
import com.github.cfogrady.vbnfc.data.NfcCharacter
|
||||||
|
import com.github.nacabaro.vbhelper.ActivityLifecycleListener
|
||||||
|
import com.github.nacabaro.vbhelper.components.TopBanner
|
||||||
|
import com.github.nacabaro.vbhelper.di.VBHelper
|
||||||
|
import com.github.nacabaro.vbhelper.domain.card.Card
|
||||||
|
import com.github.nacabaro.vbhelper.navigation.NavigationItems
|
||||||
|
import com.github.nacabaro.vbhelper.screens.cardScreen.ChooseCard
|
||||||
|
import com.github.nacabaro.vbhelper.source.StorageRepository
|
||||||
|
import com.github.nacabaro.vbhelper.source.isMissingSecrets
|
||||||
|
import com.github.nacabaro.vbhelper.source.proto.Secrets
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
|
const val SCAN_SCREEN_ACTIVITY_LIFECYCLE_LISTENER = "SCAN_SCREEN_ACTIVITY_LIFECYCLE_LISTENER"
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ScanScreen(
|
||||||
|
navController: NavController,
|
||||||
|
characterId: Long?,
|
||||||
|
scanScreenController: ScanScreenController,
|
||||||
|
launchedFromHomeScreen: Boolean
|
||||||
|
) {
|
||||||
|
val secrets by scanScreenController.secretsFlow.collectAsState(null)
|
||||||
|
|
||||||
|
val application = LocalContext.current.applicationContext as VBHelper
|
||||||
|
val storageRepository = StorageRepository(application.container.db)
|
||||||
|
var nfcCharacter by remember { mutableStateOf<NfcCharacter?>(null) }
|
||||||
|
|
||||||
|
var cardsRead by remember { mutableStateOf<List<Card>?>(null) }
|
||||||
|
|
||||||
|
val context = LocalContext.current
|
||||||
|
|
||||||
|
LaunchedEffect(storageRepository) {
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
/*
|
||||||
|
First check if there is a character sent through the navigation system
|
||||||
|
If there is not, that means we got here through the home screen nfc button
|
||||||
|
If we got here through the home screen, it does not hurt to check if there is
|
||||||
|
an active character.
|
||||||
|
*/
|
||||||
|
if (characterId != null && nfcCharacter == null) {
|
||||||
|
nfcCharacter = scanScreenController.characterToNfc(characterId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var readingScreen by remember { mutableStateOf(false) }
|
||||||
|
var writingScreen by remember { mutableStateOf(false) }
|
||||||
|
var cardSelectScreen by remember { mutableStateOf(false) }
|
||||||
|
var isDoneReadingCharacter by remember { mutableStateOf(false) }
|
||||||
|
var isDoneSendingCard by remember { mutableStateOf(false) }
|
||||||
|
var isDoneWritingCharacter by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
|
DisposableEffect(readingScreen) {
|
||||||
|
if(readingScreen) {
|
||||||
|
scanScreenController.registerActivityLifecycleListener(
|
||||||
|
SCAN_SCREEN_ACTIVITY_LIFECYCLE_LISTENER,
|
||||||
|
object: ActivityLifecycleListener {
|
||||||
|
override fun onPause() {
|
||||||
|
scanScreenController.cancelRead()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
scanScreenController.onClickRead(
|
||||||
|
secrets = secrets!!,
|
||||||
|
onComplete = {
|
||||||
|
isDoneReadingCharacter = true
|
||||||
|
},
|
||||||
|
onMultipleCards = { cards ->
|
||||||
|
cardsRead = cards
|
||||||
|
readingScreen = false
|
||||||
|
cardSelectScreen = true
|
||||||
|
isDoneReadingCharacter = true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
scanScreenController.onClickRead(
|
||||||
|
secrets = secrets!!,
|
||||||
|
onComplete = {
|
||||||
|
isDoneReadingCharacter = true
|
||||||
|
},
|
||||||
|
onMultipleCards = { cards ->
|
||||||
|
cardsRead = cards
|
||||||
|
readingScreen = false
|
||||||
|
cardSelectScreen = true
|
||||||
|
isDoneReadingCharacter = true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
onDispose {
|
||||||
|
if(readingScreen) {
|
||||||
|
scanScreenController.unregisterActivityLifecycleListener(
|
||||||
|
SCAN_SCREEN_ACTIVITY_LIFECYCLE_LISTENER
|
||||||
|
)
|
||||||
|
scanScreenController.cancelRead()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DisposableEffect(writingScreen, isDoneSendingCard) {
|
||||||
|
if (writingScreen) {
|
||||||
|
scanScreenController.registerActivityLifecycleListener(
|
||||||
|
SCAN_SCREEN_ACTIVITY_LIFECYCLE_LISTENER,
|
||||||
|
object : ActivityLifecycleListener {
|
||||||
|
override fun onPause() {
|
||||||
|
scanScreenController.cancelRead()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
if (!isDoneSendingCard) {
|
||||||
|
scanScreenController.onClickCheckCard(secrets!!, nfcCharacter!!) {
|
||||||
|
isDoneSendingCard = true
|
||||||
|
}
|
||||||
|
} else if (!isDoneWritingCharacter) {
|
||||||
|
scanScreenController.onClickWrite(secrets!!, nfcCharacter!!) {
|
||||||
|
isDoneWritingCharacter = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (secrets != null && nfcCharacter != null) {
|
||||||
|
if (!isDoneSendingCard) {
|
||||||
|
scanScreenController.onClickCheckCard(secrets!!, nfcCharacter!!) {
|
||||||
|
isDoneSendingCard = true
|
||||||
|
}
|
||||||
|
} else if (!isDoneWritingCharacter) {
|
||||||
|
scanScreenController.onClickWrite(secrets!!, nfcCharacter!!) {
|
||||||
|
isDoneWritingCharacter = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onDispose {
|
||||||
|
if(writingScreen) {
|
||||||
|
scanScreenController.unregisterActivityLifecycleListener(SCAN_SCREEN_ACTIVITY_LIFECYCLE_LISTENER)
|
||||||
|
scanScreenController.cancelRead()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isDoneReadingCharacter && !cardSelectScreen) {
|
||||||
|
readingScreen = false
|
||||||
|
navController.navigate(NavigationItems.Home.route)
|
||||||
|
} else if (isDoneSendingCard && isDoneWritingCharacter) {
|
||||||
|
writingScreen = false
|
||||||
|
navController.navigate(NavigationItems.Home.route)
|
||||||
|
LaunchedEffect(storageRepository) {
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
storageRepository
|
||||||
|
.deleteCharacter(characterId!!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (readingScreen) {
|
||||||
|
ReadingCharacterScreen("Reading character") {
|
||||||
|
readingScreen = false
|
||||||
|
scanScreenController.cancelRead()
|
||||||
|
}
|
||||||
|
} else if (writingScreen) {
|
||||||
|
if (!isDoneSendingCard) {
|
||||||
|
ReadingCharacterScreen("Sending card") {
|
||||||
|
writingScreen = false
|
||||||
|
scanScreenController.cancelRead()
|
||||||
|
}
|
||||||
|
} else if (!isDoneWritingCharacter) {
|
||||||
|
ReadingCharacterScreen("Writing character") {
|
||||||
|
isDoneSendingCard = false
|
||||||
|
writingScreen = false
|
||||||
|
scanScreenController.cancelRead()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (cardSelectScreen) {
|
||||||
|
ChooseCard(
|
||||||
|
cards = cardsRead!!,
|
||||||
|
onCardSelected = { card ->
|
||||||
|
cardSelectScreen = false
|
||||||
|
scanScreenController.flushCharacter(card.id)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
ChooseConnectOption(
|
||||||
|
onClickRead = when {
|
||||||
|
!launchedFromHomeScreen -> null
|
||||||
|
else -> {
|
||||||
|
{
|
||||||
|
if(secrets == null) {
|
||||||
|
Toast.makeText(context, "Secrets is not yet initialized. Try again.", Toast.LENGTH_SHORT).show()
|
||||||
|
} else if(secrets?.isMissingSecrets() == true) {
|
||||||
|
Toast.makeText(context, "Secrets not yet imported. Go to Settings and Import APK", Toast.LENGTH_SHORT).show()
|
||||||
|
} else {
|
||||||
|
readingScreen = true // kicks off nfc adapter in DisposableEffect
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onClickWrite = when {
|
||||||
|
nfcCharacter == null -> null
|
||||||
|
else -> {
|
||||||
|
{
|
||||||
|
if(secrets == null) {
|
||||||
|
Toast.makeText(context, "Secrets is not yet initialized. Try again.", Toast.LENGTH_SHORT).show()
|
||||||
|
} else if(secrets?.isMissingSecrets() == true) {
|
||||||
|
Toast.makeText(context, "Secrets not yet imported. Go to Settings and Import APK", Toast.LENGTH_SHORT).show()
|
||||||
|
} else {
|
||||||
|
writingScreen = true // kicks off nfc adapter in DisposableEffect
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
navController = navController
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ChooseConnectOption(
|
||||||
|
onClickRead: (() -> Unit)? = null,
|
||||||
|
onClickWrite: (() -> Unit)? = null,
|
||||||
|
navController: NavController
|
||||||
|
) {
|
||||||
|
Scaffold(
|
||||||
|
topBar = {
|
||||||
|
TopBanner(
|
||||||
|
text = "Scan a Vital Bracelet",
|
||||||
|
onBackClick = {
|
||||||
|
navController.popBackStack()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
) { contentPadding ->
|
||||||
|
Column(
|
||||||
|
verticalArrangement = Arrangement.Center,
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(contentPadding)
|
||||||
|
) {
|
||||||
|
ScanButton(
|
||||||
|
text = "Vital Bracelet to App",
|
||||||
|
disabled = onClickRead == null,
|
||||||
|
onClick = onClickRead?: { },
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
ScanButton(
|
||||||
|
text = "App to Vital Bracelet",
|
||||||
|
disabled = onClickWrite == null,
|
||||||
|
onClick = onClickWrite?: { },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ScanButton(
|
||||||
|
text: String,
|
||||||
|
onClick: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
disabled: Boolean = false,
|
||||||
|
) {
|
||||||
|
Button(
|
||||||
|
onClick = onClick,
|
||||||
|
modifier = modifier,
|
||||||
|
enabled = !disabled,
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = text,
|
||||||
|
fontSize = 16.sp,
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(4.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview(showBackground = true)
|
||||||
|
@Composable
|
||||||
|
fun ScanScreenPreview() {
|
||||||
|
ScanScreen(
|
||||||
|
navController = rememberNavController(),
|
||||||
|
scanScreenController = object: ScanScreenController {
|
||||||
|
override val secretsFlow = MutableStateFlow<Secrets>(Secrets.getDefaultInstance())
|
||||||
|
override fun unregisterActivityLifecycleListener(key: String) { }
|
||||||
|
override fun registerActivityLifecycleListener(
|
||||||
|
key: String,
|
||||||
|
activityLifecycleListener: ActivityLifecycleListener
|
||||||
|
) {
|
||||||
|
|
||||||
|
}
|
||||||
|
override fun flushCharacter(cardId: Long) {}
|
||||||
|
override fun onClickRead(secrets: Secrets, onComplete: ()->Unit, onMultipleCards: (List<Card>) -> Unit) {}
|
||||||
|
override fun onClickCheckCard(secrets: Secrets, nfcCharacter: NfcCharacter, onComplete: () -> Unit) {}
|
||||||
|
override fun onClickWrite(secrets: Secrets, nfcCharacter: NfcCharacter, onComplete: () -> Unit) {}
|
||||||
|
override fun cancelRead() {}
|
||||||
|
override fun characterFromNfc(nfcCharacter: NfcCharacter, onMultipleCards: (List<Card>, NfcCharacter) -> Unit): String { return "" }
|
||||||
|
override suspend fun characterToNfc(characterId: Long): NfcCharacter? { return null }
|
||||||
|
},
|
||||||
|
characterId = null,
|
||||||
|
launchedFromHomeScreen = false
|
||||||
|
)
|
||||||
|
}
|
||||||
+27
@@ -0,0 +1,27 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.screens.scanScreen
|
||||||
|
|
||||||
|
import com.github.cfogrady.vbnfc.data.NfcCharacter
|
||||||
|
import com.github.nacabaro.vbhelper.ActivityLifecycleListener
|
||||||
|
import com.github.nacabaro.vbhelper.domain.card.Card
|
||||||
|
import com.github.nacabaro.vbhelper.source.proto.Secrets
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
|
interface ScanScreenController {
|
||||||
|
val secretsFlow: Flow<Secrets>
|
||||||
|
fun onClickRead(secrets: Secrets, onComplete: ()->Unit, onMultipleCards: (List<Card>) -> Unit)
|
||||||
|
fun onClickCheckCard(secrets: Secrets, nfcCharacter: NfcCharacter, onComplete: () -> Unit)
|
||||||
|
fun onClickWrite(secrets: Secrets, nfcCharacter: NfcCharacter, onComplete: () -> Unit)
|
||||||
|
|
||||||
|
fun cancelRead()
|
||||||
|
|
||||||
|
fun registerActivityLifecycleListener(key: String, activityLifecycleListener: ActivityLifecycleListener)
|
||||||
|
fun unregisterActivityLifecycleListener(key: String)
|
||||||
|
|
||||||
|
fun flushCharacter(cardId: Long)
|
||||||
|
|
||||||
|
fun characterFromNfc(
|
||||||
|
nfcCharacter: NfcCharacter,
|
||||||
|
onMultipleCards: (List<Card>, NfcCharacter) -> Unit
|
||||||
|
): String
|
||||||
|
suspend fun characterToNfc(characterId: Long): NfcCharacter?
|
||||||
|
}
|
||||||
+196
@@ -0,0 +1,196 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.screens.scanScreen
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
|
import android.nfc.NfcAdapter
|
||||||
|
import android.nfc.Tag
|
||||||
|
import android.nfc.tech.NfcA
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.provider.Settings
|
||||||
|
import android.util.Log
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.activity.ComponentActivity
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
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.vb.VBNfcCharacter
|
||||||
|
import com.github.nacabaro.vbhelper.ActivityLifecycleListener
|
||||||
|
import com.github.nacabaro.vbhelper.domain.card.Card
|
||||||
|
import com.github.nacabaro.vbhelper.screens.scanScreen.converters.FromNfcConverter
|
||||||
|
import com.github.nacabaro.vbhelper.screens.scanScreen.converters.ToNfcConverter
|
||||||
|
import com.github.nacabaro.vbhelper.source.getCryptographicTransformerMap
|
||||||
|
import com.github.nacabaro.vbhelper.source.isMissingSecrets
|
||||||
|
import com.github.nacabaro.vbhelper.source.proto.Secrets
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.stateIn
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
class ScanScreenControllerImpl(
|
||||||
|
override val secretsFlow: Flow<Secrets>,
|
||||||
|
private val componentActivity: ComponentActivity,
|
||||||
|
private val registerActivityLifecycleListener: (String, ActivityLifecycleListener)->Unit,
|
||||||
|
private val unregisterActivityLifecycleListener: (String)->Unit,
|
||||||
|
): ScanScreenController {
|
||||||
|
private var lastScannedCharacter: NfcCharacter? = null
|
||||||
|
private val nfcAdapter: NfcAdapter
|
||||||
|
|
||||||
|
init {
|
||||||
|
val maybeNfcAdapter = NfcAdapter.getDefaultAdapter(componentActivity)
|
||||||
|
if (maybeNfcAdapter == null) {
|
||||||
|
Toast.makeText(componentActivity, "No NFC on device!", Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
nfcAdapter = maybeNfcAdapter
|
||||||
|
checkSecrets()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onClickRead(secrets: Secrets, onComplete: ()->Unit, onMultipleCards: (List<Card>) -> Unit) {
|
||||||
|
handleTag(secrets) { tagCommunicator ->
|
||||||
|
val character = tagCommunicator.receiveCharacter()
|
||||||
|
val resultMessage = characterFromNfc(character) { cards, nfcCharacter ->
|
||||||
|
lastScannedCharacter = nfcCharacter
|
||||||
|
onMultipleCards(cards)
|
||||||
|
|
||||||
|
}
|
||||||
|
onComplete.invoke()
|
||||||
|
resultMessage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun cancelRead() {
|
||||||
|
if(nfcAdapter.isEnabled) {
|
||||||
|
nfcAdapter.disableReaderMode(componentActivity)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun registerActivityLifecycleListener(
|
||||||
|
key: String,
|
||||||
|
activityLifecycleListener: ActivityLifecycleListener
|
||||||
|
) {
|
||||||
|
registerActivityLifecycleListener.invoke(key, activityLifecycleListener)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun unregisterActivityLifecycleListener(key: String) {
|
||||||
|
unregisterActivityLifecycleListener.invoke(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXTRACTED DIRECTLY FROM EXAMPLE APP
|
||||||
|
private fun handleTag(secrets: Secrets, handlerFunc: (TagCommunicator)->String) {
|
||||||
|
if (!nfcAdapter.isEnabled) {
|
||||||
|
showWirelessSettings()
|
||||||
|
} else {
|
||||||
|
val options = Bundle()
|
||||||
|
// Work around for some broken Nfc firmware implementations that poll the card too fast
|
||||||
|
options.putInt(NfcAdapter.EXTRA_READER_PRESENCE_CHECK_DELAY, 250)
|
||||||
|
nfcAdapter.enableReaderMode(componentActivity, buildOnReadTag(secrets, handlerFunc), NfcAdapter.FLAG_READER_NFC_A or NfcAdapter.FLAG_READER_SKIP_NDEF_CHECK,
|
||||||
|
options
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXTRACTED DIRECTLY FROM EXAMPLE APP
|
||||||
|
private fun buildOnReadTag(secrets: Secrets, handlerFunc: (TagCommunicator)->String): (Tag)->Unit {
|
||||||
|
return { tag->
|
||||||
|
val nfcData = NfcA.get(tag)
|
||||||
|
if (nfcData == null) {
|
||||||
|
componentActivity.runOnUiThread {
|
||||||
|
Toast.makeText(componentActivity, "Tag detected is not VB", Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nfcData.connect()
|
||||||
|
nfcData.use {
|
||||||
|
val tagCommunicator = TagCommunicator.getInstance(nfcData, secrets.getCryptographicTransformerMap())
|
||||||
|
val successText = handlerFunc(tagCommunicator)
|
||||||
|
componentActivity.runOnUiThread {
|
||||||
|
Toast.makeText(componentActivity, successText, Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun checkSecrets() {
|
||||||
|
componentActivity.lifecycleScope.launch(Dispatchers.IO) {
|
||||||
|
if(secretsFlow.stateIn(componentActivity.lifecycleScope).value.isMissingSecrets()) {
|
||||||
|
componentActivity.runOnUiThread {
|
||||||
|
Toast.makeText(componentActivity, "Missing Secrets. Go to settings and import Vital Arena APK", Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onClickWrite(
|
||||||
|
secrets: Secrets,
|
||||||
|
nfcCharacter: NfcCharacter,
|
||||||
|
onComplete: () -> Unit
|
||||||
|
) {
|
||||||
|
handleTag(secrets) { tagCommunicator ->
|
||||||
|
try {
|
||||||
|
if (nfcCharacter is VBNfcCharacter) {
|
||||||
|
Log.d("SendCharacter", "VBNfcCharacter")
|
||||||
|
val castNfcCharacter: VBNfcCharacter = nfcCharacter
|
||||||
|
tagCommunicator.sendCharacter(castNfcCharacter)
|
||||||
|
} else if (nfcCharacter is BENfcCharacter) {
|
||||||
|
Log.d("SendCharacter", "BENfcCharacter")
|
||||||
|
val castNfcCharacter: BENfcCharacter = nfcCharacter
|
||||||
|
tagCommunicator.sendCharacter(castNfcCharacter)
|
||||||
|
}
|
||||||
|
onComplete.invoke()
|
||||||
|
"Sent character successfully!"
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
Log.e("TAG", e.stackTraceToString())
|
||||||
|
"Whoops"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onClickCheckCard(
|
||||||
|
secrets: Secrets,
|
||||||
|
nfcCharacter: NfcCharacter,
|
||||||
|
onComplete: () -> Unit
|
||||||
|
) {
|
||||||
|
handleTag(secrets) { tagCommunicator ->
|
||||||
|
tagCommunicator.prepareDIMForCharacter(nfcCharacter.dimId)
|
||||||
|
onComplete.invoke()
|
||||||
|
"Sent DIM successfully!"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXTRACTED DIRECTLY FROM EXAMPLE APP
|
||||||
|
private fun showWirelessSettings() {
|
||||||
|
Toast.makeText(componentActivity, "NFC must be enabled", Toast.LENGTH_SHORT).show()
|
||||||
|
componentActivity.startActivity(Intent(Settings.ACTION_WIRELESS_SETTINGS))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun characterFromNfc(
|
||||||
|
nfcCharacter: NfcCharacter,
|
||||||
|
onMultipleCards: (List<Card>, NfcCharacter) -> Unit
|
||||||
|
): String {
|
||||||
|
val nfcConverter = FromNfcConverter(
|
||||||
|
componentActivity = componentActivity
|
||||||
|
)
|
||||||
|
return nfcConverter.addCharacter(nfcCharacter, onMultipleCards)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun characterToNfc(characterId: Long): NfcCharacter {
|
||||||
|
val nfcGenerator = ToNfcConverter(
|
||||||
|
componentActivity = componentActivity
|
||||||
|
)
|
||||||
|
|
||||||
|
val character = nfcGenerator.characterToNfc(characterId)
|
||||||
|
Log.d("CharacterType", character.toString())
|
||||||
|
return character
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun flushCharacter(cardId: Long) {
|
||||||
|
val nfcConverter = FromNfcConverter(
|
||||||
|
componentActivity = componentActivity
|
||||||
|
)
|
||||||
|
|
||||||
|
componentActivity.lifecycleScope.launch(Dispatchers.IO) {
|
||||||
|
if (lastScannedCharacter != null) {
|
||||||
|
nfcConverter.addCharacterUsingCard(lastScannedCharacter!!, cardId)
|
||||||
|
lastScannedCharacter = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+48
@@ -0,0 +1,48 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.screens.cardScreen
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.items
|
||||||
|
import androidx.compose.material3.Scaffold
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import com.github.nacabaro.vbhelper.components.TopBanner
|
||||||
|
import com.github.nacabaro.vbhelper.domain.card.Card
|
||||||
|
import com.github.nacabaro.vbhelper.screens.scanScreen.cardSelect.ScanCardEntry
|
||||||
|
import com.github.nacabaro.vbhelper.utils.BitmapData
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ChooseCard(
|
||||||
|
cards: List<Card>,
|
||||||
|
onCardSelected: (Card) -> Unit
|
||||||
|
) {
|
||||||
|
Scaffold (
|
||||||
|
topBar = {
|
||||||
|
TopBanner(
|
||||||
|
text = "Choose card",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
) { contentPadding ->
|
||||||
|
LazyColumn (
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(top = contentPadding.calculateTopPadding())
|
||||||
|
) {
|
||||||
|
items(cards) {
|
||||||
|
ScanCardEntry(
|
||||||
|
name = it.name,
|
||||||
|
logo = BitmapData(
|
||||||
|
it.logo,
|
||||||
|
it.logoWidth,
|
||||||
|
it.logoHeight
|
||||||
|
),
|
||||||
|
onClick = {
|
||||||
|
onCardSelected(it)
|
||||||
|
},
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(8.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+17
-13
@@ -1,8 +1,8 @@
|
|||||||
package com.github.nacabaro.vbhelper.components
|
package com.github.nacabaro.vbhelper.screens.scanScreen.cardSelect
|
||||||
|
|
||||||
import android.graphics.Bitmap
|
|
||||||
import androidx.compose.foundation.Image
|
import androidx.compose.foundation.Image
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
@@ -15,23 +15,22 @@ import androidx.compose.ui.Alignment
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
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.platform.LocalContext
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.github.nacabaro.vbhelper.utils.BitmapData
|
import com.github.nacabaro.vbhelper.utils.BitmapData
|
||||||
import java.nio.ByteBuffer
|
import com.github.nacabaro.vbhelper.utils.getBitmap
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun DexDiMEntry(
|
fun ScanCardEntry(
|
||||||
name: String,
|
name: String,
|
||||||
logo: BitmapData,
|
logo: BitmapData,
|
||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
val bitmap = remember (logo.bitmap) {
|
val bitmap = remember (logo.bitmap) { logo.getBitmap() }
|
||||||
Bitmap.createBitmap(logo.width, logo.height, Bitmap.Config.RGB_565).apply {
|
|
||||||
copyPixelsFromBuffer(ByteBuffer.wrap(logo.bitmap))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val imageBitmap = remember(bitmap) { bitmap.asImageBitmap() }
|
val imageBitmap = remember(bitmap) { bitmap.asImageBitmap() }
|
||||||
|
val density: Float = LocalContext.current.resources.displayMetrics.density
|
||||||
|
val dpSize = (logo.width * 4 / density).dp
|
||||||
|
|
||||||
Card (
|
Card (
|
||||||
shape = MaterialTheme.shapes.medium,
|
shape = MaterialTheme.shapes.medium,
|
||||||
@@ -50,13 +49,18 @@ fun DexDiMEntry(
|
|||||||
filterQuality = FilterQuality.None,
|
filterQuality = FilterQuality.None,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(8.dp)
|
.padding(8.dp)
|
||||||
.size(64.dp)
|
.size(dpSize)
|
||||||
)
|
)
|
||||||
Text(
|
Column(
|
||||||
text = name,
|
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(8.dp)
|
.padding(8.dp)
|
||||||
)
|
.weight(1f)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = name,
|
||||||
|
modifier = Modifier
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+306
@@ -0,0 +1,306 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.screens.scanScreen.converters
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.activity.ComponentActivity
|
||||||
|
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.domain.card.Card
|
||||||
|
import com.github.nacabaro.vbhelper.domain.card.CardProgress
|
||||||
|
import com.github.nacabaro.vbhelper.domain.device_data.BECharacterData
|
||||||
|
import com.github.nacabaro.vbhelper.domain.device_data.SpecialMissions
|
||||||
|
import com.github.nacabaro.vbhelper.domain.device_data.UserCharacter
|
||||||
|
import com.github.nacabaro.vbhelper.domain.device_data.VBCharacterData
|
||||||
|
import com.github.nacabaro.vbhelper.domain.device_data.VitalsHistory
|
||||||
|
import com.github.nacabaro.vbhelper.utils.DeviceType
|
||||||
|
import java.util.GregorianCalendar
|
||||||
|
|
||||||
|
class FromNfcConverter (
|
||||||
|
componentActivity: ComponentActivity
|
||||||
|
) {
|
||||||
|
private val application = componentActivity.applicationContext as VBHelper
|
||||||
|
private val database = application.container.db
|
||||||
|
|
||||||
|
|
||||||
|
fun addCharacterUsingCard(
|
||||||
|
nfcCharacter: NfcCharacter,
|
||||||
|
cardId: Long
|
||||||
|
): String {
|
||||||
|
val cardData = database
|
||||||
|
.cardDao()
|
||||||
|
.getCardById(cardId)
|
||||||
|
|
||||||
|
if (cardData == null) {
|
||||||
|
return "Card not found"
|
||||||
|
}
|
||||||
|
|
||||||
|
return insertCharacter(nfcCharacter, cardData)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun addCharacter(
|
||||||
|
nfcCharacter: NfcCharacter,
|
||||||
|
onMultipleCards: (List<Card>, NfcCharacter) -> Unit
|
||||||
|
): String {
|
||||||
|
val appReservedCardId = nfcCharacter
|
||||||
|
.appReserved2[0].toLong()
|
||||||
|
|
||||||
|
var cardData: Card? = null
|
||||||
|
|
||||||
|
if (appReservedCardId != 0L) {
|
||||||
|
val fetchedCard = database
|
||||||
|
.cardDao()
|
||||||
|
.getCardById(appReservedCardId)
|
||||||
|
|
||||||
|
if (fetchedCard == null) {
|
||||||
|
return "Card not found"
|
||||||
|
} else if (fetchedCard.cardId == nfcCharacter.dimId.toInt()) {
|
||||||
|
cardData = fetchedCard
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cardData == null) {
|
||||||
|
val allCards = database
|
||||||
|
.cardDao()
|
||||||
|
.getCardByCardId(nfcCharacter.dimId.toInt())
|
||||||
|
|
||||||
|
if (allCards.isEmpty())
|
||||||
|
return "Card not found"
|
||||||
|
|
||||||
|
if (allCards.size > 1) {
|
||||||
|
onMultipleCards(allCards, nfcCharacter)
|
||||||
|
return "Multiple cards found"
|
||||||
|
}
|
||||||
|
|
||||||
|
cardData = allCards[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
return insertCharacter(nfcCharacter, cardData)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private fun insertCharacter(
|
||||||
|
nfcCharacter: NfcCharacter,
|
||||||
|
cardData: Card
|
||||||
|
): String {
|
||||||
|
val cardCharData = database
|
||||||
|
.characterDao()
|
||||||
|
.getCharacterByMonIndex(nfcCharacter.charIndex.toInt(), cardData.id)
|
||||||
|
|
||||||
|
updateCardProgress(nfcCharacter, cardData)
|
||||||
|
|
||||||
|
val characterData = UserCharacter(
|
||||||
|
charId = cardCharData.id,
|
||||||
|
ageInDays = nfcCharacter.ageInDays.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
|
||||||
|
)
|
||||||
|
|
||||||
|
database
|
||||||
|
.userCharacterDao()
|
||||||
|
.clearActiveCharacter()
|
||||||
|
|
||||||
|
val characterId: Long = database
|
||||||
|
.userCharacterDao()
|
||||||
|
.insertCharacterData(characterData)
|
||||||
|
|
||||||
|
if (nfcCharacter is BENfcCharacter) {
|
||||||
|
addBeCharacterToDatabase(
|
||||||
|
characterId = characterId,
|
||||||
|
nfcCharacter = nfcCharacter
|
||||||
|
)
|
||||||
|
} else if (nfcCharacter is VBNfcCharacter) {
|
||||||
|
addVbCharacterToDatabase(
|
||||||
|
characterId = characterId,
|
||||||
|
nfcCharacter = nfcCharacter
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
addTransformationHistoryToDatabase(
|
||||||
|
characterId = characterId,
|
||||||
|
nfcCharacter = nfcCharacter,
|
||||||
|
dimData = cardData
|
||||||
|
)
|
||||||
|
|
||||||
|
addVitalsHistoryToDatabase(
|
||||||
|
characterId = characterId,
|
||||||
|
nfcCharacter = nfcCharacter
|
||||||
|
)
|
||||||
|
|
||||||
|
return "Done reading character!"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private fun updateCardProgress(
|
||||||
|
nfcCharacter: NfcCharacter,
|
||||||
|
cardData: Card
|
||||||
|
) {
|
||||||
|
val currentCardProgress = CardProgress(
|
||||||
|
cardId = cardData.id,
|
||||||
|
currentStage = nfcCharacter.nextAdventureMissionStage.toInt(),
|
||||||
|
unlocked = nfcCharacter.nextAdventureMissionStage.toInt() > cardData.stageCount
|
||||||
|
)
|
||||||
|
|
||||||
|
database
|
||||||
|
.cardProgressDao()
|
||||||
|
.updateDimProgress(currentCardProgress)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private fun addVbCharacterToDatabase(
|
||||||
|
characterId: Long,
|
||||||
|
nfcCharacter: VBNfcCharacter
|
||||||
|
) {
|
||||||
|
val extraCharacterData = VBCharacterData(
|
||||||
|
id = characterId,
|
||||||
|
generation = nfcCharacter.generation.toInt(),
|
||||||
|
totalTrophies = nfcCharacter.totalTrophies.toInt()
|
||||||
|
)
|
||||||
|
|
||||||
|
database
|
||||||
|
.userCharacterDao()
|
||||||
|
.insertVBCharacterData(extraCharacterData)
|
||||||
|
|
||||||
|
addSpecialMissionsToDatabase(nfcCharacter, characterId)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private fun addSpecialMissionsToDatabase(
|
||||||
|
nfcCharacter: VBNfcCharacter,
|
||||||
|
characterId: Long
|
||||||
|
) {
|
||||||
|
val specialMissionsWatch = nfcCharacter.specialMissions
|
||||||
|
val specialMissionsDb = specialMissionsWatch.map { item ->
|
||||||
|
SpecialMissions(
|
||||||
|
characterId = characterId,
|
||||||
|
goal = item.goal.toInt(),
|
||||||
|
watchId = item.id.toInt(),
|
||||||
|
progress = item.progress.toInt(),
|
||||||
|
status = item.status,
|
||||||
|
timeElapsedInMinutes = item.timeElapsedInMinutes.toInt(),
|
||||||
|
timeLimitInMinutes = item.timeLimitInMinutes.toInt(),
|
||||||
|
missionType = item.type,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
database
|
||||||
|
.userCharacterDao()
|
||||||
|
.insertSpecialMissions(*specialMissionsDb.toTypedArray())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private fun addBeCharacterToDatabase(
|
||||||
|
characterId: Long,
|
||||||
|
nfcCharacter: 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(),
|
||||||
|
)
|
||||||
|
|
||||||
|
database
|
||||||
|
.userCharacterDao()
|
||||||
|
.insertBECharacterData(extraCharacterData)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private fun addVitalsHistoryToDatabase(
|
||||||
|
characterId: Long,
|
||||||
|
nfcCharacter: NfcCharacter
|
||||||
|
) {
|
||||||
|
val vitalsHistoryWatch = nfcCharacter.vitalHistory
|
||||||
|
val vitalsHistory = vitalsHistoryWatch.map { historyElement ->
|
||||||
|
Log.d("VitalsHistory", "${historyElement.year.toInt()} ${historyElement.month.toInt()} ${historyElement.day.toInt()}")
|
||||||
|
VitalsHistory(
|
||||||
|
charId = characterId,
|
||||||
|
year = historyElement.year.toInt(),
|
||||||
|
month = historyElement.month.toInt(),
|
||||||
|
day = historyElement.day.toInt(),
|
||||||
|
vitalPoints = historyElement.vitalsGained.toInt()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
database
|
||||||
|
.userCharacterDao()
|
||||||
|
.insertVitals(*vitalsHistory.toTypedArray())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun addTransformationHistoryToDatabase(
|
||||||
|
characterId: Long,
|
||||||
|
nfcCharacter: NfcCharacter,
|
||||||
|
dimData: Card
|
||||||
|
) {
|
||||||
|
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
|
||||||
|
|
||||||
|
database
|
||||||
|
.userCharacterDao()
|
||||||
|
.insertTransformation(
|
||||||
|
characterId,
|
||||||
|
item.toCharIndex.toInt(),
|
||||||
|
dimData.id,
|
||||||
|
date
|
||||||
|
)
|
||||||
|
|
||||||
|
database
|
||||||
|
.dexDao()
|
||||||
|
.insertCharacter(
|
||||||
|
item.toCharIndex.toInt(),
|
||||||
|
dimData.id,
|
||||||
|
date
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+298
@@ -0,0 +1,298 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.screens.scanScreen.converters
|
||||||
|
|
||||||
|
import android.icu.util.Calendar
|
||||||
|
import android.icu.util.TimeZone
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.activity.ComponentActivity
|
||||||
|
import com.github.cfogrady.vbnfc.be.BENfcCharacter
|
||||||
|
import com.github.cfogrady.vbnfc.be.FirmwareVersion
|
||||||
|
import com.github.cfogrady.vbnfc.data.NfcCharacter
|
||||||
|
import com.github.cfogrady.vbnfc.vb.SpecialMission
|
||||||
|
import com.github.cfogrady.vbnfc.vb.VBNfcCharacter
|
||||||
|
import com.github.nacabaro.vbhelper.database.AppDatabase
|
||||||
|
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.utils.DeviceType
|
||||||
|
import java.util.Date
|
||||||
|
|
||||||
|
class ToNfcConverter(
|
||||||
|
private val componentActivity: ComponentActivity
|
||||||
|
) {
|
||||||
|
private val application: VBHelper = componentActivity.applicationContext as VBHelper
|
||||||
|
private val database: AppDatabase = application.container.db
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
suspend fun characterToNfc(
|
||||||
|
characterId: Long
|
||||||
|
): NfcCharacter {
|
||||||
|
val app = componentActivity.applicationContext as VBHelper
|
||||||
|
val database = app.container.db
|
||||||
|
|
||||||
|
val userCharacter = database
|
||||||
|
.userCharacterDao()
|
||||||
|
.getCharacter(characterId)
|
||||||
|
|
||||||
|
val characterInfo = database
|
||||||
|
.characterDao()
|
||||||
|
.getCharacterInfo(userCharacter.charId)
|
||||||
|
|
||||||
|
val currentCardStage = database
|
||||||
|
.cardProgressDao()
|
||||||
|
.getCardProgress(characterInfo.cardId)
|
||||||
|
|
||||||
|
return if (userCharacter.characterType == DeviceType.BEDevice)
|
||||||
|
nfcToBENfc(characterId, characterInfo, currentCardStage, userCharacter)
|
||||||
|
else
|
||||||
|
nfcToVBNfc(characterId, characterInfo, currentCardStage, userCharacter)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private suspend fun nfcToVBNfc(
|
||||||
|
characterId: Long,
|
||||||
|
characterInfo: CharacterDtos.CardCharacterInfo,
|
||||||
|
currentCardStage: Int,
|
||||||
|
userCharacter: UserCharacter
|
||||||
|
): VBNfcCharacter {
|
||||||
|
val vbData = database
|
||||||
|
.userCharacterDao()
|
||||||
|
.getVbData(characterId)
|
||||||
|
|
||||||
|
val paddedTransformationArray = generateTransformationHistory(characterId, 9)
|
||||||
|
|
||||||
|
val watchSpecialMissions = generateSpecialMissionsArray(characterId)
|
||||||
|
|
||||||
|
val nfcData = VBNfcCharacter(
|
||||||
|
dimId = characterInfo.cardId.toUShort(),
|
||||||
|
charIndex = characterInfo.charId.toUShort(),
|
||||||
|
stage = characterInfo.stage.toByte(),
|
||||||
|
attribute = characterInfo.attribute,
|
||||||
|
ageInDays = userCharacter.ageInDays.toByte(),
|
||||||
|
nextAdventureMissionStage = currentCardStage.toByte(),
|
||||||
|
mood = userCharacter.mood.toByte(),
|
||||||
|
vitalPoints = userCharacter.vitalPoints.toUShort(),
|
||||||
|
transformationCountdownInMinutes = userCharacter.transformationCountdown.toUShort(),
|
||||||
|
injuryStatus = userCharacter.injuryStatus,
|
||||||
|
trophies = userCharacter.trophies.toUShort(),
|
||||||
|
currentPhaseBattlesWon = userCharacter.currentPhaseBattlesWon.toUShort(),
|
||||||
|
currentPhaseBattlesLost = userCharacter.currentPhaseBattlesLost.toUShort(),
|
||||||
|
totalBattlesWon = userCharacter.totalBattlesWon.toUShort(),
|
||||||
|
totalBattlesLost = userCharacter.totalBattlesLost.toUShort(),
|
||||||
|
activityLevel = userCharacter.activityLevel.toByte(),
|
||||||
|
heartRateCurrent = userCharacter.heartRateCurrent.toUByte(),
|
||||||
|
transformationHistory = paddedTransformationArray,
|
||||||
|
vitalHistory = generateVitalsHistoryArray(characterId),
|
||||||
|
appReserved1 = ByteArray(12) {0},
|
||||||
|
appReserved2 = generateUShortAppReserved(userCharacter),
|
||||||
|
generation = vbData.generation.toUShort(),
|
||||||
|
totalTrophies = vbData.totalTrophies.toUShort(),
|
||||||
|
specialMissions = watchSpecialMissions.toTypedArray()
|
||||||
|
)
|
||||||
|
|
||||||
|
return nfcData
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private suspend fun generateUShortAppReserved(
|
||||||
|
userCharacter: UserCharacter
|
||||||
|
): Array<UShort> {
|
||||||
|
val cardData = database
|
||||||
|
.cardDao()
|
||||||
|
.getCardByCharacterId(userCharacter.id)
|
||||||
|
|
||||||
|
val appReserved = Array<UShort>(3) {
|
||||||
|
0u
|
||||||
|
}
|
||||||
|
|
||||||
|
appReserved[0] = cardData.id.toUShort()
|
||||||
|
|
||||||
|
return appReserved
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private suspend fun generateSpecialMissionsArray(
|
||||||
|
characterId: Long
|
||||||
|
): List<SpecialMission> {
|
||||||
|
val specialMissions = database
|
||||||
|
.userCharacterDao()
|
||||||
|
.getSpecialMissions(characterId)
|
||||||
|
|
||||||
|
val watchSpecialMissions = specialMissions.map {
|
||||||
|
SpecialMission(
|
||||||
|
goal = it.goal.toUShort(),
|
||||||
|
id = it.watchId.toUShort(),
|
||||||
|
progress = it.progress.toUShort(),
|
||||||
|
status = it.status,
|
||||||
|
timeElapsedInMinutes = it.timeElapsedInMinutes.toUShort(),
|
||||||
|
timeLimitInMinutes = it.timeLimitInMinutes.toUShort(),
|
||||||
|
type = it.missionType
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return watchSpecialMissions
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private suspend fun generateVitalsHistoryArray(
|
||||||
|
characterId: Long
|
||||||
|
): Array<NfcCharacter.DailyVitals> {
|
||||||
|
val vitalsHistory = database
|
||||||
|
.userCharacterDao()
|
||||||
|
.getVitalsHistory(characterId)
|
||||||
|
|
||||||
|
val nfcVitalsHistory = Array(7) {
|
||||||
|
NfcCharacter.DailyVitals(0u, 0u, 0u, 0u)
|
||||||
|
}
|
||||||
|
|
||||||
|
vitalsHistory.mapIndexed { index, historyElement ->
|
||||||
|
var actualYear = 0
|
||||||
|
if (historyElement.year != 2000) {
|
||||||
|
actualYear = historyElement.year
|
||||||
|
}
|
||||||
|
nfcVitalsHistory[index] = NfcCharacter.DailyVitals(
|
||||||
|
day = historyElement.day.toUByte(),
|
||||||
|
month = historyElement.month.toUByte(),
|
||||||
|
year = actualYear.toUShort(),
|
||||||
|
vitalsGained = vitalsHistory[index].vitalPoints.toUShort()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
nfcVitalsHistory.map {
|
||||||
|
Log.d("NFC", it.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
return nfcVitalsHistory
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private suspend fun nfcToBENfc(
|
||||||
|
characterId: Long,
|
||||||
|
characterInfo: CharacterDtos.CardCharacterInfo,
|
||||||
|
currentCardStage: Int,
|
||||||
|
userCharacter: UserCharacter
|
||||||
|
): BENfcCharacter {
|
||||||
|
val beData = database
|
||||||
|
.userCharacterDao()
|
||||||
|
.getBeData(characterId)
|
||||||
|
|
||||||
|
val paddedTransformationArray = generateTransformationHistory(characterId)
|
||||||
|
|
||||||
|
val nfcData = BENfcCharacter(
|
||||||
|
dimId = characterInfo.cardId.toUShort(),
|
||||||
|
charIndex = characterInfo.charId.toUShort(),
|
||||||
|
stage = characterInfo.stage.toByte(),
|
||||||
|
attribute = characterInfo.attribute,
|
||||||
|
ageInDays = userCharacter.ageInDays.toByte(),
|
||||||
|
nextAdventureMissionStage = currentCardStage.toByte(),
|
||||||
|
mood = userCharacter.mood.toByte(),
|
||||||
|
vitalPoints = userCharacter.vitalPoints.toUShort(),
|
||||||
|
itemEffectMentalStateValue = beData.itemEffectMentalStateValue.toByte(),
|
||||||
|
itemEffectMentalStateMinutesRemaining = beData.itemEffectMentalStateMinutesRemaining.toByte(),
|
||||||
|
itemEffectActivityLevelValue = beData.itemEffectActivityLevelValue.toByte(),
|
||||||
|
itemEffectActivityLevelMinutesRemaining = beData.itemEffectActivityLevelMinutesRemaining.toByte(),
|
||||||
|
itemEffectVitalPointsChangeValue = beData.itemEffectVitalPointsChangeValue.toByte(),
|
||||||
|
itemEffectVitalPointsChangeMinutesRemaining = beData.itemEffectVitalPointsChangeMinutesRemaining.toByte(),
|
||||||
|
transformationCountdownInMinutes = userCharacter.transformationCountdown.toUShort(),
|
||||||
|
injuryStatus = userCharacter.injuryStatus,
|
||||||
|
trainingPp = userCharacter.trophies.toUShort(),
|
||||||
|
currentPhaseBattlesWon = userCharacter.currentPhaseBattlesWon.toUShort(),
|
||||||
|
currentPhaseBattlesLost = userCharacter.currentPhaseBattlesLost.toUShort(),
|
||||||
|
totalBattlesWon = userCharacter.totalBattlesWon.toUShort(),
|
||||||
|
totalBattlesLost = userCharacter.totalBattlesLost.toUShort(),
|
||||||
|
activityLevel = userCharacter.activityLevel.toByte(),
|
||||||
|
heartRateCurrent = userCharacter.heartRateCurrent.toUByte(),
|
||||||
|
transformationHistory = paddedTransformationArray,
|
||||||
|
vitalHistory = generateVitalsHistoryArray(characterId),
|
||||||
|
appReserved1 = ByteArray(12) {0},
|
||||||
|
appReserved2 = Array(3) {0u},
|
||||||
|
trainingHp = beData.trainingHp.toUShort(),
|
||||||
|
trainingAp = beData.trainingAp.toUShort(),
|
||||||
|
trainingBp = beData.trainingBp.toUShort(),
|
||||||
|
remainingTrainingTimeInMinutes = beData.remainingTrainingTimeInMinutes.toUShort(),
|
||||||
|
abilityRarity = beData.abilityRarity,
|
||||||
|
abilityType = beData.abilityType.toUShort(),
|
||||||
|
abilityBranch = beData.abilityBranch.toUShort(),
|
||||||
|
abilityReset = beData.abilityReset.toByte(),
|
||||||
|
rank = beData.rank.toByte(),
|
||||||
|
itemType = beData.itemType.toByte(),
|
||||||
|
itemMultiplier = beData.itemMultiplier.toByte(),
|
||||||
|
itemRemainingTime = beData.itemRemainingTime.toByte(),
|
||||||
|
otp0 = byteArrayOf(8),
|
||||||
|
otp1 = byteArrayOf(8),
|
||||||
|
characterCreationFirmwareVersion = FirmwareVersion(
|
||||||
|
minorVersion = beData.minorVersion.toByte(),
|
||||||
|
majorVersion = beData.majorVersion.toByte()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
return nfcData
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private suspend fun generateTransformationHistory(
|
||||||
|
characterId: Long,
|
||||||
|
length: Int = 8
|
||||||
|
): Array<NfcCharacter.Transformation> {
|
||||||
|
val transformationHistory = database
|
||||||
|
.userCharacterDao()
|
||||||
|
.getTransformationHistory(characterId)!!
|
||||||
|
.map {
|
||||||
|
val date = Date(it.transformationDate)
|
||||||
|
val calendar = android.icu.util.GregorianCalendar(TimeZone.getTimeZone("UTC"))
|
||||||
|
calendar.time = date
|
||||||
|
|
||||||
|
Log.d(
|
||||||
|
"TransformationHistory",
|
||||||
|
"Year: ${calendar.get(Calendar.YEAR)}, " +
|
||||||
|
"Month: ${calendar.get(Calendar.MONTH) + 1}, " +
|
||||||
|
"Day: ${calendar.get(Calendar.DAY_OF_MONTH)}"
|
||||||
|
)
|
||||||
|
|
||||||
|
NfcCharacter.Transformation(
|
||||||
|
toCharIndex = it.monIndex.toUByte(),
|
||||||
|
year = calendar
|
||||||
|
.get(Calendar.YEAR)
|
||||||
|
.toUShort(),
|
||||||
|
month = (calendar
|
||||||
|
.get(Calendar.MONTH) + 1)
|
||||||
|
.toUByte(),
|
||||||
|
day = calendar
|
||||||
|
.get(Calendar.DAY_OF_MONTH)
|
||||||
|
.toUByte()
|
||||||
|
)
|
||||||
|
}.toTypedArray()
|
||||||
|
|
||||||
|
val paddedTransformationArray = padTransformationArray(transformationHistory, length)
|
||||||
|
|
||||||
|
return paddedTransformationArray
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private fun padTransformationArray(
|
||||||
|
transformationArray: Array<NfcCharacter.Transformation>,
|
||||||
|
length: Int
|
||||||
|
): Array<NfcCharacter.Transformation> {
|
||||||
|
if (transformationArray.size >= 8) {
|
||||||
|
return transformationArray
|
||||||
|
}
|
||||||
|
|
||||||
|
val paddedArray = Array(length) {
|
||||||
|
NfcCharacter.Transformation(
|
||||||
|
toCharIndex = 255u,
|
||||||
|
year = 65535u,
|
||||||
|
month = 255u,
|
||||||
|
day = 255u
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
System.arraycopy(transformationArray, 0, paddedArray, 0, transformationArray.size)
|
||||||
|
return paddedArray
|
||||||
|
}
|
||||||
|
}
|
||||||
+41
@@ -0,0 +1,41 @@
|
|||||||
|
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.") { }
|
||||||
|
SettingsEntry(title = "shvstrz", description = "Designing the app icon in SVG.") { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+20
-10
@@ -1,4 +1,4 @@
|
|||||||
package com.github.nacabaro.vbhelper.screens
|
package com.github.nacabaro.vbhelper.screens.settingsScreen
|
||||||
|
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
@@ -18,11 +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,
|
||||||
onClickImportCard: () -> Unit
|
settingsScreenController: SettingsScreenControllerImpl,
|
||||||
) {
|
) {
|
||||||
Scaffold (
|
Scaffold (
|
||||||
topBar = {
|
topBar = {
|
||||||
@@ -42,18 +43,27 @@ fun SettingsScreen(
|
|||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.verticalScroll(rememberScrollState())
|
.verticalScroll(rememberScrollState())
|
||||||
) {
|
) {
|
||||||
SettingsSection("General")
|
SettingsSection("NFC Communication")
|
||||||
SettingsEntry(title = "Import VB key", description = "Import standard vital bracelet keys") { }
|
SettingsEntry(title = "Import APK", description = "Import Secrets From Vital Arean 2.1.0 APK") {
|
||||||
SettingsEntry(title = "Import VB Characters key", description = "Import standard vital bracelet keys") { }
|
settingsScreenController.onClickImportApk()
|
||||||
SettingsEntry(title = "Import VB BE key", description = "Import standard vital bracelet keys") { }
|
}
|
||||||
SettingsEntry(title = "Import transform functions", description = "Import standard vital bracelet keys") { }
|
|
||||||
SettingsEntry(title = "Import decryption key", description = "Import standard vital bracelet keys") { }
|
|
||||||
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()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.screens.settingsScreen
|
||||||
|
|
||||||
|
interface SettingsScreenController {
|
||||||
|
fun onClickOpenDirectory()
|
||||||
|
fun onClickImportDatabase()
|
||||||
|
fun onClickImportApk()
|
||||||
|
fun onClickImportCard()
|
||||||
|
}
|
||||||
+517
@@ -0,0 +1,517 @@
|
|||||||
|
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.DimCard
|
||||||
|
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.characters.Sprite
|
||||||
|
import com.github.nacabaro.vbhelper.domain.card.Card
|
||||||
|
import com.github.nacabaro.vbhelper.domain.card.CardProgress
|
||||||
|
import com.github.nacabaro.vbhelper.domain.card.CardCharacter
|
||||||
|
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 suspend fun importEvoData(
|
||||||
|
cardId: Long,
|
||||||
|
card: com.github.cfogrady.vb.dim.card.Card<*, *, *, *, *, *>
|
||||||
|
) {
|
||||||
|
for (index in 0 until card.transformationRequirements.transformationEntries.size) {
|
||||||
|
val evo = card.transformationRequirements.transformationEntries[index]
|
||||||
|
|
||||||
|
var transformationTimerHours: Int
|
||||||
|
var unlockAdventureLevel: Int
|
||||||
|
|
||||||
|
if (card is BemCard) {
|
||||||
|
transformationTimerHours = card
|
||||||
|
.transformationRequirements
|
||||||
|
.transformationEntries[index]
|
||||||
|
.minutesUntilTransformation / 60
|
||||||
|
unlockAdventureLevel = if (
|
||||||
|
card
|
||||||
|
.transformationRequirements
|
||||||
|
.transformationEntries[index]
|
||||||
|
.requiredCompletedAdventureLevel == 65535
|
||||||
|
) {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
card
|
||||||
|
.transformationRequirements
|
||||||
|
.transformationEntries[index]
|
||||||
|
.requiredCompletedAdventureLevel
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
transformationTimerHours = (card as DimCard)
|
||||||
|
.transformationRequirements
|
||||||
|
.transformationEntries[index]
|
||||||
|
.hoursUntilEvolution
|
||||||
|
unlockAdventureLevel = if (
|
||||||
|
card
|
||||||
|
.adventureLevels
|
||||||
|
.levels
|
||||||
|
.last()
|
||||||
|
.bossCharacterIndex == card.transformationRequirements.transformationEntries[index].toCharacterIndex
|
||||||
|
) {
|
||||||
|
14
|
||||||
|
/*
|
||||||
|
Magic number incoming!!
|
||||||
|
|
||||||
|
In the case of DiMCards, stage 15 is the one that unlocks the locked character.
|
||||||
|
We know it is a locked character if the last adventure level's boss character index
|
||||||
|
is the current index. If it is, we add stage 15 complete as a requirement for transformation.
|
||||||
|
*/
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
/*
|
||||||
|
Another magic number...
|
||||||
|
|
||||||
|
The rest of the characters are not locked.
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
database
|
||||||
|
.characterDao()
|
||||||
|
.insertPossibleTransformation(
|
||||||
|
cardId = cardId,
|
||||||
|
fromChraraIndex = evo.fromCharacterIndex,
|
||||||
|
toChraraIndex = evo.toCharacterIndex,
|
||||||
|
requiredVitals = evo.requiredVitalValues,
|
||||||
|
requiredTrophies = evo.requiredTrophies,
|
||||||
|
requiredBattles = evo.requiredBattles,
|
||||||
|
requiredWinRate = evo.requiredWinRatio,
|
||||||
|
requiredAdventureLevelCompleted = unlockAdventureLevel,
|
||||||
|
changeTimerHours = transformationTimerHours
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun importCharacterData(
|
||||||
|
cardId: Long,
|
||||||
|
card: com.github.cfogrady.vb.dim.card.Card<*, *, *, *, *, *>
|
||||||
|
) {
|
||||||
|
var spriteCounter = when (card is BemCard) {
|
||||||
|
true -> 54
|
||||||
|
false -> 10
|
||||||
|
}
|
||||||
|
|
||||||
|
val domainCharacters = mutableListOf<CardCharacter>()
|
||||||
|
|
||||||
|
val characters = card
|
||||||
|
.characterStats
|
||||||
|
.characterEntries
|
||||||
|
|
||||||
|
for (index in 0 until characters.size) {
|
||||||
|
var domainSprite: Sprite?
|
||||||
|
if (index < 2 && card is DimCard) {
|
||||||
|
domainSprite = Sprite(
|
||||||
|
width = card.spriteData.sprites[spriteCounter + 1].spriteDimensions.width,
|
||||||
|
height = card.spriteData.sprites[spriteCounter + 1].spriteDimensions.height,
|
||||||
|
spriteIdle1 = card.spriteData.sprites[spriteCounter + 1].pixelData,
|
||||||
|
spriteIdle2 = card.spriteData.sprites[spriteCounter + 2].pixelData,
|
||||||
|
spriteWalk1 = card.spriteData.sprites[spriteCounter + 1].pixelData,
|
||||||
|
spriteWalk2 = card.spriteData.sprites[spriteCounter + 3].pixelData,
|
||||||
|
spriteRun1 = card.spriteData.sprites[spriteCounter + 1].pixelData,
|
||||||
|
spriteRun2 = card.spriteData.sprites[spriteCounter + 3].pixelData,
|
||||||
|
spriteTrain1 = card.spriteData.sprites[spriteCounter + 1].pixelData,
|
||||||
|
spriteTrain2 = card.spriteData.sprites[spriteCounter + 3].pixelData,
|
||||||
|
spriteHappy = card.spriteData.sprites[spriteCounter + 4].pixelData,
|
||||||
|
spriteSleep = card.spriteData.sprites[spriteCounter + 5].pixelData,
|
||||||
|
spriteAttack = card.spriteData.sprites[spriteCounter + 2].pixelData,
|
||||||
|
spriteDodge = card.spriteData.sprites[spriteCounter + 3].pixelData
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
domainSprite = Sprite(
|
||||||
|
width = card.spriteData.sprites[spriteCounter + 1].spriteDimensions.width,
|
||||||
|
height = card.spriteData.sprites[spriteCounter + 1].spriteDimensions.height,
|
||||||
|
spriteIdle1 = card.spriteData.sprites[spriteCounter + 1].pixelData,
|
||||||
|
spriteIdle2 = card.spriteData.sprites[spriteCounter + 2].pixelData,
|
||||||
|
spriteWalk1 = card.spriteData.sprites[spriteCounter + 3].pixelData,
|
||||||
|
spriteWalk2 = card.spriteData.sprites[spriteCounter + 4].pixelData,
|
||||||
|
spriteRun1 = card.spriteData.sprites[spriteCounter + 5].pixelData,
|
||||||
|
spriteRun2 = card.spriteData.sprites[spriteCounter + 6].pixelData,
|
||||||
|
spriteTrain1 = card.spriteData.sprites[spriteCounter + 7].pixelData,
|
||||||
|
spriteTrain2 = card.spriteData.sprites[spriteCounter + 8].pixelData,
|
||||||
|
spriteHappy = card.spriteData.sprites[spriteCounter + 9].pixelData,
|
||||||
|
spriteSleep = card.spriteData.sprites[spriteCounter + 10].pixelData,
|
||||||
|
spriteAttack = card.spriteData.sprites[spriteCounter + 11].pixelData,
|
||||||
|
spriteDodge = card.spriteData.sprites[spriteCounter + 12].pixelData
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val spriteId = database
|
||||||
|
.spriteDao()
|
||||||
|
.insertSprite(domainSprite)
|
||||||
|
|
||||||
|
|
||||||
|
domainCharacters.add(
|
||||||
|
CardCharacter(
|
||||||
|
cardId = cardId,
|
||||||
|
spriteId = spriteId,
|
||||||
|
charaIndex = index,
|
||||||
|
nameSprite = 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,
|
||||||
|
nameWidth = card.spriteData.sprites[spriteCounter].spriteDimensions.width,
|
||||||
|
nameHeight = card.spriteData.sprites[spriteCounter].spriteDimensions.height
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
spriteCounter += if (card is BemCard) {
|
||||||
|
14
|
||||||
|
} else {
|
||||||
|
when (index) {
|
||||||
|
0 -> 6
|
||||||
|
1 -> 7
|
||||||
|
else -> 14
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
database
|
||||||
|
.characterDao()
|
||||||
|
.insertCharacter(*domainCharacters.toTypedArray())
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun importAdventureMissions(
|
||||||
|
cardId: Long,
|
||||||
|
card: com.github.cfogrady.vb.dim.card.Card<*, *, *, *, *, *>
|
||||||
|
) {
|
||||||
|
Log.d("importAdventureMissions", "Importing adventure missions")
|
||||||
|
if (card is BemCard) {
|
||||||
|
card.adventureLevels.levels.forEach {
|
||||||
|
database
|
||||||
|
.cardAdventureDao()
|
||||||
|
.insertNewAdventure(
|
||||||
|
cardId = cardId,
|
||||||
|
characterId = it.bossCharacterIndex,
|
||||||
|
steps = it.steps,
|
||||||
|
bossAp = it.bossAp,
|
||||||
|
bossHp = it.bossHp,
|
||||||
|
bossDp = it.bossDp,
|
||||||
|
bossBp = it.bossBp
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else if (card is DimCard) {
|
||||||
|
card.adventureLevels.levels.map {
|
||||||
|
database
|
||||||
|
.cardAdventureDao()
|
||||||
|
.insertNewAdventure(
|
||||||
|
cardId = cardId,
|
||||||
|
characterId = it.bossCharacterIndex,
|
||||||
|
steps = it.steps,
|
||||||
|
bossAp = it.bossAp,
|
||||||
|
bossHp = it.bossHp,
|
||||||
|
bossDp = it.bossDp,
|
||||||
|
bossBp = null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun importCardFusions(
|
||||||
|
cardId: Long,
|
||||||
|
card: com.github.cfogrady.vb.dim.card.Card<*, *, *, *, *, *>
|
||||||
|
) {
|
||||||
|
Log.d("importCardFusions", "Importing card fusions")
|
||||||
|
if (card is DimCard) {
|
||||||
|
card
|
||||||
|
.attributeFusions
|
||||||
|
.entries
|
||||||
|
.forEach {
|
||||||
|
database
|
||||||
|
.cardFusionsDao()
|
||||||
|
.insertNewFusion(
|
||||||
|
cardId = cardId,
|
||||||
|
fromCharaId = it.characterIndex,
|
||||||
|
toCharaIdAttr1 = it.attribute1Fusion,
|
||||||
|
toCharaIdAttr2 = it.attribute2Fusion,
|
||||||
|
toCharaIdAttr3 = it.attribute3Fusion,
|
||||||
|
toCharaIdAttr4 = it.attribute4Fusion
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateCardProgress(
|
||||||
|
cardId: Long,
|
||||||
|
) {
|
||||||
|
val cardProgress = CardProgress(
|
||||||
|
cardId = cardId,
|
||||||
|
currentStage = 0,
|
||||||
|
unlocked = false
|
||||||
|
)
|
||||||
|
|
||||||
|
database
|
||||||
|
.cardProgressDao()
|
||||||
|
.updateDimProgress(cardProgress)
|
||||||
|
}
|
||||||
|
|
||||||
|
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(
|
||||||
|
cardId = card.header.dimId,
|
||||||
|
logo = card.spriteData.sprites[0].pixelData,
|
||||||
|
name = card.spriteData.text,
|
||||||
|
stageCount = card.adventureLevels.levels.size,
|
||||||
|
logoHeight = card.spriteData.sprites[0].height,
|
||||||
|
logoWidth = card.spriteData.sprites[0].width,
|
||||||
|
isBEm = card is BemCard
|
||||||
|
)
|
||||||
|
|
||||||
|
val cardId = database
|
||||||
|
.cardDao()
|
||||||
|
.insertNewCard(cardModel)
|
||||||
|
|
||||||
|
updateCardProgress(cardId = cardId)
|
||||||
|
|
||||||
|
importCharacterData(cardId, card)
|
||||||
|
|
||||||
|
importEvoData(cardId, card)
|
||||||
|
|
||||||
|
importAdventureMissions(cardId, card)
|
||||||
|
|
||||||
|
importCardFusions(cardId, card)
|
||||||
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user