mirror of
https://github.com/nacabaro/vbhelper.git
synced 2026-10-01 15:41:36 +00:00
Compare commits
112
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1d9879fb50 | ||
|
|
3fa072ce1e | ||
|
|
69245be8dd | ||
|
|
73c393df64 | ||
|
|
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 | ||
|
|
b91da136df | ||
|
|
e4366ce15c | ||
|
|
7582f944b5 | ||
|
|
a028611d8a | ||
|
|
5edd753da1 | ||
|
|
5691f998f2 | ||
|
|
4e2b9eb541 | ||
|
|
19fbed0ef2 | ||
|
|
82292823d3 | ||
|
|
01e82a6a44 | ||
|
|
1e73f52da5 | ||
|
|
d9e492eb0e | ||
|
|
5bd5fc3f74 | ||
|
|
3019560a26 | ||
|
|
a01d0eba47 | ||
|
|
bc02689850 |
+12
@@ -0,0 +1,12 @@
|
|||||||
|
|
||||||
|
.gradle/
|
||||||
|
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
local.properties
|
||||||
|
|
||||||
|
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
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
# VBHelper
|
||||||
|
|
||||||
|
Application to interact with the Vital series, VB, VH, VBC and VBBE.
|
||||||
|
|
||||||
|
## Current state of the project
|
||||||
|
|
||||||
|
Right now the project is still under development, and until further notice, any database updates will result in having to erase application data.
|
||||||
|
|
||||||
|
This document will be updated once the application does not need any more database resets.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
As of now, the project allows you to read characters, view characters stats, and send them back to your watch.
|
||||||
|
|
||||||
|
You can also apply items to the characters read, such as special missions, or change timers, and store characters in the storage section.
|
||||||
|
|
||||||
|
You also earn new items every time an item such as a special mission (VB only) or a character completes an in-app adventure mission.
|
||||||
|
|
||||||
|
App also comes with a dex that will update every time a new character is added, and allows you to see evolution requirements and current adventure stage in the watch.
|
||||||
|
|
||||||
|
## How to set up
|
||||||
|
|
||||||
|
1. Download the latest version for VB Arena APK from a trustworthy source. If your download is a standalone APK, continue to step 2. Otherwise, if your download is an XAPK, do the following:
|
||||||
|
|
||||||
|
1. Using your phone file manager, rename the XAPK file to ZIP, and extract its contents. You can also do this with any other device, such as Windows, macOS and Linux.
|
||||||
|
|
||||||
|
2. Once the files are extracted, look for an APK called `com.bandai.vitalbraceletarena.apk`. Copy it somewhere else, you will need it.
|
||||||
|
|
||||||
|
2. Install an APK release for VB Arena. You will find the releases [here](http://github.com/nacabaro/vbhelper/releases). Download the latest release and install its APK.
|
||||||
|
|
||||||
|
Note, in the current stage of the project, you will have to delete the old application from your device. If the app keeps crashing after installing, clear application data and storage.
|
||||||
|
|
||||||
|
3. Import secrets in the app. These secrets will allow the app to talk to the watch. On the main screen, click on the gear icon, then `Import secrets`.
|
||||||
|
|
||||||
|
You will be prompted to choose a file. Choose the APK file that was previously obtained.
|
||||||
|
|
||||||
|
4. Import cards. Due to copyright laws, we cannot offer the characters and sprites themselves in the application. In order to import the cards do the following.
|
||||||
|
|
||||||
|
1. Using your own DiM/BEm cards, dump the cards to your device. You can get an in-depth tutorial in [here](http://mrblinky.net/digimon/vb/dimcardtool/dimcardtool.html). You can download the dump tool from [here](http://mrblinky.net/digimon/vb/dimcardtool/)
|
||||||
|
|
||||||
|
2. Once installed the tool and drivers, open the tool, connect your DiM/BEm reader hardware to yout computer and click on Read card.
|
||||||
|
|
||||||
|
3. Transfer the resulting file to your mobile device. You can put them anywhere, as long as they are accessible. My recommendation is to put them under a folder called `Cards` in your `Internal storage` or `SD Card`
|
||||||
|
|
||||||
|
4. In the app, click on import card. Next choose the BIN file corresponding to the card you want to import.
|
||||||
|
|
||||||
|
**Note: if you do not import the card, whenever you attempt to read a character from th watch, the character you read will get deleted.**
|
||||||
|
|
||||||
|
5. App will now be ready to be used.
|
||||||
|
|
||||||
|
## Planned features
|
||||||
|
|
||||||
|
- Online battles, undegoing development by `lightheel`.
|
||||||
|
|
||||||
|
- VitalWear compatibility, undergoing development by `cfogrady`.
|
||||||
|
|
||||||
|
- Support for multiple languages, not yet started.
|
||||||
|
|
||||||
|
- Database backup/restore.
|
||||||
|
|
||||||
|
## Credits
|
||||||
|
|
||||||
|
- `cyanic` for helping us understand more about the VB connection protocol.
|
||||||
|
|
||||||
|
- `cfogrady` for making both [`VB-DIM-Reader`](https://github.com/cfogrady/VB-DIM-Reader) and [`lib-vb-nfc`](https://github.com/cfogrady/lib-vb-nfc)
|
||||||
|
|
||||||
|
- `lightheel` for working on the online component in the application, both server and battle client.
|
||||||
|
|
||||||
|
- `shvstrz` for the app icon.
|
||||||
+30
-2
@@ -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,11 +39,35 @@ 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 {
|
||||||
implementation(libs.androidx.room.runtime)
|
implementation(libs.androidx.room.runtime)
|
||||||
implementation(project(":vb-nfc-reader"))
|
implementation(libs.vb.nfc.reader)
|
||||||
|
implementation(libs.dim.reader)
|
||||||
ksp(libs.androidx.room.compiler)
|
ksp(libs.androidx.room.compiler)
|
||||||
annotationProcessor(libs.androidx.room.compiler)
|
annotationProcessor(libs.androidx.room.compiler)
|
||||||
implementation(libs.androidx.core.ktx)
|
implementation(libs.androidx.core.ktx)
|
||||||
@@ -50,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)
|
||||||
@@ -63,5 +89,7 @@ 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")
|
||||||
|
implementation("androidx.datastore:datastore-preferences:1.1.7")
|
||||||
}
|
}
|
||||||
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,234 +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.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
|
||||||
import androidx.compose.runtime.mutableStateOf
|
|
||||||
import androidx.compose.runtime.remember
|
|
||||||
import androidx.compose.runtime.setValue
|
|
||||||
import androidx.compose.ui.platform.LocalContext
|
|
||||||
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.R
|
|
||||||
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.temporary_domain.TemporaryBECharacterData
|
import com.github.nacabaro.vbhelper.navigation.AppNavigationHandlers
|
||||||
import com.github.nacabaro.vbhelper.temporary_domain.TemporaryCharacterData
|
import com.github.nacabaro.vbhelper.screens.homeScreens.HomeScreenControllerImpl
|
||||||
import com.github.nacabaro.vbhelper.temporary_domain.TemporaryTransformationHistory
|
import com.github.nacabaro.vbhelper.screens.itemsScreen.ItemsScreenControllerImpl
|
||||||
|
import com.github.nacabaro.vbhelper.screens.scanScreen.ScanScreenControllerImpl
|
||||||
|
import com.github.nacabaro.vbhelper.screens.settingsScreen.SettingsScreenControllerImpl
|
||||||
|
import com.github.nacabaro.vbhelper.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
|
|
||||||
|
|
||||||
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) {
|
||||||
// EXTRACTED DIRECTLY FROM EXAMPLE APP
|
throw IllegalStateException("Key is already in use")
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
deviceToCryptographicTransformers = getMapOfCryptographicTransformers()
|
|
||||||
|
|
||||||
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
Log.i("MainActivity", "Resume")
|
||||||
|
for(activityListener in onActivityLifecycleListeners) {
|
||||||
|
activityListener.value.onResume()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun MainApplication() {
|
private fun MainApplication(
|
||||||
var isDoneReadingCharacter by remember { mutableStateOf(false) }
|
scanScreenController: ScanScreenControllerImpl,
|
||||||
val application = LocalContext.current.applicationContext as VBHelper
|
settingsScreenController: SettingsScreenControllerImpl,
|
||||||
val storageRepository = application.container.db
|
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,
|
||||||
addCharacterScannedIntoDatabase()
|
storageScreenController,
|
||||||
isDoneReadingCharacter = true
|
homeScreenController,
|
||||||
"Done reading character"
|
spriteViewerController,
|
||||||
}
|
cardScreenController
|
||||||
},
|
|
||||||
onClickScan = {
|
|
||||||
isDoneReadingCharacter = false
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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() {
|
|
||||||
val beCharacter = nfcCharacter as MutableStateFlow<BENfcCharacter?>
|
|
||||||
val temporaryCharacterData = TemporaryCharacterData(
|
|
||||||
dimId = nfcCharacter.value!!.dimId.toInt(),
|
|
||||||
charIndex = nfcCharacter.value!!.charIndex.toInt(),
|
|
||||||
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!!.transformationCountdown.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()
|
|
||||||
)
|
)
|
||||||
|
|
||||||
val application = applicationContext as VBHelper
|
|
||||||
val storageRepository = application.container.db
|
|
||||||
val characterId: Long = storageRepository
|
|
||||||
.temporaryMonsterDao()
|
|
||||||
.insertCharacterData(temporaryCharacterData)
|
|
||||||
|
|
||||||
val temporaryBECharacterData = TemporaryBECharacterData(
|
|
||||||
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
|
|
||||||
.temporaryMonsterDao()
|
|
||||||
.insertBECharacterData(temporaryBECharacterData)
|
|
||||||
|
|
||||||
val transformationHistoryWatch = beCharacter.value!!.transformationHistory
|
|
||||||
val domainTransformationHistory = transformationHistoryWatch.map { item ->
|
|
||||||
TemporaryTransformationHistory(
|
|
||||||
monId = characterId,
|
|
||||||
toCharIndex = item.toCharIndex.toInt(),
|
|
||||||
yearsSince1988 = item.yearsSince1988.toInt(),
|
|
||||||
month = item.month.toInt(),
|
|
||||||
day = item.day.toInt()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
storageRepository
|
|
||||||
.temporaryMonsterDao()
|
|
||||||
.insertTransformationHistory(*domainTransformationHistory.toTypedArray())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,209 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.components
|
||||||
|
|
||||||
|
import android.widget.Toast
|
||||||
|
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.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.material3.Card
|
||||||
|
import androidx.compose.material3.CardDefaults
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
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.ColorFilter
|
||||||
|
import androidx.compose.ui.graphics.FilterQuality
|
||||||
|
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 com.github.nacabaro.vbhelper.utils.BitmapData
|
||||||
|
import com.github.nacabaro.vbhelper.utils.getBitmap
|
||||||
|
import androidx.compose.ui.graphics.Shape
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import 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
|
||||||
|
fun CharacterEntry(
|
||||||
|
icon: BitmapData,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
obscure: Boolean = false,
|
||||||
|
disabled: Boolean = false,
|
||||||
|
shape: Shape = MaterialTheme.shapes.medium,
|
||||||
|
multiplier: Int = 4,
|
||||||
|
onClick: () -> Unit = { }
|
||||||
|
) {
|
||||||
|
val bitmap = remember (icon.bitmap) {
|
||||||
|
if(obscure) icon.getObscuredBitmap() else icon.getBitmap()
|
||||||
|
}
|
||||||
|
val imageBitmap = remember(bitmap) { bitmap.asImageBitmap() }
|
||||||
|
val density: Float = LocalContext.current.resources.displayMetrics.density
|
||||||
|
val dpSize = (icon.width * multiplier / density).dp
|
||||||
|
|
||||||
|
Card(
|
||||||
|
shape = shape,
|
||||||
|
onClick = when (disabled) {
|
||||||
|
true -> { {} }
|
||||||
|
false -> onClick
|
||||||
|
},
|
||||||
|
modifier = modifier
|
||||||
|
.aspectRatio(1f)
|
||||||
|
.padding(8.dp)
|
||||||
|
) {
|
||||||
|
Box(
|
||||||
|
contentAlignment = Alignment.BottomCenter,
|
||||||
|
modifier = Modifier
|
||||||
|
.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,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,10 +1,12 @@
|
|||||||
package com.github.nacabaro.vbhelper.components
|
package com.github.nacabaro.vbhelper.components
|
||||||
|
|
||||||
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
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.Alignment
|
||||||
@@ -20,10 +22,14 @@ fun TopBanner(
|
|||||||
text: String,
|
text: String,
|
||||||
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
|
||||||
|
.background(MaterialTheme.colorScheme.background)
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(16.dp)
|
.padding(16.dp)
|
||||||
) {
|
) {
|
||||||
@@ -32,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,28 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.daos
|
||||||
|
|
||||||
|
import androidx.room.Dao
|
||||||
|
import androidx.room.Insert
|
||||||
|
import androidx.room.Query
|
||||||
|
import com.github.nacabaro.vbhelper.domain.card.CardProgress
|
||||||
|
import com.github.nacabaro.vbhelper.dtos.CharacterDtos
|
||||||
|
|
||||||
|
@Dao
|
||||||
|
interface CardProgressDao {
|
||||||
|
@Query("""
|
||||||
|
UPDATE CardProgress
|
||||||
|
SET
|
||||||
|
currentStage = :currentStage,
|
||||||
|
unlocked = :unlocked
|
||||||
|
WHERE cardId = :cardId AND
|
||||||
|
currentStage < :currentStage
|
||||||
|
""")
|
||||||
|
fun updateCardProgress(currentStage: Int, cardId: Long, unlocked: Boolean)
|
||||||
|
|
||||||
|
@Query(
|
||||||
|
"SELECT currentStage FROM CardProgress WHERE cardId = :cardId"
|
||||||
|
)
|
||||||
|
fun getCardProgress(cardId: Long): Int
|
||||||
|
|
||||||
|
@Insert
|
||||||
|
fun insertCardProgress(cardProgress: CardProgress)
|
||||||
|
}
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.daos
|
||||||
|
|
||||||
|
import androidx.room.Dao
|
||||||
|
import androidx.room.Insert
|
||||||
|
import androidx.room.Query
|
||||||
|
import com.github.nacabaro.vbhelper.domain.card.CardCharacter
|
||||||
|
import com.github.nacabaro.vbhelper.domain.characters.Sprite
|
||||||
|
import com.github.nacabaro.vbhelper.dtos.CharacterDtos
|
||||||
|
|
||||||
|
@Dao
|
||||||
|
interface CharacterDao {
|
||||||
|
@Insert
|
||||||
|
suspend fun insertCharacter(vararg characterData: CardCharacter)
|
||||||
|
|
||||||
|
@Query("SELECT * FROM CardCharacter WHERE charaIndex = :monIndex AND cardId = :dimId LIMIT 1")
|
||||||
|
fun getCharacterByMonIndex(monIndex: Int, dimId: Long): CardCharacter
|
||||||
|
|
||||||
|
@Insert
|
||||||
|
suspend fun insertSprite(vararg sprite: Sprite)
|
||||||
|
|
||||||
|
@Query(
|
||||||
|
"""
|
||||||
|
SELECT
|
||||||
|
d.cardId as cardId,
|
||||||
|
c.charaIndex as charId,
|
||||||
|
c.stage as stage,
|
||||||
|
c.attribute as attribute,
|
||||||
|
cp.currentStage as currentStage
|
||||||
|
FROM CardCharacter c
|
||||||
|
JOIN UserCharacter uc ON c.id = uc.charId
|
||||||
|
JOIN Card d ON c.cardId = d.id
|
||||||
|
JOIN CardProgress cp ON d.id = cp.cardId
|
||||||
|
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,16 +0,0 @@
|
|||||||
package com.github.nacabaro.vbhelper.daos
|
|
||||||
|
|
||||||
import androidx.room.Dao
|
|
||||||
import androidx.room.Insert
|
|
||||||
import androidx.room.Query
|
|
||||||
import com.github.nacabaro.vbhelper.domain.Dim
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
|
||||||
|
|
||||||
@Dao
|
|
||||||
interface DiMDao {
|
|
||||||
@Insert
|
|
||||||
suspend fun insertNewDim(dim: Dim)
|
|
||||||
|
|
||||||
@Query("SELECT * FROM Dim")
|
|
||||||
fun getAllDims(): Flow<List<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,54 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.daos
|
||||||
|
|
||||||
|
import androidx.room.Dao
|
||||||
|
import androidx.room.Query
|
||||||
|
import com.github.nacabaro.vbhelper.dtos.ItemDtos
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
|
@Dao
|
||||||
|
interface ItemDao {
|
||||||
|
@Query(
|
||||||
|
"""
|
||||||
|
SELECT *
|
||||||
|
FROM Items
|
||||||
|
ORDER BY Items.itemIcon ASC
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
fun getAllItems(): Flow<List<ItemDtos.ItemsWithQuantities>>
|
||||||
|
|
||||||
|
@Query(
|
||||||
|
"""
|
||||||
|
SELECT *
|
||||||
|
FROM Items
|
||||||
|
WHERE quantity > 0
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
fun getAllUserItems(): Flow<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>
|
||||||
|
}
|
||||||
@@ -0,0 +1,231 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.daos
|
||||||
|
|
||||||
|
import androidx.room.Dao
|
||||||
|
import androidx.room.Insert
|
||||||
|
import androidx.room.OnConflictStrategy
|
||||||
|
import androidx.room.Query
|
||||||
|
import androidx.room.Upsert
|
||||||
|
import com.github.nacabaro.vbhelper.domain.card.CardCharacter
|
||||||
|
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.SpecialMissions
|
||||||
|
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
|
||||||
|
|
||||||
|
@Dao
|
||||||
|
interface UserCharacterDao {
|
||||||
|
@Insert
|
||||||
|
fun insertCharacterData(characterData: UserCharacter): Long
|
||||||
|
|
||||||
|
@Insert
|
||||||
|
fun insertBECharacterData(characterData: BECharacterData)
|
||||||
|
|
||||||
|
@Insert
|
||||||
|
fun insertVBCharacterData(characterData: VBCharacterData)
|
||||||
|
|
||||||
|
@Upsert
|
||||||
|
fun updateCharacter(character: UserCharacter)
|
||||||
|
|
||||||
|
@Upsert
|
||||||
|
fun updateBECharacterData(characterData: BECharacterData)
|
||||||
|
|
||||||
|
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||||
|
fun insertTransformationHistory(vararg transformationHistory: TransformationHistory)
|
||||||
|
|
||||||
|
@Upsert
|
||||||
|
fun insertSpecialMissions(vararg specialMissions: SpecialMissions)
|
||||||
|
|
||||||
|
@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
|
||||||
|
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
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
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")
|
||||||
|
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>
|
||||||
|
}
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
package com.github.nacabaro.vbhelper.daos
|
|
||||||
|
|
||||||
import androidx.room.Dao
|
|
||||||
import androidx.room.Insert
|
|
||||||
import androidx.room.Query
|
|
||||||
import com.github.nacabaro.vbhelper.domain.UserMonsters
|
|
||||||
|
|
||||||
@Dao
|
|
||||||
interface UserMonstersDao {
|
|
||||||
@Insert
|
|
||||||
fun insertUserMonsters(userMonsters: UserMonsters)
|
|
||||||
|
|
||||||
@Query("SELECT * FROM UserMonsters WHERE userId = :userId")
|
|
||||||
fun getUserMonsters(userId: Int): List<UserMonsters>
|
|
||||||
}
|
|
||||||
-11
@@ -1,11 +0,0 @@
|
|||||||
package com.github.nacabaro.vbhelper.daos
|
|
||||||
|
|
||||||
import androidx.room.Dao
|
|
||||||
import androidx.room.Insert
|
|
||||||
import com.github.nacabaro.vbhelper.temporary_domain.TemporaryTransformationHistory
|
|
||||||
|
|
||||||
@Dao
|
|
||||||
interface UserMonstersTransformationHistoryDao {
|
|
||||||
@Insert
|
|
||||||
fun insertTransformations(vararg transformations: TemporaryTransformationHistory)
|
|
||||||
}
|
|
||||||
@@ -2,20 +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.temporary_daos.TemporaryMonsterDao
|
import com.github.nacabaro.vbhelper.daos.AdventureDao
|
||||||
import com.github.nacabaro.vbhelper.temporary_domain.TemporaryBECharacterData
|
import com.github.nacabaro.vbhelper.daos.CardAdventureDao
|
||||||
import com.github.nacabaro.vbhelper.temporary_domain.TemporaryCharacterData
|
import com.github.nacabaro.vbhelper.daos.CharacterDao
|
||||||
import com.github.nacabaro.vbhelper.temporary_domain.TemporaryTransformationHistory
|
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.domain.card.Background
|
||||||
|
import com.github.nacabaro.vbhelper.domain.card.CardCharacter
|
||||||
|
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.SpecialMissions
|
||||||
|
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.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 = [
|
||||||
TemporaryCharacterData::class,
|
Card::class,
|
||||||
TemporaryBECharacterData::class,
|
CardProgress::class,
|
||||||
TemporaryTransformationHistory::class
|
CardCharacter::class,
|
||||||
|
CardAdventure::class,
|
||||||
|
CardFusions::class,
|
||||||
|
Sprite::class,
|
||||||
|
UserCharacter::class,
|
||||||
|
BECharacterData::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 temporaryMonsterDao(): TemporaryMonsterDao
|
abstract fun cardDao(): CardDao
|
||||||
|
abstract fun cardProgressDao(): CardProgressDao
|
||||||
|
abstract fun characterDao(): CharacterDao
|
||||||
|
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,11 @@
|
|||||||
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.CurrencyRepository
|
||||||
|
import com.github.nacabaro.vbhelper.source.DataStoreSecretsRepository
|
||||||
|
|
||||||
interface AppContainer {
|
interface AppContainer {
|
||||||
val db: AppDatabase
|
val db: AppDatabase
|
||||||
|
val dataStoreSecretsRepository: DataStoreSecretsRepository
|
||||||
|
val currencyRepository: CurrencyRepository
|
||||||
}
|
}
|
||||||
@@ -1,14 +1,41 @@
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import androidx.datastore.core.DataStore
|
||||||
|
import androidx.datastore.dataStore
|
||||||
|
import androidx.datastore.preferences.core.Preferences
|
||||||
|
import androidx.datastore.preferences.preferencesDataStore
|
||||||
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.CurrencyRepository
|
||||||
|
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"
|
||||||
|
private const val USER_PREFERENCES_NAME = "user_preferences"
|
||||||
|
val Context.secretsStore: DataStore<Secrets> by dataStore(
|
||||||
|
fileName = SECRETS_DATA_STORE_NAME,
|
||||||
|
serializer = SecretsSerializer
|
||||||
|
)
|
||||||
|
|
||||||
|
val Context.currencyStore: DataStore<Preferences> by preferencesDataStore(
|
||||||
|
name = USER_PREFERENCES_NAME
|
||||||
|
)
|
||||||
|
|
||||||
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)
|
||||||
|
|
||||||
|
override val currencyRepository = CurrencyRepository(context.currencyStore)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
package com.github.nacabaro.vbhelper.domain
|
|
||||||
|
|
||||||
import androidx.room.Entity
|
|
||||||
import androidx.room.PrimaryKey
|
|
||||||
|
|
||||||
@Entity
|
|
||||||
data class Dim(
|
|
||||||
@PrimaryKey(autoGenerate = true)
|
|
||||||
val id: Int,
|
|
||||||
val name: String,
|
|
||||||
val stageCount: 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
|
|
||||||
)
|
|
||||||
@@ -7,13 +7,13 @@ import androidx.room.PrimaryKey
|
|||||||
@Entity(
|
@Entity(
|
||||||
foreignKeys = [
|
foreignKeys = [
|
||||||
ForeignKey(
|
ForeignKey(
|
||||||
entity = Mon::class,
|
entity = Character::class,
|
||||||
parentColumns = ["id"],
|
parentColumns = ["id"],
|
||||||
childColumns = ["monId"],
|
childColumns = ["monId"],
|
||||||
onDelete = ForeignKey.CASCADE
|
onDelete = ForeignKey.CASCADE
|
||||||
),
|
),
|
||||||
ForeignKey(
|
ForeignKey(
|
||||||
entity = Mon::class,
|
entity = Character::class,
|
||||||
parentColumns = ["id"],
|
parentColumns = ["id"],
|
||||||
childColumns = ["nextMon"],
|
childColumns = ["nextMon"],
|
||||||
onDelete = ForeignKey.CASCADE
|
onDelete = ForeignKey.CASCADE
|
||||||
|
|||||||
@@ -1,28 +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 Mon (
|
|
||||||
@PrimaryKey val id: Int,
|
|
||||||
val dimId: Int,
|
|
||||||
val monIndex: Int,
|
|
||||||
val name: String,
|
|
||||||
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 evoTime: Int, // In minutes
|
|
||||||
)
|
|
||||||
@@ -13,7 +13,7 @@ import androidx.room.PrimaryKey
|
|||||||
onDelete = ForeignKey.CASCADE
|
onDelete = ForeignKey.CASCADE
|
||||||
),
|
),
|
||||||
ForeignKey(
|
ForeignKey(
|
||||||
entity = Mon::class,
|
entity = Character::class,
|
||||||
parentColumns = ["id"],
|
parentColumns = ["id"],
|
||||||
childColumns = ["monId"],
|
childColumns = ["monId"],
|
||||||
onDelete = ForeignKey.CASCADE
|
onDelete = ForeignKey.CASCADE
|
||||||
|
|||||||
@@ -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
|
||||||
|
)
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.domain.card
|
||||||
|
|
||||||
|
import androidx.room.Entity
|
||||||
|
import androidx.room.PrimaryKey
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
data class Card(
|
||||||
|
@PrimaryKey(autoGenerate = true)
|
||||||
|
val id: Long = 0,
|
||||||
|
val cardId: Int,
|
||||||
|
val logo: ByteArray,
|
||||||
|
val logoWidth: Int,
|
||||||
|
val logoHeight: Int,
|
||||||
|
val name: String,
|
||||||
|
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
|
||||||
|
)
|
||||||
+7
-9
@@ -1,28 +1,26 @@
|
|||||||
package com.github.nacabaro.vbhelper.temporary_domain
|
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.cfogrady.vbnfc.be.BENfcCharacter
|
|
||||||
import com.github.cfogrady.vbnfc.be.FirmwareVersion
|
|
||||||
import com.github.cfogrady.vbnfc.data.NfcCharacter
|
import com.github.cfogrady.vbnfc.data.NfcCharacter
|
||||||
|
|
||||||
@Entity(
|
@Entity(
|
||||||
foreignKeys = [
|
foreignKeys = [
|
||||||
ForeignKey(
|
ForeignKey(
|
||||||
entity = TemporaryCharacterData::class,
|
entity = UserCharacter::class,
|
||||||
parentColumns = ["id"],
|
parentColumns = ["id"],
|
||||||
childColumns = ["id"],
|
childColumns = ["id"],
|
||||||
onDelete = ForeignKey.CASCADE
|
onDelete = ForeignKey.CASCADE
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
data class TemporaryBECharacterData (
|
data class BECharacterData (
|
||||||
@PrimaryKey(autoGenerate = true) val id: Long,
|
@PrimaryKey(autoGenerate = true) val id: Long,
|
||||||
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,
|
||||||
@@ -34,9 +32,9 @@ data class TemporaryBECharacterData (
|
|||||||
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
|
||||||
|
)
|
||||||
+29
@@ -0,0 +1,29 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.domain.device_data
|
||||||
|
|
||||||
|
import androidx.room.Entity
|
||||||
|
import androidx.room.ForeignKey
|
||||||
|
import androidx.room.PrimaryKey
|
||||||
|
import com.github.nacabaro.vbhelper.domain.card.CardCharacter
|
||||||
|
|
||||||
|
@Entity(
|
||||||
|
foreignKeys = [
|
||||||
|
ForeignKey(
|
||||||
|
entity = UserCharacter::class,
|
||||||
|
parentColumns = ["id"],
|
||||||
|
childColumns = ["monId"],
|
||||||
|
onDelete = ForeignKey.CASCADE
|
||||||
|
),
|
||||||
|
ForeignKey(
|
||||||
|
entity = CardCharacter::class,
|
||||||
|
parentColumns = ["id"],
|
||||||
|
childColumns = ["stageId"],
|
||||||
|
onDelete = ForeignKey.CASCADE
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
data class TransformationHistory (
|
||||||
|
@PrimaryKey(autoGenerate = true) val id: Long = 0,
|
||||||
|
val monId: Long,
|
||||||
|
val stageId: Long,
|
||||||
|
val transformationDate: Long
|
||||||
|
)
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
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.data.NfcCharacter
|
||||||
|
import com.github.nacabaro.vbhelper.utils.DeviceType
|
||||||
|
import com.github.nacabaro.vbhelper.domain.card.CardCharacter
|
||||||
|
|
||||||
|
@Entity(
|
||||||
|
foreignKeys = [
|
||||||
|
ForeignKey(
|
||||||
|
entity = CardCharacter::class,
|
||||||
|
parentColumns = ["id"],
|
||||||
|
childColumns = ["charId"],
|
||||||
|
onDelete = ForeignKey.CASCADE
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
/**
|
||||||
|
* UserCharacter represents and instance of a character. The charId should map to a Character
|
||||||
|
*/
|
||||||
|
data class UserCharacter (
|
||||||
|
@PrimaryKey(autoGenerate = true) val id: Long = 0,
|
||||||
|
var charId: Long,
|
||||||
|
var ageInDays: Int,
|
||||||
|
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,
|
||||||
|
var isActive: Boolean
|
||||||
|
)
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
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 = ["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,
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,117 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.dtos
|
||||||
|
|
||||||
|
import com.github.cfogrady.vbnfc.data.NfcCharacter
|
||||||
|
import com.github.nacabaro.vbhelper.utils.DeviceType
|
||||||
|
|
||||||
|
|
||||||
|
object CharacterDtos {
|
||||||
|
data class CharacterWithSprites(
|
||||||
|
var id: Long = 0,
|
||||||
|
var charId: Long,
|
||||||
|
var stage: Int,
|
||||||
|
var attribute: NfcCharacter.Attribute,
|
||||||
|
var ageInDays: Int,
|
||||||
|
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,
|
||||||
|
val currentStage: Int
|
||||||
|
)
|
||||||
|
|
||||||
|
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 vitalPoints: Int,
|
||||||
|
var transformationCountdown: Int,
|
||||||
|
var injuryStatus: NfcCharacter.InjuryStatus,
|
||||||
|
var trophies: Int,
|
||||||
|
var currentPhaseBattlesWon: Int,
|
||||||
|
var currentPhaseBattlesLost: Int,
|
||||||
|
var totalBattlesWon: Int,
|
||||||
|
var totalBattlesLost: Int,
|
||||||
|
var activityLevel: Int,
|
||||||
|
var heartRateCurrent: Int,
|
||||||
|
var characterType: DeviceType,
|
||||||
|
val spriteIdle: ByteArray,
|
||||||
|
val spriteWidth: Int,
|
||||||
|
val spriteHeight: Int,
|
||||||
|
val isBemCard: Boolean,
|
||||||
|
val finishesAdventure: Long,
|
||||||
|
val originalTimeInMinutes: Long
|
||||||
|
)
|
||||||
|
|
||||||
|
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,4 +0,0 @@
|
|||||||
package com.github.nacabaro.vbhelper.dtos
|
|
||||||
|
|
||||||
class MonsterDataCombined {
|
|
||||||
}
|
|
||||||
@@ -1,24 +1,60 @@
|
|||||||
package com.github.nacabaro.vbhelper.navigation
|
package com.github.nacabaro.vbhelper.navigation
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.compose.animation.core.tween
|
||||||
|
import androidx.compose.animation.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.HomeScreen
|
import com.github.nacabaro.vbhelper.screens.cardScreen.CardViewScreen
|
||||||
import com.github.nacabaro.vbhelper.screens.ScanScreen
|
import com.github.nacabaro.vbhelper.screens.homeScreens.HomeScreen
|
||||||
import com.github.nacabaro.vbhelper.screens.SettingsScreen
|
import com.github.nacabaro.vbhelper.screens.itemsScreen.ItemsScreen
|
||||||
import com.github.nacabaro.vbhelper.screens.StorageScreen
|
import com.github.nacabaro.vbhelper.screens.scanScreen.ScanScreen
|
||||||
|
import com.github.nacabaro.vbhelper.screens.scanScreen.ScanScreenControllerImpl
|
||||||
|
import com.github.nacabaro.vbhelper.screens.settingsScreen.SettingsScreen
|
||||||
|
import com.github.nacabaro.vbhelper.screens.spriteViewer.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,
|
|
||||||
isDoneReadingCharacter: Boolean
|
|
||||||
) {
|
) {
|
||||||
val navController = rememberNavController()
|
val navController = rememberNavController()
|
||||||
|
|
||||||
@@ -29,37 +65,130 @@ 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,
|
||||||
|
cardScreenController = applicationNavigationHandlers.cardScreenController
|
||||||
|
)
|
||||||
}
|
}
|
||||||
composable(BottomNavItem.Settings.route) {
|
composable(NavigationItems.Settings.route) {
|
||||||
SettingsScreen(
|
SettingsScreen(
|
||||||
|
navController = navController,
|
||||||
|
settingsScreenController = applicationNavigationHandlers.settingsScreenController
|
||||||
|
)
|
||||||
|
}
|
||||||
|
composable(NavigationItems.Viewer.route) {
|
||||||
|
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(NavigationItems.ApplyItem.route) {
|
||||||
|
val itemId = it.arguments?.getString("itemId")
|
||||||
|
if (itemId != null) {
|
||||||
|
ChooseCharacterScreen(
|
||||||
|
itemsScreenController = applicationNavigationHandlers
|
||||||
|
.itemsScreenController,
|
||||||
|
navController = navController,
|
||||||
|
itemId = itemId.toLong()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
composable(NavigationItems.Adventure.route) {
|
||||||
|
AdventureScreen(
|
||||||
|
navController = navController,
|
||||||
|
storageScreenController = applicationNavigationHandlers
|
||||||
|
.adventureScreenController
|
||||||
|
)
|
||||||
|
}
|
||||||
|
composable(NavigationItems.Credits.route) {
|
||||||
|
CreditsScreen(
|
||||||
|
navController = navController
|
||||||
|
)
|
||||||
|
}
|
||||||
|
composable(NavigationItems.CardAdventure.route) {
|
||||||
|
val cardId = it.arguments?.getString("cardId")
|
||||||
|
if (cardId != null) {
|
||||||
|
CardAdventureScreen(
|
||||||
|
navController = navController,
|
||||||
|
cardId = cardId.toLong(),
|
||||||
|
cardScreenController = applicationNavigationHandlers
|
||||||
|
.cardScreenController
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +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")
|
|
||||||
}
|
|
||||||
@@ -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,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,196 +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.CardElevation
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
|
||||||
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.res.painterResource
|
|
||||||
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.R
|
|
||||||
import com.github.nacabaro.vbhelper.components.TopBanner
|
|
||||||
import com.github.nacabaro.vbhelper.di.VBHelper
|
|
||||||
import com.github.nacabaro.vbhelper.source.StorageRepository
|
|
||||||
import com.github.nacabaro.vbhelper.temporary_domain.TemporaryCharacterData
|
|
||||||
import kotlinx.coroutines.coroutineScope
|
|
||||||
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 { mutableStateListOf<TemporaryCharacterData>() }
|
|
||||||
|
|
||||||
var selectedCharacter by remember { mutableStateOf<Long?>(null) }
|
|
||||||
|
|
||||||
LaunchedEffect(storageRepository) {
|
|
||||||
coroutineScope.launch {
|
|
||||||
monList.clear()
|
|
||||||
monList.addAll(storageRepository.getAllCharacters())
|
|
||||||
Log.d("StorageScreen", "Updated data: $monList")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.d("StorageScreen", "monList: $monList")
|
|
||||||
|
|
||||||
Scaffold (
|
|
||||||
topBar = { TopBanner(text = "My Digimon") }
|
|
||||||
) { contentPadding ->
|
|
||||||
if (monList.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) { index ->
|
|
||||||
var showDialog by rememberSaveable { mutableStateOf(false) }
|
|
||||||
|
|
||||||
StorageEntry(
|
|
||||||
name = index.dimId.toString() + " - " + index.charIndex.toString(),
|
|
||||||
icon = R.drawable.ic_launcher_foreground,
|
|
||||||
onClick = { selectedCharacter = index.id },
|
|
||||||
modifier = Modifier
|
|
||||||
.padding(8.dp)
|
|
||||||
)
|
|
||||||
|
|
||||||
if (selectedCharacter != null) {
|
|
||||||
StorageDialog(
|
|
||||||
characterId = selectedCharacter!!,
|
|
||||||
onDismissRequest = { selectedCharacter = null }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun StorageEntry(
|
|
||||||
name: String,
|
|
||||||
icon: Int,
|
|
||||||
onClick: () -> Unit = {},
|
|
||||||
modifier: Modifier = Modifier
|
|
||||||
) {
|
|
||||||
Card(
|
|
||||||
shape = MaterialTheme.shapes.medium,
|
|
||||||
onClick = onClick,
|
|
||||||
modifier = modifier
|
|
||||||
.padding(8.dp)
|
|
||||||
) {
|
|
||||||
Column(
|
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxSize()
|
|
||||||
) {
|
|
||||||
Image(
|
|
||||||
painter = painterResource(id = icon),
|
|
||||||
contentDescription = name,
|
|
||||||
modifier = Modifier
|
|
||||||
.padding(8.dp)
|
|
||||||
.size(64.dp)
|
|
||||||
)
|
|
||||||
Text(
|
|
||||||
text = name,
|
|
||||||
textAlign = TextAlign.Center,
|
|
||||||
modifier = Modifier
|
|
||||||
.padding(8.dp)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@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<TemporaryCharacterData?>(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)
|
||||||
|
}
|
||||||
+103
@@ -0,0 +1,103 @@
|
|||||||
|
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.flow.first
|
||||||
|
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()
|
||||||
|
.first()
|
||||||
|
.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: -")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+66
@@ -0,0 +1,66 @@
|
|||||||
|
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.getValue
|
||||||
|
import androidx.compose.runtime.mutableIntStateOf
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.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 by remember { mutableIntStateOf(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 - 1
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
+74
@@ -0,0 +1,74 @@
|
|||||||
|
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.flow.first
|
||||||
|
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()
|
||||||
|
.first()
|
||||||
|
.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,164 @@
|
|||||||
|
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.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.domain.items.ItemType
|
||||||
|
import com.github.nacabaro.vbhelper.dtos.ItemDtos
|
||||||
|
import com.github.nacabaro.vbhelper.ui.theme.VBHelperTheme
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ItemDialog(
|
||||||
|
item: ItemDtos.ItemsWithQuantities,
|
||||||
|
onClickCancel: () -> Unit,
|
||||||
|
onClickUse: (() -> Unit)? = null,
|
||||||
|
onClickPurchase: (() -> Unit)? = null,
|
||||||
|
) {
|
||||||
|
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 = getIconResource(item.itemIcon)),
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier
|
||||||
|
.size(96.dp)
|
||||||
|
.align(Alignment.Center)
|
||||||
|
)
|
||||||
|
Icon(
|
||||||
|
painter = painterResource(id = getLengthResource(item.itemLength)),
|
||||||
|
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 = item.name,
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Text(
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
fontSize = MaterialTheme.typography.bodyMedium.fontSize,
|
||||||
|
fontFamily = MaterialTheme.typography.bodyMedium.fontFamily,
|
||||||
|
text = item.description,
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(4.dp)
|
||||||
|
)
|
||||||
|
if (onClickPurchase != null) {
|
||||||
|
Text(
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
fontSize = MaterialTheme.typography.bodySmall.fontSize,
|
||||||
|
fontFamily = MaterialTheme.typography.bodySmall.fontFamily,
|
||||||
|
text = "Costs ${item.price} credits",
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Text(
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
fontSize = MaterialTheme.typography.bodySmall.fontSize,
|
||||||
|
fontFamily = MaterialTheme.typography.bodySmall.fontFamily,
|
||||||
|
text = "You have ${item.quantity} of this item",
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
)
|
||||||
|
|
||||||
|
Row (
|
||||||
|
horizontalArrangement = Arrangement.Center,
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
if (onClickUse != null) {
|
||||||
|
Button(
|
||||||
|
onClick = onClickUse
|
||||||
|
) {
|
||||||
|
Text("Use item")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (onClickPurchase != null) {
|
||||||
|
Button(
|
||||||
|
onClick = onClickPurchase
|
||||||
|
) {
|
||||||
|
Text("Purchase")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.size(8.dp))
|
||||||
|
Button(
|
||||||
|
onClick = onClickCancel
|
||||||
|
) {
|
||||||
|
Text("Cancel")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
@Preview(showBackground = true)
|
||||||
|
fun PreviewItemDialog() {
|
||||||
|
VBHelperTheme {
|
||||||
|
ItemDialog(
|
||||||
|
item = ItemDtos.ItemsWithQuantities(
|
||||||
|
name = "AP Training x3 (60 min)",
|
||||||
|
description = "Boosts AP during training (for 60 minutes)",
|
||||||
|
itemIcon = R.drawable.baseline_attack_24,
|
||||||
|
itemLength = R.drawable.baseline_60_min_timer,
|
||||||
|
quantity = 19,
|
||||||
|
id = 1,
|
||||||
|
price = 500,
|
||||||
|
itemType = ItemType.BEITEM
|
||||||
|
),
|
||||||
|
onClickUse = { },
|
||||||
|
onClickCancel = { }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.screens.itemsScreen
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.aspectRatio
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.material3.Card
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
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.unit.dp
|
||||||
|
import com.github.nacabaro.vbhelper.dtos.ItemDtos
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ItemElement(
|
||||||
|
item: ItemDtos.ItemsWithQuantities,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
onClick: (() -> Unit) = { }
|
||||||
|
) {
|
||||||
|
Card (
|
||||||
|
onClick = onClick,
|
||||||
|
modifier = modifier
|
||||||
|
.aspectRatio(1f)
|
||||||
|
) {
|
||||||
|
Box(modifier = Modifier.fillMaxSize()) {
|
||||||
|
Icon(
|
||||||
|
painter = painterResource(id = getIconResource(item.itemIcon)),
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier
|
||||||
|
.size(96.dp)
|
||||||
|
.align(Alignment.Center)
|
||||||
|
.padding(16.dp)
|
||||||
|
)
|
||||||
|
Icon(
|
||||||
|
painter = painterResource(id = getLengthResource(item.itemLength)),
|
||||||
|
contentDescription = null,
|
||||||
|
tint = MaterialTheme.colorScheme.surfaceTint,
|
||||||
|
modifier = Modifier
|
||||||
|
.size(48.dp)
|
||||||
|
.align(Alignment.TopStart)
|
||||||
|
.padding(8.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.screens.itemsScreen
|
||||||
|
|
||||||
|
import com.github.nacabaro.vbhelper.R
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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,134 @@
|
|||||||
|
package com.github.nacabaro.vbhelper.screens.itemsScreen
|
||||||
|
|
||||||
|
import android.widget.Toast
|
||||||
|
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.foundation.lazy.grid.GridCells
|
||||||
|
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
|
||||||
|
import androidx.compose.foundation.lazy.grid.items
|
||||||
|
import androidx.compose.material3.Card
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.navigation.NavController
|
||||||
|
import com.github.nacabaro.vbhelper.database.AppDatabase
|
||||||
|
import com.github.nacabaro.vbhelper.di.VBHelper
|
||||||
|
import com.github.nacabaro.vbhelper.dtos.ItemDtos
|
||||||
|
import com.github.nacabaro.vbhelper.source.CurrencyRepository
|
||||||
|
import com.github.nacabaro.vbhelper.source.ItemsRepository
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ItemsStore(
|
||||||
|
navController: NavController
|
||||||
|
) {
|
||||||
|
val application = LocalContext.current.applicationContext as VBHelper
|
||||||
|
val itemsRepository = ItemsRepository(application.container.db)
|
||||||
|
val myItems by itemsRepository.getAllItems().collectAsState(emptyList())
|
||||||
|
|
||||||
|
var selectedElementIndex by remember { mutableStateOf<Int?>(null) }
|
||||||
|
|
||||||
|
val currencyRepository = application.container.currencyRepository
|
||||||
|
val currentCurrency = currencyRepository.currencyValue.collectAsState(0)
|
||||||
|
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
|
|
||||||
|
if (myItems.isEmpty()) {
|
||||||
|
Column(
|
||||||
|
verticalArrangement = Arrangement.Center,
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
modifier = Modifier.fillMaxSize()
|
||||||
|
) {
|
||||||
|
Text("No items")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Column() {
|
||||||
|
Card(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(8.dp)
|
||||||
|
.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = "${currentCurrency.value} credits",
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(8.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
LazyVerticalGrid(
|
||||||
|
columns = GridCells.Fixed(3),
|
||||||
|
modifier = Modifier
|
||||||
|
) {
|
||||||
|
items(myItems) { index ->
|
||||||
|
ItemElement(
|
||||||
|
item = index,
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(8.dp),
|
||||||
|
onClick = {
|
||||||
|
selectedElementIndex = myItems.indexOf(index)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (selectedElementIndex != null) {
|
||||||
|
ItemDialog(
|
||||||
|
item = myItems[selectedElementIndex!!],
|
||||||
|
onClickPurchase = {
|
||||||
|
scope.launch {
|
||||||
|
Toast.makeText(
|
||||||
|
application.applicationContext,
|
||||||
|
purchaseItem(
|
||||||
|
application.container.db,
|
||||||
|
myItems[selectedElementIndex!!],
|
||||||
|
currencyRepository
|
||||||
|
),
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
).show(
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onClickCancel = { selectedElementIndex = null }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun purchaseItem(
|
||||||
|
db: AppDatabase,
|
||||||
|
item: ItemDtos.ItemsWithQuantities,
|
||||||
|
currencyRepository: CurrencyRepository
|
||||||
|
): String {
|
||||||
|
if (currencyRepository.currencyValue.first() < item.price) {
|
||||||
|
return "Not enough credits"
|
||||||
|
} else {
|
||||||
|
db
|
||||||
|
.itemDao()
|
||||||
|
.purchaseItem(
|
||||||
|
item.id,
|
||||||
|
1
|
||||||
|
)
|
||||||
|
|
||||||
|
currencyRepository
|
||||||
|
.setCurrencyValue(
|
||||||
|
currencyRepository.currencyValue.first() - item.price
|
||||||
|
)
|
||||||
|
|
||||||
|
return "Purchase successful!"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
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.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.unit.dp
|
||||||
|
import androidx.navigation.NavController
|
||||||
|
import com.github.nacabaro.vbhelper.di.VBHelper
|
||||||
|
import com.github.nacabaro.vbhelper.navigation.NavigationItems
|
||||||
|
import com.github.nacabaro.vbhelper.source.ItemsRepository
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun MyItems(
|
||||||
|
navController: NavController,
|
||||||
|
) {
|
||||||
|
val application = LocalContext.current.applicationContext as VBHelper
|
||||||
|
val itemsRepository = ItemsRepository(application.container.db)
|
||||||
|
val myItems by itemsRepository.getUserItems().collectAsState(emptyList())
|
||||||
|
|
||||||
|
var selectedElementIndex by remember { mutableStateOf<Int?>(null) }
|
||||||
|
|
||||||
|
if (myItems.isEmpty()) {
|
||||||
|
Column(
|
||||||
|
verticalArrangement = Arrangement.Center,
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
modifier = Modifier.fillMaxSize()
|
||||||
|
) {
|
||||||
|
Text("No items")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LazyVerticalGrid(
|
||||||
|
columns = GridCells.Fixed(3),
|
||||||
|
modifier = Modifier
|
||||||
|
) {
|
||||||
|
items(myItems) { index ->
|
||||||
|
ItemElement(
|
||||||
|
item = index,
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(8.dp),
|
||||||
|
onClick = {
|
||||||
|
selectedElementIndex = myItems.indexOf(index)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectedElementIndex != null) {
|
||||||
|
ItemDialog(
|
||||||
|
item = myItems[selectedElementIndex!!],
|
||||||
|
onClickUse = {
|
||||||
|
navController
|
||||||
|
.navigate(
|
||||||
|
NavigationItems
|
||||||
|
.ApplyItem.route
|
||||||
|
.replace(
|
||||||
|
"{itemId}",
|
||||||
|
myItems[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)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+26
-40
@@ -1,57 +1,37 @@
|
|||||||
package com.github.nacabaro.vbhelper.screens
|
package com.github.nacabaro.vbhelper.screens.scanScreen.cardSelect
|
||||||
|
|
||||||
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.fillMaxWidth
|
|
||||||
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.foundation.lazy.LazyColumn
|
|
||||||
import androidx.compose.material3.Card
|
import androidx.compose.material3.Card
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Scaffold
|
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.painterResource
|
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.unit.dp
|
||||||
import com.github.nacabaro.vbhelper.R
|
import com.github.nacabaro.vbhelper.utils.BitmapData
|
||||||
import com.github.nacabaro.vbhelper.components.TopBanner
|
import com.github.nacabaro.vbhelper.utils.getBitmap
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun DexScreen() {
|
fun ScanCardEntry(
|
||||||
Scaffold (
|
|
||||||
topBar = { TopBanner("Discovered Digimon") }
|
|
||||||
) { contentPadding ->
|
|
||||||
LazyColumn (
|
|
||||||
modifier = Modifier
|
|
||||||
.padding(top = contentPadding.calculateTopPadding())
|
|
||||||
) {
|
|
||||||
items(100) { i ->
|
|
||||||
DexDiMEntry(
|
|
||||||
name = "Digimon $i",
|
|
||||||
icon = R.drawable.baseline_egg_24,
|
|
||||||
onClick = {},
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.padding(
|
|
||||||
vertical = 8.dp,
|
|
||||||
horizontal = 16.dp
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun DexDiMEntry(
|
|
||||||
name: String,
|
name: String,
|
||||||
icon: Int,
|
logo: BitmapData,
|
||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
modifier: Modifier = Modifier
|
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 (
|
Card (
|
||||||
shape = MaterialTheme.shapes.medium,
|
shape = MaterialTheme.shapes.medium,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
@@ -64,17 +44,23 @@ fun DexDiMEntry(
|
|||||||
.padding(8.dp)
|
.padding(8.dp)
|
||||||
) {
|
) {
|
||||||
Image (
|
Image (
|
||||||
painter = painterResource(id = icon),
|
bitmap = imageBitmap,
|
||||||
contentDescription = name,
|
contentDescription = name,
|
||||||
|
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
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user