mirror of
https://github.com/nacabaro/vbhelper.git
synced 2026-10-01 15:41:36 +00:00
Compare commits
13
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e5ccff16d0 | ||
|
|
4b866aad8a | ||
|
|
33ad2728b8 | ||
|
|
05f04fdc49 | ||
|
|
07315fe11b | ||
|
|
2e72b543f6 | ||
|
|
81bfec5ac5 | ||
|
|
8ec98b9d39 | ||
|
|
e549fb4469 | ||
|
|
8d62437aae | ||
|
|
7a8815694d | ||
|
|
464f2e48a7 | ||
|
|
f641e8aaa0 |
+14
-21
@@ -2,22 +2,21 @@ import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
|||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
alias(libs.plugins.android.application)
|
alias(libs.plugins.android.application)
|
||||||
alias(libs.plugins.kotlin.android)
|
|
||||||
alias(libs.plugins.kotlin.compose)
|
alias(libs.plugins.kotlin.compose)
|
||||||
id("com.google.devtools.ksp") version "2.3.0"
|
alias(libs.plugins.vksp)
|
||||||
id("com.google.protobuf")
|
alias(libs.plugins.vprotobuf)
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
namespace = "com.github.nacabaro.vbhelper"
|
namespace = "com.github.nacabaro.vbhelper"
|
||||||
compileSdk = 36
|
compileSdk = 37
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId = "com.github.nacabaro.vbhelper"
|
applicationId = "com.github.nacabaro.vbhelper"
|
||||||
minSdk = 28
|
minSdk = 28
|
||||||
targetSdk = 36
|
targetSdk = 37
|
||||||
versionCode = 1
|
versionCode = 1
|
||||||
versionName = "Alpha 0.6.3"
|
versionName = "Alpha 0.6.4"
|
||||||
|
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
}
|
}
|
||||||
@@ -56,9 +55,6 @@ protobuf {
|
|||||||
artifact = "com.google.protobuf:protoc:4.27.0"
|
artifact = "com.google.protobuf:protoc:4.27.0"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generates the java Protobuf-lite code for the Protobufs in this project. See
|
|
||||||
// https://github.com/google/protobuf-gradle-plugin#customizing-protobuf-compilation
|
|
||||||
// for more information.
|
|
||||||
generateProtoTasks {
|
generateProtoTasks {
|
||||||
all().forEach { task ->
|
all().forEach { task ->
|
||||||
task.builtins {
|
task.builtins {
|
||||||
@@ -90,6 +86,11 @@ dependencies {
|
|||||||
implementation(libs.androidx.ui.tooling.preview)
|
implementation(libs.androidx.ui.tooling.preview)
|
||||||
implementation(libs.androidx.material3)
|
implementation(libs.androidx.material3)
|
||||||
implementation(libs.androidx.compose.material.icons.extended)
|
implementation(libs.androidx.compose.material.icons.extended)
|
||||||
|
implementation(libs.play.services.wearable)
|
||||||
|
implementation(libs.kotlinx.coroutines.play.services)
|
||||||
|
implementation(libs.timber)
|
||||||
|
implementation(libs.tinylog.api)
|
||||||
|
implementation(libs.tinylog.impl)
|
||||||
|
|
||||||
debugImplementation(libs.androidx.ui.tooling)
|
debugImplementation(libs.androidx.ui.tooling)
|
||||||
debugImplementation(libs.androidx.ui.test.manifest)
|
debugImplementation(libs.androidx.ui.test.manifest)
|
||||||
@@ -104,18 +105,10 @@ dependencies {
|
|||||||
androidTestImplementation(platform(libs.androidx.compose.bom))
|
androidTestImplementation(platform(libs.androidx.compose.bom))
|
||||||
androidTestImplementation(libs.androidx.ui.test.junit4)
|
androidTestImplementation(libs.androidx.ui.test.junit4)
|
||||||
|
|
||||||
debugImplementation(libs.androidx.ui.tooling)
|
implementation(libs.retrofit)
|
||||||
debugImplementation(libs.androidx.ui.test.manifest)
|
implementation(libs.retrofit.converter.gson)
|
||||||
implementation("androidx.navigation:navigation-compose:2.7.0")
|
implementation(libs.gson)
|
||||||
implementation("com.google.android.material:material:1.2.0")
|
|
||||||
implementation(libs.protobuf.javalite)
|
|
||||||
implementation("androidx.compose.material:material")
|
|
||||||
implementation("androidx.datastore:datastore-preferences:1.1.7")
|
|
||||||
|
|
||||||
implementation("com.squareup.retrofit2:retrofit:2.9.0")
|
|
||||||
implementation("com.squareup.retrofit2:converter-gson:2.9.0")
|
|
||||||
implementation("com.google.code.gson:gson:2.10.1")
|
|
||||||
|
|
||||||
// HTTP request logging
|
// HTTP request logging
|
||||||
implementation("com.squareup.okhttp3:logging-interceptor:4.11.0")
|
implementation(libs.okhttp.logging.interceptor)
|
||||||
}
|
}
|
||||||
-439
@@ -1,439 +0,0 @@
|
|||||||
package com.github.nacabaro.vbhelper.screens.scanScreen
|
|
||||||
|
|
||||||
import android.nfc.IsoDep
|
|
||||||
import android.nfc.NfcA
|
|
||||||
import android.nfc.Tag
|
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
|
||||||
import com.github.cfogrady.vbnfc.TagCommunicator
|
|
||||||
import com.github.cfogrady.vbnfc.data.NfcCharacter
|
|
||||||
import com.github.cfogrady.vbnfc.vb.VBNfcCharacter
|
|
||||||
import org.junit.Test
|
|
||||||
import org.junit.Before
|
|
||||||
import org.junit.runner.RunWith
|
|
||||||
import org.mockito.Mock
|
|
||||||
import org.mockito.MockitoAnnotations
|
|
||||||
import org.mockito.kotlin.whenever
|
|
||||||
import org.mockito.kotlin.any
|
|
||||||
import org.mockito.kotlin.verify
|
|
||||||
import kotlin.test.assertEquals
|
|
||||||
import kotlin.test.assertNotNull
|
|
||||||
import kotlin.test.assertNull
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Instrumented tests for NFC-A (Bandai toy) transfer functionality.
|
|
||||||
*
|
|
||||||
* These tests verify:
|
|
||||||
* - Transport detection (NFC-A vs ISO-DEP)
|
|
||||||
* - Read operations (Watch to VBH)
|
|
||||||
* - Write operations (VBH to Watch)
|
|
||||||
* - Slot state detection
|
|
||||||
* - Error handling
|
|
||||||
*
|
|
||||||
* Run with:
|
|
||||||
* ./gradlew connectedAndroidTest --tests ScanNfcaIntegrationTest
|
|
||||||
*/
|
|
||||||
@RunWith(AndroidJUnit4::class)
|
|
||||||
class ScanNfcaIntegrationTest {
|
|
||||||
|
|
||||||
@Mock
|
|
||||||
private lateinit var mockTag: Tag
|
|
||||||
|
|
||||||
@Mock
|
|
||||||
private lateinit var mockNfcA: NfcA
|
|
||||||
|
|
||||||
@Mock
|
|
||||||
private lateinit var mockIsoDep: IsoDep
|
|
||||||
|
|
||||||
@Mock
|
|
||||||
private lateinit var mockTagCommunicator: TagCommunicator
|
|
||||||
|
|
||||||
@Before
|
|
||||||
fun setup() {
|
|
||||||
MockitoAnnotations.openMocks(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ==========================================
|
|
||||||
// TEST GROUP 1: Transport Detection
|
|
||||||
// ==========================================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TEST 1.1: Verify NFC-A is correctly detected
|
|
||||||
*
|
|
||||||
* When: Tag with NFC-A support is detected
|
|
||||||
* Then: Transport should be NFC_A
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
fun testNfcATransportDetection() {
|
|
||||||
// Arrange
|
|
||||||
whenever(NfcA.get(mockTag)).thenReturn(mockNfcA)
|
|
||||||
whenever(IsoDep.get(mockTag)).thenReturn(null)
|
|
||||||
|
|
||||||
// Act
|
|
||||||
val nfcA = NfcA.get(mockTag)
|
|
||||||
val isoDep = IsoDep.get(mockTag)
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
assertNotNull(nfcA, "NfcA should be detected")
|
|
||||||
assertNull(isoDep, "IsoDep should not be present")
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TEST 1.2: Verify ISO-DEP vs NFC-A detection priority
|
|
||||||
*
|
|
||||||
* When: Both NFC-A and ISO-DEP present (shouldn't happen, but test anyway)
|
|
||||||
* Then: ISO-DEP should take priority if it's VitalWear
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
fun testIsoDepPriorityOverNfcA() {
|
|
||||||
// Arrange
|
|
||||||
whenever(NfcA.get(mockTag)).thenReturn(mockNfcA)
|
|
||||||
whenever(IsoDep.get(mockTag)).thenReturn(mockIsoDep)
|
|
||||||
|
|
||||||
// Act
|
|
||||||
val isoDep = IsoDep.get(mockTag)
|
|
||||||
val nfcA = NfcA.get(mockTag)
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
// In actual code, ISO-DEP is checked first
|
|
||||||
assertNotNull(isoDep, "ISO-DEP should be detected first")
|
|
||||||
assertNotNull(nfcA, "NFC-A should also be present")
|
|
||||||
}
|
|
||||||
|
|
||||||
// ==========================================
|
|
||||||
// TEST GROUP 2: Slot State Detection
|
|
||||||
// ==========================================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TEST 2.1: Verify device not full detection
|
|
||||||
*
|
|
||||||
* When: Device has active=true, backup=false
|
|
||||||
* Then: Should allow write operation
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
fun testSlotStateAllowsWriteWhenNotFull() {
|
|
||||||
// Arrange: Create mock state - device not full
|
|
||||||
val slotState = NfcASlotState(
|
|
||||||
count = 2,
|
|
||||||
activePresent = true,
|
|
||||||
backupPresent = false
|
|
||||||
)
|
|
||||||
|
|
||||||
// Act
|
|
||||||
val isFull = slotState.isFull()
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
assertEquals(false, isFull, "Device should not be full")
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TEST 2.2: Verify device full detection
|
|
||||||
*
|
|
||||||
* When: Device has active=true, backup=true
|
|
||||||
* Then: Should block write operation
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
fun testSlotStateBlocksWriteWhenFull() {
|
|
||||||
// Arrange: Create mock state - device full
|
|
||||||
val slotState = NfcASlotState(
|
|
||||||
count = 2,
|
|
||||||
activePresent = true,
|
|
||||||
backupPresent = true
|
|
||||||
)
|
|
||||||
|
|
||||||
// Act
|
|
||||||
val isFull = slotState.isFull()
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
assertEquals(true, isFull, "Device should be full")
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TEST 2.3: Verify count-based full detection
|
|
||||||
*
|
|
||||||
* When: Device reports count >= 2
|
|
||||||
* Then: Should be considered full
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
fun testSlotStateFullByCount() {
|
|
||||||
// Arrange
|
|
||||||
val slotStateFull = NfcASlotState(count = 2, activePresent = null, backupPresent = null)
|
|
||||||
val slotStateNotFull = NfcASlotState(count = 1, activePresent = null, backupPresent = null)
|
|
||||||
|
|
||||||
// Act
|
|
||||||
val isFullCount2 = slotStateFull.isFull()
|
|
||||||
val isFullCount1 = slotStateNotFull.isFull()
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
assertEquals(true, isFullCount2, "Count >= 2 should be full")
|
|
||||||
assertEquals(false, isFullCount1, "Count < 2 should not be full")
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TEST 2.4: Verify fallback when introspection fails
|
|
||||||
*
|
|
||||||
* When: Cannot introspect slot state (all null)
|
|
||||||
* Then: Should default to non-blocking path
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
fun testSlotStateFallbackWhenUnknown() {
|
|
||||||
// Arrange: No slot info available
|
|
||||||
val slotState = NfcASlotState(count = null, activePresent = null, backupPresent = null)
|
|
||||||
|
|
||||||
// Act
|
|
||||||
val isFull = slotState.isFull()
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
assertEquals(false, isFull, "Unknown state should not block (default to false)")
|
|
||||||
}
|
|
||||||
|
|
||||||
// ==========================================
|
|
||||||
// TEST GROUP 3: Character Conversion
|
|
||||||
// ==========================================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TEST 3.1: Verify NFC character to DB character conversion
|
|
||||||
*
|
|
||||||
* When: VBNfcCharacter is read from toy
|
|
||||||
* Then: Should convert to database format correctly
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
fun testNfcCharacterConversionVB() {
|
|
||||||
// Arrange
|
|
||||||
val vbNfcCharacter = VBNfcCharacter()
|
|
||||||
// In real test, would populate with actual data
|
|
||||||
|
|
||||||
// Act
|
|
||||||
// This would call FromNfcConverter.addCharacter()
|
|
||||||
// For now, just verify the object exists
|
|
||||||
assertNotNull(vbNfcCharacter, "VBNfcCharacter should be created")
|
|
||||||
}
|
|
||||||
|
|
||||||
// ==========================================
|
|
||||||
// TEST GROUP 4: Write Operation Validation
|
|
||||||
// ==========================================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TEST 4.1: Verify write is blocked when device full
|
|
||||||
*
|
|
||||||
* Scenario:
|
|
||||||
* - User selects character to write
|
|
||||||
* - Device has 2 active+backup (full)
|
|
||||||
* - Click "VBH to Watch"
|
|
||||||
*
|
|
||||||
* Expected:
|
|
||||||
* - WriteResult.BLOCKED_DEVICE_FULL
|
|
||||||
* - No transfer occurs
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
fun testWriteBlockedWhenDeviceFull() {
|
|
||||||
// Arrange
|
|
||||||
val slotState = NfcASlotState(
|
|
||||||
count = 2,
|
|
||||||
activePresent = true,
|
|
||||||
backupPresent = true
|
|
||||||
)
|
|
||||||
|
|
||||||
// Act
|
|
||||||
val isFull = slotState.isFull()
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
assertEquals(true, isFull, "Should block when full")
|
|
||||||
// In real test, would verify WriteResult.BLOCKED_DEVICE_FULL returned
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TEST 4.2: Verify write allowed when slot available
|
|
||||||
*
|
|
||||||
* Scenario:
|
|
||||||
* - User selects character to write
|
|
||||||
* - Device has active only (backup empty)
|
|
||||||
* - Click "VBH to Watch"
|
|
||||||
*
|
|
||||||
* Expected:
|
|
||||||
* - WriteResult.MOVE_CONFIRMED
|
|
||||||
* - Active→Backup migration performed
|
|
||||||
* - Character transferred
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
fun testWriteAllowedWhenSlotAvailable() {
|
|
||||||
// Arrange
|
|
||||||
val slotState = NfcASlotState(
|
|
||||||
count = 2,
|
|
||||||
activePresent = true,
|
|
||||||
backupPresent = false
|
|
||||||
)
|
|
||||||
|
|
||||||
// Act
|
|
||||||
val isFull = slotState.isFull()
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
assertEquals(false, isFull, "Should allow when slot available")
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TEST 4.3: Verify write rejected when no active character
|
|
||||||
*
|
|
||||||
* Scenario:
|
|
||||||
* - Device only has backup (no active)
|
|
||||||
* - Click "VBH to Watch"
|
|
||||||
*
|
|
||||||
* Expected:
|
|
||||||
* - WriteResult.COPIED
|
|
||||||
* - Transfer skipped
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
fun testWriteSkippedWhenNoActiveCharacter() {
|
|
||||||
// Arrange: Only backup, no active
|
|
||||||
val slotState = NfcASlotState(
|
|
||||||
count = 1,
|
|
||||||
activePresent = false,
|
|
||||||
backupPresent = true
|
|
||||||
)
|
|
||||||
|
|
||||||
// Act
|
|
||||||
val isValidForWrite = slotState.activePresent == true && slotState.backupPresent == false
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
assertEquals(false, isValidForWrite, "Should reject write when no active")
|
|
||||||
}
|
|
||||||
|
|
||||||
// ==========================================
|
|
||||||
// TEST GROUP 5: Debouncing
|
|
||||||
// ==========================================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TEST 5.1: Verify rapid re-taps are debounced
|
|
||||||
*
|
|
||||||
* Scenario:
|
|
||||||
* - User taps toy for read
|
|
||||||
* - User immediately taps again (< 1.5 sec)
|
|
||||||
*
|
|
||||||
* Expected:
|
|
||||||
* - Only first tap processes
|
|
||||||
* - Second tap ignored
|
|
||||||
* - Database contains only 1 character
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
fun testRapidTapDebouncing() {
|
|
||||||
// Arrange
|
|
||||||
val TAG_DEBOUNCE_MS = 1500L
|
|
||||||
val now = System.currentTimeMillis()
|
|
||||||
val firstTapTime = now
|
|
||||||
val secondTapTime = now + 500 // 500ms later (within debounce)
|
|
||||||
|
|
||||||
// Act
|
|
||||||
val timeDifference = secondTapTime - firstTapTime
|
|
||||||
val isWithinDebounce = timeDifference < TAG_DEBOUNCE_MS
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
assertEquals(true, isWithinDebounce, "Second tap should be debounced")
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TEST 5.2: Verify tap after debounce window is processed
|
|
||||||
*
|
|
||||||
* Scenario:
|
|
||||||
* - User taps toy for read
|
|
||||||
* - User taps again after 2 seconds (> 1.5 sec)
|
|
||||||
*
|
|
||||||
* Expected:
|
|
||||||
* - First tap processes
|
|
||||||
* - Second tap also processes
|
|
||||||
* - Database contains 2 characters
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
fun testTapAllowedAfterDebounceWindow() {
|
|
||||||
// Arrange
|
|
||||||
val TAG_DEBOUNCE_MS = 1500L
|
|
||||||
val now = System.currentTimeMillis()
|
|
||||||
val firstTapTime = now
|
|
||||||
val secondTapTime = now + 2000 // 2 seconds later (outside debounce)
|
|
||||||
|
|
||||||
// Act
|
|
||||||
val timeDifference = secondTapTime - firstTapTime
|
|
||||||
val isWithinDebounce = timeDifference < TAG_DEBOUNCE_MS
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
assertEquals(false, isWithinDebounce, "Second tap should be processed")
|
|
||||||
}
|
|
||||||
|
|
||||||
// ==========================================
|
|
||||||
// TEST GROUP 6: Error Handling
|
|
||||||
// ==========================================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TEST 6.1: Verify graceful handling of corrupt character data
|
|
||||||
*
|
|
||||||
* Scenario:
|
|
||||||
* - Toy has corrupted character data
|
|
||||||
* - User clicks "Watch to VBH"
|
|
||||||
* - Tap toy
|
|
||||||
*
|
|
||||||
* Expected:
|
|
||||||
* - Exception caught
|
|
||||||
* - Toast shown: "Whoops"
|
|
||||||
* - App doesn't crash
|
|
||||||
* - User can retry
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
fun testCorruptCharacterHandling() {
|
|
||||||
// This is more of an integration test
|
|
||||||
// Would require mocking TagCommunicator.receiveCharacter() to throw
|
|
||||||
|
|
||||||
// Pseudocode:
|
|
||||||
// whenever(mockTagCommunicator.receiveCharacter())
|
|
||||||
// .thenThrow(IOException("Corrupt data"))
|
|
||||||
|
|
||||||
// In real test:
|
|
||||||
// Call onClickRead()
|
|
||||||
// Verify: Toast shown, no crash, app recovers
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TEST 6.2: Verify NFC connection timeout handling
|
|
||||||
*
|
|
||||||
* Scenario:
|
|
||||||
* - User starts read/write
|
|
||||||
* - Remove toy during transfer
|
|
||||||
*
|
|
||||||
* Expected:
|
|
||||||
* - Connection timeout
|
|
||||||
* - Toast: "Whoops"
|
|
||||||
* - Reader mode disabled
|
|
||||||
* - App recovers
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
fun testNfcConnectionTimeoutHandling() {
|
|
||||||
// Pseudocode:
|
|
||||||
// whenever(mockNfcA.connect())
|
|
||||||
// .thenThrow(IOException("NFC connection lost"))
|
|
||||||
|
|
||||||
// In real test:
|
|
||||||
// Verify timeout is caught, UI updates, no crash
|
|
||||||
}
|
|
||||||
|
|
||||||
// ==========================================
|
|
||||||
// Helper Classes (from ScanScreenControllerImpl)
|
|
||||||
// ==========================================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Mirrors the NfcASlotState data class from ScanScreenControllerImpl.kt
|
|
||||||
*/
|
|
||||||
private data class NfcASlotState(
|
|
||||||
val count: Int?,
|
|
||||||
val activePresent: Boolean?,
|
|
||||||
val backupPresent: Boolean?,
|
|
||||||
) {
|
|
||||||
fun isFull(): Boolean {
|
|
||||||
if (count != null) {
|
|
||||||
return count >= 2
|
|
||||||
}
|
|
||||||
if (activePresent != null && backupPresent != null) {
|
|
||||||
return activePresent && backupPresent
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@@ -7,6 +7,7 @@
|
|||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||||
|
<uses-permission android:name="android.permission.VIBRATE" />
|
||||||
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
|
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
|
||||||
tools:ignore="ScopedStorage" />
|
tools:ignore="ScopedStorage" />
|
||||||
|
|
||||||
@@ -34,7 +35,6 @@
|
|||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:label="@string/app_name"
|
|
||||||
android:theme="@style/Theme.VBHelper">
|
android:theme="@style/Theme.VBHelper">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
@@ -51,8 +51,10 @@
|
|||||||
<action android:name="android.intent.action.VIEW" />
|
<action android:name="android.intent.action.VIEW" />
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
<category android:name="android.intent.category.BROWSABLE" />
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
<data android:scheme="http" android:host="localhost" android:port="8080" android:pathPrefix="/authenticate" />
|
<data android:scheme="http" />
|
||||||
<data android:scheme="http" android:host="127.0.0.1" android:port="8080" android:pathPrefix="/authenticate" />
|
<data android:host="localhost" android:port="8080" />
|
||||||
|
<data android:host="127.0.0.1" android:port="8080" />
|
||||||
|
<data android:pathPrefix="/authenticate" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.SEND" />
|
<action android:name="android.intent.action.SEND" />
|
||||||
|
|||||||
@@ -49,7 +49,8 @@ class MainActivity : ComponentActivity() {
|
|||||||
application.container.dataStoreSecretsRepository.secretsFlow,
|
application.container.dataStoreSecretsRepository.secretsFlow,
|
||||||
this,
|
this,
|
||||||
this::registerActivityLifecycleListener,
|
this::registerActivityLifecycleListener,
|
||||||
this::unregisterActivityLifecycleListener
|
this::unregisterActivityLifecycleListener,
|
||||||
|
application.container.db,
|
||||||
)
|
)
|
||||||
val settingsScreenController = SettingsScreenControllerImpl(this)
|
val settingsScreenController = SettingsScreenControllerImpl(this)
|
||||||
val itemsScreenController = ItemsScreenControllerImpl(this)
|
val itemsScreenController = ItemsScreenControllerImpl(this)
|
||||||
|
|||||||
@@ -2,11 +2,13 @@ package com.github.nacabaro.vbhelper.daos
|
|||||||
|
|
||||||
import androidx.room.Dao
|
import androidx.room.Dao
|
||||||
import androidx.room.Query
|
import androidx.room.Query
|
||||||
|
import androidx.room.RewriteQueriesToDropUnusedColumns
|
||||||
import com.github.nacabaro.vbhelper.dtos.CharacterDtos
|
import com.github.nacabaro.vbhelper.dtos.CharacterDtos
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
|
|
||||||
@Dao
|
@Dao
|
||||||
|
@RewriteQueriesToDropUnusedColumns
|
||||||
interface AdventureDao {
|
interface AdventureDao {
|
||||||
@Query("""
|
@Query("""
|
||||||
INSERT INTO Adventure (characterId, originalDuration, finishesAdventure)
|
INSERT INTO Adventure (characterId, originalDuration, finishesAdventure)
|
||||||
|
|||||||
@@ -13,6 +13,9 @@ interface CardDao {
|
|||||||
@Insert(onConflict = OnConflictStrategy.IGNORE)
|
@Insert(onConflict = OnConflictStrategy.IGNORE)
|
||||||
suspend fun insertNewCard(card: Card): Long
|
suspend fun insertNewCard(card: Card): Long
|
||||||
|
|
||||||
|
@Query("SELECT * FROM Card")
|
||||||
|
fun getAllCards(): List<Card>
|
||||||
|
|
||||||
@Query("SELECT * FROM Card WHERE cardId = :id")
|
@Query("SELECT * FROM Card WHERE cardId = :id")
|
||||||
fun getCardByCardId(id: Int): List<Card>
|
fun getCardByCardId(id: Int): List<Card>
|
||||||
|
|
||||||
|
|||||||
@@ -2,11 +2,13 @@ package com.github.nacabaro.vbhelper.daos
|
|||||||
|
|
||||||
import androidx.room.Dao
|
import androidx.room.Dao
|
||||||
import androidx.room.Query
|
import androidx.room.Query
|
||||||
|
import androidx.room.RewriteQueriesToDropUnusedColumns
|
||||||
import com.github.cfogrady.vbnfc.data.NfcCharacter
|
import com.github.cfogrady.vbnfc.data.NfcCharacter
|
||||||
import com.github.nacabaro.vbhelper.dtos.CharacterDtos
|
import com.github.nacabaro.vbhelper.dtos.CharacterDtos
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
@Dao
|
@Dao
|
||||||
|
@RewriteQueriesToDropUnusedColumns
|
||||||
interface CardFusionsDao {
|
interface CardFusionsDao {
|
||||||
@Query("""
|
@Query("""
|
||||||
INSERT INTO
|
INSERT INTO
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import androidx.room.Insert
|
|||||||
import androidx.room.OnConflictStrategy
|
import androidx.room.OnConflictStrategy
|
||||||
import androidx.room.Query
|
import androidx.room.Query
|
||||||
import androidx.room.Upsert
|
import androidx.room.Upsert
|
||||||
|
import androidx.room.RewriteQueriesToDropUnusedColumns
|
||||||
import com.github.nacabaro.vbhelper.domain.card.CardCharacter
|
import com.github.nacabaro.vbhelper.domain.card.CardCharacter
|
||||||
import com.github.nacabaro.vbhelper.domain.device_data.UserCharacter
|
import com.github.nacabaro.vbhelper.domain.device_data.UserCharacter
|
||||||
import com.github.nacabaro.vbhelper.domain.device_data.BECharacterData
|
import com.github.nacabaro.vbhelper.domain.device_data.BECharacterData
|
||||||
@@ -16,6 +17,7 @@ import com.github.nacabaro.vbhelper.dtos.CharacterDtos
|
|||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
@Dao
|
@Dao
|
||||||
|
@RewriteQueriesToDropUnusedColumns
|
||||||
interface UserCharacterDao {
|
interface UserCharacterDao {
|
||||||
@Insert
|
@Insert
|
||||||
fun insertCharacterData(characterData: UserCharacter): Long
|
fun insertCharacterData(characterData: UserCharacter): Long
|
||||||
@@ -131,6 +133,9 @@ interface UserCharacterDao {
|
|||||||
@Query("SELECT * FROM VBCharacterData WHERE id = :id")
|
@Query("SELECT * FROM VBCharacterData WHERE id = :id")
|
||||||
fun getVbData(id: Long): Flow<VBCharacterData>
|
fun getVbData(id: Long): Flow<VBCharacterData>
|
||||||
|
|
||||||
|
@Query("SELECT * FROM VBCharacterData WHERE id = :id")
|
||||||
|
suspend fun getVbDataOrNull(id: Long): VBCharacterData?
|
||||||
|
|
||||||
@Query("SELECT * FROM SpecialMissions WHERE characterId = :id")
|
@Query("SELECT * FROM SpecialMissions WHERE characterId = :id")
|
||||||
fun getSpecialMissions(id: Long): Flow<List<SpecialMissions>>
|
fun getSpecialMissions(id: Long): Flow<List<SpecialMissions>>
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ package com.github.nacabaro.vbhelper.database
|
|||||||
|
|
||||||
import androidx.room.Database
|
import androidx.room.Database
|
||||||
import androidx.room.RoomDatabase
|
import androidx.room.RoomDatabase
|
||||||
|
import androidx.room.migration.Migration
|
||||||
|
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||||
import com.github.nacabaro.vbhelper.daos.AdventureDao
|
import com.github.nacabaro.vbhelper.daos.AdventureDao
|
||||||
import com.github.nacabaro.vbhelper.daos.CardAdventureDao
|
import com.github.nacabaro.vbhelper.daos.CardAdventureDao
|
||||||
import com.github.nacabaro.vbhelper.daos.CharacterDao
|
import com.github.nacabaro.vbhelper.daos.CharacterDao
|
||||||
@@ -13,6 +15,10 @@ import com.github.nacabaro.vbhelper.daos.ItemDao
|
|||||||
import com.github.nacabaro.vbhelper.daos.SpecialMissionDao
|
import com.github.nacabaro.vbhelper.daos.SpecialMissionDao
|
||||||
import com.github.nacabaro.vbhelper.daos.SpriteDao
|
import com.github.nacabaro.vbhelper.daos.SpriteDao
|
||||||
import com.github.nacabaro.vbhelper.daos.UserCharacterDao
|
import com.github.nacabaro.vbhelper.daos.UserCharacterDao
|
||||||
|
import com.github.nacabaro.vbhelper.daos.VitalWearSettingsDao
|
||||||
|
import com.github.nacabaro.vbhelper.daos.CharacterTransferPolicyDao
|
||||||
|
import com.github.nacabaro.vbhelper.companion.validation.ValidatedCardDao
|
||||||
|
import com.github.nacabaro.vbhelper.companion.validation.ValidatedCardEntity
|
||||||
import com.github.nacabaro.vbhelper.domain.card.Background
|
import com.github.nacabaro.vbhelper.domain.card.Background
|
||||||
import com.github.nacabaro.vbhelper.domain.card.CardCharacter
|
import com.github.nacabaro.vbhelper.domain.card.CardCharacter
|
||||||
import com.github.nacabaro.vbhelper.domain.card.Card
|
import com.github.nacabaro.vbhelper.domain.card.Card
|
||||||
@@ -29,10 +35,13 @@ import com.github.nacabaro.vbhelper.domain.device_data.TransformationHistory
|
|||||||
import com.github.nacabaro.vbhelper.domain.device_data.UserCharacter
|
import com.github.nacabaro.vbhelper.domain.device_data.UserCharacter
|
||||||
import com.github.nacabaro.vbhelper.domain.device_data.VBCharacterData
|
import com.github.nacabaro.vbhelper.domain.device_data.VBCharacterData
|
||||||
import com.github.nacabaro.vbhelper.domain.device_data.VitalsHistory
|
import com.github.nacabaro.vbhelper.domain.device_data.VitalsHistory
|
||||||
|
import com.github.nacabaro.vbhelper.domain.device_data.VitalWearCharacterSettings
|
||||||
|
import com.github.nacabaro.vbhelper.domain.device_data.CharacterTransferPolicy
|
||||||
import com.github.nacabaro.vbhelper.domain.items.Items
|
import com.github.nacabaro.vbhelper.domain.items.Items
|
||||||
|
|
||||||
@Database(
|
@Database(
|
||||||
version = 1,
|
version = 3,
|
||||||
|
exportSchema = false,
|
||||||
entities = [
|
entities = [
|
||||||
Card::class,
|
Card::class,
|
||||||
CardProgress::class,
|
CardProgress::class,
|
||||||
@@ -50,7 +59,10 @@ import com.github.nacabaro.vbhelper.domain.items.Items
|
|||||||
Items::class,
|
Items::class,
|
||||||
Adventure::class,
|
Adventure::class,
|
||||||
Background::class,
|
Background::class,
|
||||||
PossibleTransformations::class
|
PossibleTransformations::class,
|
||||||
|
ValidatedCardEntity::class,
|
||||||
|
VitalWearCharacterSettings::class,
|
||||||
|
CharacterTransferPolicy::class
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
abstract class AppDatabase : RoomDatabase() {
|
abstract class AppDatabase : RoomDatabase() {
|
||||||
@@ -65,4 +77,35 @@ abstract class AppDatabase : RoomDatabase() {
|
|||||||
abstract fun specialMissionDao(): SpecialMissionDao
|
abstract fun specialMissionDao(): SpecialMissionDao
|
||||||
abstract fun cardAdventureDao(): CardAdventureDao
|
abstract fun cardAdventureDao(): CardAdventureDao
|
||||||
abstract fun cardFusionsDao(): CardFusionsDao
|
abstract fun cardFusionsDao(): CardFusionsDao
|
||||||
|
abstract fun validatedCardDao(): ValidatedCardDao
|
||||||
|
abstract fun vitalWearSettingsDao(): VitalWearSettingsDao
|
||||||
|
abstract fun characterTransferPolicyDao(): CharacterTransferPolicyDao
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val MIGRATION_1_2 = object : Migration(1, 2) {
|
||||||
|
override fun migrate(db: SupportSQLiteDatabase) {
|
||||||
|
// Create new tables
|
||||||
|
db.execSQL("CREATE TABLE IF NOT EXISTS `Items` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `description` TEXT NOT NULL, `itemIcon` INTEGER NOT NULL, `itemLength` INTEGER NOT NULL, `price` INTEGER NOT NULL, `quantity` INTEGER NOT NULL, `itemType` TEXT NOT NULL, PRIMARY KEY(`id`))")
|
||||||
|
db.execSQL("CREATE TABLE IF NOT EXISTS `SpecialMissions` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `characterId` INTEGER NOT NULL, `goal` INTEGER NOT NULL, `watchId` INTEGER NOT NULL, `progress` INTEGER NOT NULL, `status` TEXT NOT NULL, `timeElapsedInMinutes` INTEGER NOT NULL, `timeLimitInMinutes` INTEGER NOT NULL, `missionType` TEXT NOT NULL, FOREIGN KEY(`characterId`) REFERENCES `UserCharacter`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )")
|
||||||
|
db.execSQL("CREATE TABLE IF NOT EXISTS `VBCharacterData` (`id` INTEGER NOT NULL, `generation` INTEGER NOT NULL, `totalTrophies` INTEGER NOT NULL, PRIMARY KEY(`id`), FOREIGN KEY(`id`) REFERENCES `UserCharacter`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )")
|
||||||
|
db.execSQL("CREATE TABLE IF NOT EXISTS `TransformationHistory` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `monId` INTEGER NOT NULL, `stageId` INTEGER NOT NULL, `transformationDate` INTEGER NOT NULL, FOREIGN KEY(`monId`) REFERENCES `UserCharacter`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`stageId`) REFERENCES `CardCharacter`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )")
|
||||||
|
db.execSQL("CREATE TABLE IF NOT EXISTS `VitalsHistory` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `charId` INTEGER NOT NULL, `year` INTEGER NOT NULL, `month` INTEGER NOT NULL, `day` INTEGER NOT NULL, `vitalPoints` INTEGER NOT NULL, FOREIGN KEY(`charId`) REFERENCES `UserCharacter`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )")
|
||||||
|
db.execSQL("CREATE TABLE IF NOT EXISTS `Dex` (`id` INTEGER NOT NULL, `discoveredOn` INTEGER NOT NULL, PRIMARY KEY(`id`), FOREIGN KEY(`id`) REFERENCES `CardCharacter`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )")
|
||||||
|
db.execSQL("CREATE TABLE IF NOT EXISTS `Adventure` (`characterId` INTEGER NOT NULL, `originalDuration` INTEGER NOT NULL, `finishesAdventure` INTEGER NOT NULL, PRIMARY KEY(`characterId`), FOREIGN KEY(`characterId`) REFERENCES `UserCharacter`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )")
|
||||||
|
db.execSQL("CREATE TABLE IF NOT EXISTS `Background` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `cardId` INTEGER NOT NULL, `background` BLOB NOT NULL, `backgroundWidth` INTEGER NOT NULL, `backgroundHeight` INTEGER NOT NULL, FOREIGN KEY(`cardId`) REFERENCES `Card`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )")
|
||||||
|
db.execSQL("CREATE TABLE IF NOT EXISTS `PossibleTransformations` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `charaId` INTEGER NOT NULL, `requiredVitals` INTEGER NOT NULL, `requiredTrophies` INTEGER NOT NULL, `requiredBattles` INTEGER NOT NULL, `requiredWinRate` INTEGER NOT NULL, `changeTimerHours` INTEGER NOT NULL, `requiredAdventureLevelCompleted` INTEGER NOT NULL, `toCharaId` INTEGER, FOREIGN KEY(`charaId`) REFERENCES `CardCharacter`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`toCharaId`) REFERENCES `CardCharacter`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )")
|
||||||
|
db.execSQL("CREATE TABLE IF NOT EXISTS `validated` (`cardId` INTEGER NOT NULL, PRIMARY KEY(`cardId`))")
|
||||||
|
db.execSQL("CREATE TABLE IF NOT EXISTS `VitalWearCharacterSettings` (`characterId` INTEGER NOT NULL, `trainingInBackground` INTEGER NOT NULL, `allowedBattles` INTEGER NOT NULL, `accumulatedDailyInjuries` INTEGER NOT NULL, PRIMARY KEY(`characterId`))")
|
||||||
|
db.execSQL("CREATE TABLE IF NOT EXISTS `CharacterTransferPolicy` (`characterId` INTEGER NOT NULL, `nativeDeviceType` TEXT NOT NULL, `preferredHceExportFormat` TEXT NOT NULL, `preferredNfcaExportFormat` TEXT NOT NULL, `lastObservedImportFormat` TEXT, `lastTransferTransport` TEXT, `lastTransferTarget` TEXT, `preserveVbRoundTrip` INTEGER NOT NULL, `preserveBeRoundTrip` INTEGER NOT NULL, PRIMARY KEY(`characterId`), FOREIGN KEY(`characterId`) REFERENCES `UserCharacter`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )")
|
||||||
|
|
||||||
|
// Add columns to existing tables
|
||||||
|
try {
|
||||||
|
db.execSQL("ALTER TABLE `UserCharacter` ADD COLUMN `characterType` TEXT NOT NULL DEFAULT 'BEDevice'")
|
||||||
|
} catch (e: Exception) {}
|
||||||
|
try {
|
||||||
|
db.execSQL("ALTER TABLE `Card` ADD COLUMN `isBEm` INTEGER NOT NULL DEFAULT 0")
|
||||||
|
} catch (e: Exception) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -3,9 +3,13 @@ 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.CurrencyRepository
|
||||||
import com.github.nacabaro.vbhelper.source.DataStoreSecretsRepository
|
import com.github.nacabaro.vbhelper.source.DataStoreSecretsRepository
|
||||||
|
import com.github.nacabaro.vbhelper.companion.validation.ValidatedCardManager
|
||||||
|
import com.github.nacabaro.vbhelper.companion.logs.CompanionLogService
|
||||||
|
|
||||||
interface AppContainer {
|
interface AppContainer {
|
||||||
val db: AppDatabase
|
val db: AppDatabase
|
||||||
val dataStoreSecretsRepository: DataStoreSecretsRepository
|
val dataStoreSecretsRepository: DataStoreSecretsRepository
|
||||||
val currencyRepository: CurrencyRepository
|
val currencyRepository: CurrencyRepository
|
||||||
|
val validatedCardManager: ValidatedCardManager
|
||||||
|
val companionLogService: CompanionLogService
|
||||||
}
|
}
|
||||||
@@ -8,6 +8,8 @@ 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.CurrencyRepository
|
||||||
import com.github.nacabaro.vbhelper.source.DataStoreSecretsRepository
|
import com.github.nacabaro.vbhelper.source.DataStoreSecretsRepository
|
||||||
|
import com.github.nacabaro.vbhelper.companion.validation.ValidatedCardManager
|
||||||
|
import com.github.nacabaro.vbhelper.companion.logs.CompanionLogService
|
||||||
import com.github.nacabaro.vbhelper.source.SecretsSerializer
|
import com.github.nacabaro.vbhelper.source.SecretsSerializer
|
||||||
import com.github.nacabaro.vbhelper.source.proto.Secrets
|
import com.github.nacabaro.vbhelper.source.proto.Secrets
|
||||||
|
|
||||||
@@ -30,6 +32,7 @@ class DefaultAppContainer(private val context: Context) : AppContainer {
|
|||||||
klass = AppDatabase::class.java,
|
klass = AppDatabase::class.java,
|
||||||
"internalDb"
|
"internalDb"
|
||||||
)
|
)
|
||||||
|
.addMigrations(AppDatabase.MIGRATION_1_2)
|
||||||
.createFromAsset("items.db")
|
.createFromAsset("items.db")
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
@@ -37,5 +40,11 @@ class DefaultAppContainer(private val context: Context) : AppContainer {
|
|||||||
override val dataStoreSecretsRepository = DataStoreSecretsRepository(context.secretsStore)
|
override val dataStoreSecretsRepository = DataStoreSecretsRepository(context.secretsStore)
|
||||||
|
|
||||||
override val currencyRepository = CurrencyRepository(context.currencyStore)
|
override val currencyRepository = CurrencyRepository(context.currencyStore)
|
||||||
|
|
||||||
|
override val validatedCardManager by lazy {
|
||||||
|
ValidatedCardManager(db.validatedCardDao())
|
||||||
|
}
|
||||||
|
|
||||||
|
override val companionLogService = CompanionLogService()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,10 +2,18 @@ package com.github.nacabaro.vbhelper.di
|
|||||||
|
|
||||||
import DefaultAppContainer
|
import DefaultAppContainer
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
|
import com.github.nacabaro.vbhelper.companion.validation.ValidatedCardManager
|
||||||
|
import com.github.nacabaro.vbhelper.companion.logs.CompanionLogService
|
||||||
|
|
||||||
class VBHelper : Application() {
|
class VBHelper : Application() {
|
||||||
lateinit var container: DefaultAppContainer
|
lateinit var container: DefaultAppContainer
|
||||||
|
|
||||||
|
val validatedCardManager: ValidatedCardManager
|
||||||
|
get() = container.validatedCardManager
|
||||||
|
|
||||||
|
val companionLogService: CompanionLogService
|
||||||
|
get() = container.companionLogService
|
||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
container = DefaultAppContainer(applicationContext)
|
container = DefaultAppContainer(applicationContext)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.github.nacabaro.vbhelper.domain.card
|
package com.github.nacabaro.vbhelper.domain.card
|
||||||
|
|
||||||
import androidx.room.Entity
|
import androidx.room.Entity
|
||||||
|
import androidx.room.Index
|
||||||
import androidx.room.ForeignKey
|
import androidx.room.ForeignKey
|
||||||
import androidx.room.PrimaryKey
|
import androidx.room.PrimaryKey
|
||||||
|
|
||||||
@@ -12,7 +13,8 @@ import androidx.room.PrimaryKey
|
|||||||
childColumns = ["cardId"],
|
childColumns = ["cardId"],
|
||||||
onDelete = ForeignKey.CASCADE
|
onDelete = ForeignKey.CASCADE
|
||||||
)
|
)
|
||||||
]
|
],
|
||||||
|
indices = [Index(value = ["cardId"])]
|
||||||
)
|
)
|
||||||
data class Background (
|
data class Background (
|
||||||
@PrimaryKey(autoGenerate = true) val id: Long,
|
@PrimaryKey(autoGenerate = true) val id: Long,
|
||||||
@@ -20,4 +22,28 @@ data class Background (
|
|||||||
val background: ByteArray,
|
val background: ByteArray,
|
||||||
val backgroundWidth: Int,
|
val backgroundWidth: Int,
|
||||||
val backgroundHeight: Int
|
val backgroundHeight: Int
|
||||||
)
|
) {
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (this === other) return true
|
||||||
|
if (javaClass != other?.javaClass) return false
|
||||||
|
|
||||||
|
other as Background
|
||||||
|
|
||||||
|
if (id != other.id) return false
|
||||||
|
if (cardId != other.cardId) return false
|
||||||
|
if (!background.contentEquals(other.background)) return false
|
||||||
|
if (backgroundWidth != other.backgroundWidth) return false
|
||||||
|
if (backgroundHeight != other.backgroundHeight) return false
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
var result = id.hashCode()
|
||||||
|
result = 31 * result + cardId.hashCode()
|
||||||
|
result = 31 * result + background.contentHashCode()
|
||||||
|
result = 31 * result + backgroundWidth
|
||||||
|
result = 31 * result + backgroundHeight
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -14,4 +14,34 @@ data class Card(
|
|||||||
val name: String,
|
val name: String,
|
||||||
val stageCount: Int,
|
val stageCount: Int,
|
||||||
val isBEm: Boolean
|
val isBEm: Boolean
|
||||||
)
|
) {
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (this === other) return true
|
||||||
|
if (javaClass != other?.javaClass) return false
|
||||||
|
|
||||||
|
other as Card
|
||||||
|
|
||||||
|
if (id != other.id) return false
|
||||||
|
if (cardId != other.cardId) return false
|
||||||
|
if (!logo.contentEquals(other.logo)) return false
|
||||||
|
if (logoWidth != other.logoWidth) return false
|
||||||
|
if (logoHeight != other.logoHeight) return false
|
||||||
|
if (name != other.name) return false
|
||||||
|
if (stageCount != other.stageCount) return false
|
||||||
|
if (isBEm != other.isBEm) return false
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
var result = id.hashCode()
|
||||||
|
result = 31 * result + cardId
|
||||||
|
result = 31 * result + logo.contentHashCode()
|
||||||
|
result = 31 * result + logoWidth
|
||||||
|
result = 31 * result + logoHeight
|
||||||
|
result = 31 * result + name.hashCode()
|
||||||
|
result = 31 * result + stageCount
|
||||||
|
result = 31 * result + isBEm.hashCode()
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.github.nacabaro.vbhelper.domain.card
|
package com.github.nacabaro.vbhelper.domain.card
|
||||||
|
|
||||||
import androidx.room.Entity
|
import androidx.room.Entity
|
||||||
|
import androidx.room.Index
|
||||||
import androidx.room.ForeignKey
|
import androidx.room.ForeignKey
|
||||||
import androidx.room.PrimaryKey
|
import androidx.room.PrimaryKey
|
||||||
|
|
||||||
@@ -18,6 +19,10 @@ import androidx.room.PrimaryKey
|
|||||||
childColumns = ["cardId"],
|
childColumns = ["cardId"],
|
||||||
onDelete = ForeignKey.CASCADE
|
onDelete = ForeignKey.CASCADE
|
||||||
)
|
)
|
||||||
|
],
|
||||||
|
indices = [
|
||||||
|
Index(value = ["characterId"]),
|
||||||
|
Index(value = ["cardId"])
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
data class CardAdventure(
|
data class CardAdventure(
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.github.nacabaro.vbhelper.domain.card
|
package com.github.nacabaro.vbhelper.domain.card
|
||||||
|
|
||||||
import androidx.room.Entity
|
import androidx.room.Entity
|
||||||
|
import androidx.room.Index
|
||||||
import androidx.room.ForeignKey
|
import androidx.room.ForeignKey
|
||||||
import androidx.room.PrimaryKey
|
import androidx.room.PrimaryKey
|
||||||
import com.github.cfogrady.vbnfc.data.NfcCharacter
|
import com.github.cfogrady.vbnfc.data.NfcCharacter
|
||||||
@@ -20,6 +21,10 @@ import com.github.nacabaro.vbhelper.domain.characters.Sprite
|
|||||||
childColumns = ["spriteId"],
|
childColumns = ["spriteId"],
|
||||||
onDelete = ForeignKey.CASCADE
|
onDelete = ForeignKey.CASCADE
|
||||||
)
|
)
|
||||||
|
],
|
||||||
|
indices = [
|
||||||
|
Index(value = ["cardId"]),
|
||||||
|
Index(value = ["spriteId"])
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -41,4 +46,42 @@ data class CardCharacter (
|
|||||||
val nameSprite: ByteArray,
|
val nameSprite: ByteArray,
|
||||||
val nameWidth: Int,
|
val nameWidth: Int,
|
||||||
val nameHeight: Int
|
val nameHeight: Int
|
||||||
)
|
) {
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (this === other) return true
|
||||||
|
if (javaClass != other?.javaClass) return false
|
||||||
|
|
||||||
|
other as CardCharacter
|
||||||
|
|
||||||
|
if (id != other.id) return false
|
||||||
|
if (cardId != other.cardId) return false
|
||||||
|
if (spriteId != other.spriteId) return false
|
||||||
|
if (charaIndex != other.charaIndex) return false
|
||||||
|
if (stage != other.stage) return false
|
||||||
|
if (attribute != other.attribute) return false
|
||||||
|
if (baseHp != other.baseHp) return false
|
||||||
|
if (baseBp != other.baseBp) return false
|
||||||
|
if (baseAp != other.baseAp) return false
|
||||||
|
if (!nameSprite.contentEquals(other.nameSprite)) return false
|
||||||
|
if (nameWidth != other.nameWidth) return false
|
||||||
|
if (nameHeight != other.nameHeight) return false
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
var result = id.hashCode()
|
||||||
|
result = 31 * result + cardId.hashCode()
|
||||||
|
result = 31 * result + spriteId.hashCode()
|
||||||
|
result = 31 * result + charaIndex
|
||||||
|
result = 31 * result + stage
|
||||||
|
result = 31 * result + attribute.hashCode()
|
||||||
|
result = 31 * result + baseHp
|
||||||
|
result = 31 * result + baseBp
|
||||||
|
result = 31 * result + baseAp
|
||||||
|
result = 31 * result + nameSprite.contentHashCode()
|
||||||
|
result = 31 * result + nameWidth
|
||||||
|
result = 31 * result + nameHeight
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.github.nacabaro.vbhelper.domain.card
|
package com.github.nacabaro.vbhelper.domain.card
|
||||||
|
|
||||||
import androidx.room.Entity
|
import androidx.room.Entity
|
||||||
|
import androidx.room.Index
|
||||||
import androidx.room.ForeignKey
|
import androidx.room.ForeignKey
|
||||||
import androidx.room.PrimaryKey
|
import androidx.room.PrimaryKey
|
||||||
import com.github.cfogrady.vbnfc.data.NfcCharacter
|
import com.github.cfogrady.vbnfc.data.NfcCharacter
|
||||||
@@ -19,6 +20,10 @@ import com.github.cfogrady.vbnfc.data.NfcCharacter
|
|||||||
childColumns = ["toCharaId"],
|
childColumns = ["toCharaId"],
|
||||||
onDelete = ForeignKey.CASCADE
|
onDelete = ForeignKey.CASCADE
|
||||||
)
|
)
|
||||||
|
],
|
||||||
|
indices = [
|
||||||
|
Index(value = ["fromCharaId"]),
|
||||||
|
Index(value = ["toCharaId"])
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
data class CardFusions(
|
data class CardFusions(
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.github.nacabaro.vbhelper.domain.card
|
package com.github.nacabaro.vbhelper.domain.card
|
||||||
|
|
||||||
import androidx.room.Entity
|
import androidx.room.Entity
|
||||||
|
import androidx.room.Index
|
||||||
import androidx.room.ForeignKey
|
import androidx.room.ForeignKey
|
||||||
import androidx.room.PrimaryKey
|
import androidx.room.PrimaryKey
|
||||||
|
|
||||||
@@ -18,6 +19,10 @@ import androidx.room.PrimaryKey
|
|||||||
childColumns = ["toCharaId"],
|
childColumns = ["toCharaId"],
|
||||||
onDelete = ForeignKey.CASCADE
|
onDelete = ForeignKey.CASCADE
|
||||||
)
|
)
|
||||||
|
],
|
||||||
|
indices = [
|
||||||
|
Index(value = ["charaId"]),
|
||||||
|
Index(value = ["toCharaId"])
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
data class PossibleTransformations (
|
data class PossibleTransformations (
|
||||||
|
|||||||
@@ -20,4 +20,48 @@ data class Sprite(
|
|||||||
val spriteDodge: ByteArray,
|
val spriteDodge: ByteArray,
|
||||||
val width: Int,
|
val width: Int,
|
||||||
val height: Int
|
val height: Int
|
||||||
)
|
) {
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (this === other) return true
|
||||||
|
if (javaClass != other?.javaClass) return false
|
||||||
|
|
||||||
|
other as Sprite
|
||||||
|
|
||||||
|
if (id != other.id) return false
|
||||||
|
if (!spriteIdle1.contentEquals(other.spriteIdle1)) return false
|
||||||
|
if (!spriteIdle2.contentEquals(other.spriteIdle2)) return false
|
||||||
|
if (!spriteWalk1.contentEquals(other.spriteWalk1)) return false
|
||||||
|
if (!spriteWalk2.contentEquals(other.spriteWalk2)) return false
|
||||||
|
if (!spriteRun1.contentEquals(other.spriteRun1)) return false
|
||||||
|
if (!spriteRun2.contentEquals(other.spriteRun2)) return false
|
||||||
|
if (!spriteTrain1.contentEquals(other.spriteTrain1)) return false
|
||||||
|
if (!spriteTrain2.contentEquals(other.spriteTrain2)) return false
|
||||||
|
if (!spriteHappy.contentEquals(other.spriteHappy)) return false
|
||||||
|
if (!spriteSleep.contentEquals(other.spriteSleep)) return false
|
||||||
|
if (!spriteAttack.contentEquals(other.spriteAttack)) return false
|
||||||
|
if (!spriteDodge.contentEquals(other.spriteDodge)) return false
|
||||||
|
if (width != other.width) return false
|
||||||
|
if (height != other.height) return false
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
var result = id
|
||||||
|
result = 31 * result + spriteIdle1.contentHashCode()
|
||||||
|
result = 31 * result + spriteIdle2.contentHashCode()
|
||||||
|
result = 31 * result + spriteWalk1.contentHashCode()
|
||||||
|
result = 31 * result + spriteWalk2.contentHashCode()
|
||||||
|
result = 31 * result + spriteRun1.contentHashCode()
|
||||||
|
result = 31 * result + spriteRun2.contentHashCode()
|
||||||
|
result = 31 * result + spriteTrain1.contentHashCode()
|
||||||
|
result = 31 * result + spriteTrain2.contentHashCode()
|
||||||
|
result = 31 * result + spriteHappy.contentHashCode()
|
||||||
|
result = 31 * result + spriteSleep.contentHashCode()
|
||||||
|
result = 31 * result + spriteAttack.contentHashCode()
|
||||||
|
result = 31 * result + spriteDodge.contentHashCode()
|
||||||
|
result = 31 * result + width
|
||||||
|
result = 31 * result + height
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+3
-1
@@ -1,6 +1,7 @@
|
|||||||
package com.github.nacabaro.vbhelper.domain.device_data
|
package com.github.nacabaro.vbhelper.domain.device_data
|
||||||
|
|
||||||
import androidx.room.Entity
|
import androidx.room.Entity
|
||||||
|
import androidx.room.Index
|
||||||
import androidx.room.ForeignKey
|
import androidx.room.ForeignKey
|
||||||
import androidx.room.PrimaryKey
|
import androidx.room.PrimaryKey
|
||||||
import com.github.cfogrady.vbnfc.vb.SpecialMission
|
import com.github.cfogrady.vbnfc.vb.SpecialMission
|
||||||
@@ -13,7 +14,8 @@ import com.github.cfogrady.vbnfc.vb.SpecialMission
|
|||||||
childColumns = ["characterId"],
|
childColumns = ["characterId"],
|
||||||
onDelete = ForeignKey.CASCADE
|
onDelete = ForeignKey.CASCADE
|
||||||
)
|
)
|
||||||
]
|
],
|
||||||
|
indices = [Index(value = ["characterId"])]
|
||||||
)
|
)
|
||||||
data class SpecialMissions (
|
data class SpecialMissions (
|
||||||
@PrimaryKey(autoGenerate = true) var id: Long = 0,
|
@PrimaryKey(autoGenerate = true) var id: Long = 0,
|
||||||
|
|||||||
+5
@@ -1,6 +1,7 @@
|
|||||||
package com.github.nacabaro.vbhelper.domain.device_data
|
package com.github.nacabaro.vbhelper.domain.device_data
|
||||||
|
|
||||||
import androidx.room.Entity
|
import androidx.room.Entity
|
||||||
|
import androidx.room.Index
|
||||||
import androidx.room.ForeignKey
|
import androidx.room.ForeignKey
|
||||||
import androidx.room.PrimaryKey
|
import androidx.room.PrimaryKey
|
||||||
import com.github.nacabaro.vbhelper.domain.card.CardCharacter
|
import com.github.nacabaro.vbhelper.domain.card.CardCharacter
|
||||||
@@ -19,6 +20,10 @@ import com.github.nacabaro.vbhelper.domain.card.CardCharacter
|
|||||||
childColumns = ["stageId"],
|
childColumns = ["stageId"],
|
||||||
onDelete = ForeignKey.CASCADE
|
onDelete = ForeignKey.CASCADE
|
||||||
)
|
)
|
||||||
|
],
|
||||||
|
indices = [
|
||||||
|
Index(value = ["monId"]),
|
||||||
|
Index(value = ["stageId"])
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
data class TransformationHistory (
|
data class TransformationHistory (
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.github.nacabaro.vbhelper.domain.device_data
|
package com.github.nacabaro.vbhelper.domain.device_data
|
||||||
|
|
||||||
import androidx.room.Entity
|
import androidx.room.Entity
|
||||||
|
import androidx.room.Index
|
||||||
import androidx.room.ForeignKey
|
import androidx.room.ForeignKey
|
||||||
import androidx.room.PrimaryKey
|
import androidx.room.PrimaryKey
|
||||||
import com.github.cfogrady.vbnfc.data.NfcCharacter
|
import com.github.cfogrady.vbnfc.data.NfcCharacter
|
||||||
@@ -15,7 +16,8 @@ import com.github.nacabaro.vbhelper.domain.card.CardCharacter
|
|||||||
childColumns = ["charId"],
|
childColumns = ["charId"],
|
||||||
onDelete = ForeignKey.CASCADE
|
onDelete = ForeignKey.CASCADE
|
||||||
)
|
)
|
||||||
]
|
],
|
||||||
|
indices = [Index(value = ["charId"])]
|
||||||
)
|
)
|
||||||
/**
|
/**
|
||||||
* UserCharacter represents and instance of a character. The charId should map to a Character
|
* UserCharacter represents and instance of a character. The charId should map to a Character
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.github.nacabaro.vbhelper.domain.device_data
|
package com.github.nacabaro.vbhelper.domain.device_data
|
||||||
|
|
||||||
import androidx.room.Entity
|
import androidx.room.Entity
|
||||||
|
import androidx.room.Index
|
||||||
import androidx.room.ForeignKey
|
import androidx.room.ForeignKey
|
||||||
import androidx.room.PrimaryKey
|
import androidx.room.PrimaryKey
|
||||||
|
|
||||||
@@ -12,7 +13,8 @@ import androidx.room.PrimaryKey
|
|||||||
childColumns = ["charId"],
|
childColumns = ["charId"],
|
||||||
onDelete = ForeignKey.CASCADE
|
onDelete = ForeignKey.CASCADE
|
||||||
)
|
)
|
||||||
]
|
],
|
||||||
|
indices = [Index(value = ["charId"])]
|
||||||
)
|
)
|
||||||
data class VitalsHistory (
|
data class VitalsHistory (
|
||||||
@PrimaryKey(autoGenerate = true) val id: Long = 0,
|
@PrimaryKey(autoGenerate = true) val id: Long = 0,
|
||||||
|
|||||||
@@ -9,7 +9,35 @@ object CardDtos {
|
|||||||
val logoHeight: Int,
|
val logoHeight: Int,
|
||||||
val totalCharacters: Int,
|
val totalCharacters: Int,
|
||||||
val obtainedCharacters: Int,
|
val obtainedCharacters: Int,
|
||||||
)
|
) {
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (this === other) return true
|
||||||
|
if (javaClass != other?.javaClass) return false
|
||||||
|
|
||||||
|
other as CardProgress
|
||||||
|
|
||||||
|
if (cardId != other.cardId) return false
|
||||||
|
if (cardName != other.cardName) return false
|
||||||
|
if (!cardLogo.contentEquals(other.cardLogo)) return false
|
||||||
|
if (logoWidth != other.logoWidth) return false
|
||||||
|
if (logoHeight != other.logoHeight) return false
|
||||||
|
if (totalCharacters != other.totalCharacters) return false
|
||||||
|
if (obtainedCharacters != other.obtainedCharacters) return false
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
var result = cardId.hashCode()
|
||||||
|
result = 31 * result + cardName.hashCode()
|
||||||
|
result = 31 * result + cardLogo.contentHashCode()
|
||||||
|
result = 31 * result + logoWidth
|
||||||
|
result = 31 * result + logoHeight
|
||||||
|
result = 31 * result + totalCharacters
|
||||||
|
result = 31 * result + obtainedCharacters
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
data class CardAdventureWithSprites (
|
data class CardAdventureWithSprites (
|
||||||
val characterName: ByteArray,
|
val characterName: ByteArray,
|
||||||
@@ -23,11 +51,67 @@ object CardDtos {
|
|||||||
val characterDp: Int,
|
val characterDp: Int,
|
||||||
val characterHp: Int,
|
val characterHp: Int,
|
||||||
val steps: Int,
|
val steps: Int,
|
||||||
)
|
) {
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (this === other) return true
|
||||||
|
if (javaClass != other?.javaClass) return false
|
||||||
|
|
||||||
|
other as CardAdventureWithSprites
|
||||||
|
|
||||||
|
if (!characterName.contentEquals(other.characterName)) return false
|
||||||
|
if (characterNameWidth != other.characterNameWidth) return false
|
||||||
|
if (characterNameHeight != other.characterNameHeight) return false
|
||||||
|
if (!characterIdleSprite.contentEquals(other.characterIdleSprite)) return false
|
||||||
|
if (characterIdleSpriteWidth != other.characterIdleSpriteWidth) return false
|
||||||
|
if (characterIdleSpriteHeight != other.characterIdleSpriteHeight) return false
|
||||||
|
if (characterAp != other.characterAp) return false
|
||||||
|
if (characterBp != other.characterBp) return false
|
||||||
|
if (characterDp != other.characterDp) return false
|
||||||
|
if (characterHp != other.characterHp) return false
|
||||||
|
if (steps != other.steps) return false
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
var result = characterName.contentHashCode()
|
||||||
|
result = 31 * result + characterNameWidth
|
||||||
|
result = 31 * result + characterNameHeight
|
||||||
|
result = 31 * result + characterIdleSprite.contentHashCode()
|
||||||
|
result = 31 * result + characterIdleSpriteWidth
|
||||||
|
result = 31 * result + characterIdleSpriteHeight
|
||||||
|
result = 31 * result + characterAp
|
||||||
|
result = 31 * result + (characterBp ?: 0)
|
||||||
|
result = 31 * result + characterDp
|
||||||
|
result = 31 * result + characterHp
|
||||||
|
result = 31 * result + steps
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
data class CardIcon (
|
data class CardIcon (
|
||||||
val cardIcon: ByteArray,
|
val cardIcon: ByteArray,
|
||||||
val cardIconWidth: Int,
|
val cardIconWidth: Int,
|
||||||
val cardIconHeight: Int
|
val cardIconHeight: Int
|
||||||
)
|
) {
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (this === other) return true
|
||||||
|
if (javaClass != other?.javaClass) return false
|
||||||
|
|
||||||
|
other as CardIcon
|
||||||
|
|
||||||
|
if (!cardIcon.contentEquals(other.cardIcon)) return false
|
||||||
|
if (cardIconWidth != other.cardIconWidth) return false
|
||||||
|
if (cardIconHeight != other.cardIconHeight) return false
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
var result = cardIcon.contentHashCode()
|
||||||
|
result = 31 * result + cardIconWidth
|
||||||
|
result = 31 * result + cardIconHeight
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -33,7 +33,75 @@ object CharacterDtos {
|
|||||||
val isBemCard: Boolean,
|
val isBemCard: Boolean,
|
||||||
val isInAdventure: Boolean,
|
val isInAdventure: Boolean,
|
||||||
val active: Boolean
|
val active: Boolean
|
||||||
)
|
) {
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (this === other) return true
|
||||||
|
if (javaClass != other?.javaClass) return false
|
||||||
|
|
||||||
|
other as CharacterWithSprites
|
||||||
|
|
||||||
|
if (id != other.id) return false
|
||||||
|
if (charId != other.charId) return false
|
||||||
|
if (stage != other.stage) return false
|
||||||
|
if (attribute != other.attribute) return false
|
||||||
|
if (ageInDays != other.ageInDays) return false
|
||||||
|
if (mood != other.mood) return false
|
||||||
|
if (vitalPoints != other.vitalPoints) return false
|
||||||
|
if (transformationCountdown != other.transformationCountdown) return false
|
||||||
|
if (injuryStatus != other.injuryStatus) return false
|
||||||
|
if (trophies != other.trophies) return false
|
||||||
|
if (currentPhaseBattlesWon != other.currentPhaseBattlesWon) return false
|
||||||
|
if (currentPhaseBattlesLost != other.currentPhaseBattlesLost) return false
|
||||||
|
if (totalBattlesWon != other.totalBattlesWon) return false
|
||||||
|
if (totalBattlesLost != other.totalBattlesLost) return false
|
||||||
|
if (activityLevel != other.activityLevel) return false
|
||||||
|
if (heartRateCurrent != other.heartRateCurrent) return false
|
||||||
|
if (characterType != other.characterType) return false
|
||||||
|
if (!spriteIdle.contentEquals(other.spriteIdle)) return false
|
||||||
|
if (!spriteIdle2.contentEquals(other.spriteIdle2)) return false
|
||||||
|
if (spriteWidth != other.spriteWidth) return false
|
||||||
|
if (spriteHeight != other.spriteHeight) return false
|
||||||
|
if (!nameSprite.contentEquals(other.nameSprite)) return false
|
||||||
|
if (nameSpriteWidth != other.nameSpriteWidth) return false
|
||||||
|
if (nameSpriteHeight != other.nameSpriteHeight) return false
|
||||||
|
if (isBemCard != other.isBemCard) return false
|
||||||
|
if (isInAdventure != other.isInAdventure) return false
|
||||||
|
if (active != other.active) return false
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
var result = id.hashCode()
|
||||||
|
result = 31 * result + charId.hashCode()
|
||||||
|
result = 31 * result + stage
|
||||||
|
result = 31 * result + attribute.hashCode()
|
||||||
|
result = 31 * result + ageInDays
|
||||||
|
result = 31 * result + mood
|
||||||
|
result = 31 * result + vitalPoints
|
||||||
|
result = 31 * result + transformationCountdown
|
||||||
|
result = 31 * result + injuryStatus.hashCode()
|
||||||
|
result = 31 * result + trophies
|
||||||
|
result = 31 * result + currentPhaseBattlesWon
|
||||||
|
result = 31 * result + currentPhaseBattlesLost
|
||||||
|
result = 31 * result + totalBattlesWon
|
||||||
|
result = 31 * result + totalBattlesLost
|
||||||
|
result = 31 * result + activityLevel
|
||||||
|
result = 31 * result + heartRateCurrent
|
||||||
|
result = 31 * result + characterType.hashCode()
|
||||||
|
result = 31 * result + spriteIdle.contentHashCode()
|
||||||
|
result = 31 * result + spriteIdle2.contentHashCode()
|
||||||
|
result = 31 * result + spriteWidth
|
||||||
|
result = 31 * result + spriteHeight
|
||||||
|
result = 31 * result + nameSprite.contentHashCode()
|
||||||
|
result = 31 * result + nameSpriteWidth
|
||||||
|
result = 31 * result + nameSpriteHeight
|
||||||
|
result = 31 * result + isBemCard.hashCode()
|
||||||
|
result = 31 * result + isInAdventure.hashCode()
|
||||||
|
result = 31 * result + active.hashCode()
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
data class CardCharacterInfo(
|
data class CardCharacterInfo(
|
||||||
val cardId: Long,
|
val cardId: Long,
|
||||||
@@ -50,7 +118,33 @@ object CharacterDtos {
|
|||||||
val spriteHeight: Int,
|
val spriteHeight: Int,
|
||||||
val monIndex: Int,
|
val monIndex: Int,
|
||||||
val transformationDate: Long
|
val transformationDate: Long
|
||||||
)
|
) {
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (this === other) return true
|
||||||
|
if (javaClass != other?.javaClass) return false
|
||||||
|
|
||||||
|
other as TransformationHistory
|
||||||
|
|
||||||
|
if (id != other.id) return false
|
||||||
|
if (!spriteIdle.contentEquals(other.spriteIdle)) return false
|
||||||
|
if (spriteWidth != other.spriteWidth) return false
|
||||||
|
if (spriteHeight != other.spriteHeight) return false
|
||||||
|
if (monIndex != other.monIndex) return false
|
||||||
|
if (transformationDate != other.transformationDate) return false
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
var result = id.hashCode()
|
||||||
|
result = 31 * result + spriteIdle.contentHashCode()
|
||||||
|
result = 31 * result + spriteWidth
|
||||||
|
result = 31 * result + spriteHeight
|
||||||
|
result = 31 * result + monIndex
|
||||||
|
result = 31 * result + transformationDate.hashCode()
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
data class TransformationHistoryExport(
|
data class TransformationHistoryExport(
|
||||||
val stageId: Long,
|
val stageId: Long,
|
||||||
@@ -73,14 +167,76 @@ object CharacterDtos {
|
|||||||
val baseAp: Int,
|
val baseAp: Int,
|
||||||
val stage: Int,
|
val stage: Int,
|
||||||
val attribute: NfcCharacter.Attribute,
|
val attribute: NfcCharacter.Attribute,
|
||||||
)
|
) {
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (this === other) return true
|
||||||
|
if (javaClass != other?.javaClass) return false
|
||||||
|
|
||||||
|
other as CardCharaProgress
|
||||||
|
|
||||||
|
if (id != other.id) return false
|
||||||
|
if (!spriteIdle.contentEquals(other.spriteIdle)) return false
|
||||||
|
if (spriteWidth != other.spriteWidth) return false
|
||||||
|
if (spriteHeight != other.spriteHeight) return false
|
||||||
|
if (!nameSprite.contentEquals(other.nameSprite)) return false
|
||||||
|
if (nameSpriteWidth != other.nameSpriteWidth) return false
|
||||||
|
if (nameSpriteHeight != other.nameSpriteHeight) return false
|
||||||
|
if (discoveredOn != other.discoveredOn) return false
|
||||||
|
if (baseHp != other.baseHp) return false
|
||||||
|
if (baseBp != other.baseBp) return false
|
||||||
|
if (baseAp != other.baseAp) return false
|
||||||
|
if (stage != other.stage) return false
|
||||||
|
if (attribute != other.attribute) return false
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
var result = id.hashCode()
|
||||||
|
result = 31 * result + spriteIdle.contentHashCode()
|
||||||
|
result = 31 * result + spriteWidth
|
||||||
|
result = 31 * result + spriteHeight
|
||||||
|
result = 31 * result + nameSprite.contentHashCode()
|
||||||
|
result = 31 * result + nameSpriteWidth
|
||||||
|
result = 31 * result + nameSpriteHeight
|
||||||
|
result = 31 * result + (discoveredOn?.hashCode() ?: 0)
|
||||||
|
result = 31 * result + baseHp
|
||||||
|
result = 31 * result + baseBp
|
||||||
|
result = 31 * result + baseAp
|
||||||
|
result = 31 * result + stage
|
||||||
|
result = 31 * result + attribute.hashCode()
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
data class CardProgress(
|
data class CardProgress(
|
||||||
val id: Long,
|
val id: Long,
|
||||||
val spriteIdle: ByteArray,
|
val spriteIdle: ByteArray,
|
||||||
val spriteWidth: Int,
|
val spriteWidth: Int,
|
||||||
val spriteHeight: Int,
|
val spriteHeight: Int,
|
||||||
)
|
) {
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (this === other) return true
|
||||||
|
if (javaClass != other?.javaClass) return false
|
||||||
|
|
||||||
|
other as CardProgress
|
||||||
|
|
||||||
|
if (id != other.id) return false
|
||||||
|
if (!spriteIdle.contentEquals(other.spriteIdle)) return false
|
||||||
|
if (spriteWidth != other.spriteWidth) return false
|
||||||
|
if (spriteHeight != other.spriteHeight) return false
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
var result = id.hashCode()
|
||||||
|
result = 31 * result + spriteIdle.contentHashCode()
|
||||||
|
result = 31 * result + spriteWidth
|
||||||
|
result = 31 * result + spriteHeight
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
data class AdventureCharacterWithSprites(
|
data class AdventureCharacterWithSprites(
|
||||||
var id: Long = 0,
|
var id: Long = 0,
|
||||||
@@ -106,7 +262,67 @@ object CharacterDtos {
|
|||||||
val isBemCard: Boolean,
|
val isBemCard: Boolean,
|
||||||
val finishesAdventure: Long,
|
val finishesAdventure: Long,
|
||||||
val originalTimeInMinutes: Long
|
val originalTimeInMinutes: Long
|
||||||
)
|
) {
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (this === other) return true
|
||||||
|
if (javaClass != other?.javaClass) return false
|
||||||
|
|
||||||
|
other as AdventureCharacterWithSprites
|
||||||
|
|
||||||
|
if (id != other.id) return false
|
||||||
|
if (charId != other.charId) return false
|
||||||
|
if (stage != other.stage) return false
|
||||||
|
if (attribute != other.attribute) return false
|
||||||
|
if (ageInDays != other.ageInDays) return false
|
||||||
|
if (mood != other.mood) return false
|
||||||
|
if (vitalPoints != other.vitalPoints) return false
|
||||||
|
if (transformationCountdown != other.transformationCountdown) return false
|
||||||
|
if (injuryStatus != other.injuryStatus) return false
|
||||||
|
if (trophies != other.trophies) return false
|
||||||
|
if (currentPhaseBattlesWon != other.currentPhaseBattlesWon) return false
|
||||||
|
if (currentPhaseBattlesLost != other.currentPhaseBattlesLost) return false
|
||||||
|
if (totalBattlesWon != other.totalBattlesWon) return false
|
||||||
|
if (totalBattlesLost != other.totalBattlesLost) return false
|
||||||
|
if (activityLevel != other.activityLevel) return false
|
||||||
|
if (heartRateCurrent != other.heartRateCurrent) return false
|
||||||
|
if (characterType != other.characterType) return false
|
||||||
|
if (!spriteIdle.contentEquals(other.spriteIdle)) return false
|
||||||
|
if (spriteWidth != other.spriteWidth) return false
|
||||||
|
if (spriteHeight != other.spriteHeight) return false
|
||||||
|
if (isBemCard != other.isBemCard) return false
|
||||||
|
if (finishesAdventure != other.finishesAdventure) return false
|
||||||
|
if (originalTimeInMinutes != other.originalTimeInMinutes) return false
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
var result = id.hashCode()
|
||||||
|
result = 31 * result + charId.hashCode()
|
||||||
|
result = 31 * result + stage
|
||||||
|
result = 31 * result + attribute.hashCode()
|
||||||
|
result = 31 * result + ageInDays
|
||||||
|
result = 31 * result + mood
|
||||||
|
result = 31 * result + vitalPoints
|
||||||
|
result = 31 * result + transformationCountdown
|
||||||
|
result = 31 * result + injuryStatus.hashCode()
|
||||||
|
result = 31 * result + trophies
|
||||||
|
result = 31 * result + currentPhaseBattlesWon
|
||||||
|
result = 31 * result + currentPhaseBattlesLost
|
||||||
|
result = 31 * result + totalBattlesWon
|
||||||
|
result = 31 * result + totalBattlesLost
|
||||||
|
result = 31 * result + activityLevel
|
||||||
|
result = 31 * result + heartRateCurrent
|
||||||
|
result = 31 * result + characterType.hashCode()
|
||||||
|
result = 31 * result + spriteIdle.contentHashCode()
|
||||||
|
result = 31 * result + spriteWidth
|
||||||
|
result = 31 * result + spriteHeight
|
||||||
|
result = 31 * result + isBemCard.hashCode()
|
||||||
|
result = 31 * result + finishesAdventure.hashCode()
|
||||||
|
result = 31 * result + originalTimeInMinutes.hashCode()
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
data class EvolutionRequirementsWithSpritesAndObtained(
|
data class EvolutionRequirementsWithSpritesAndObtained(
|
||||||
val charaId: Long,
|
val charaId: Long,
|
||||||
@@ -121,7 +337,45 @@ object CharacterDtos {
|
|||||||
val requiredWinRate: Int,
|
val requiredWinRate: Int,
|
||||||
val changeTimerHours: Int,
|
val changeTimerHours: Int,
|
||||||
val requiredAdventureLevelCompleted: Int
|
val requiredAdventureLevelCompleted: Int
|
||||||
)
|
) {
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (this === other) return true
|
||||||
|
if (javaClass != other?.javaClass) return false
|
||||||
|
|
||||||
|
other as EvolutionRequirementsWithSpritesAndObtained
|
||||||
|
|
||||||
|
if (charaId != other.charaId) return false
|
||||||
|
if (fromCharaId != other.fromCharaId) return false
|
||||||
|
if (!spriteIdle.contentEquals(other.spriteIdle)) return false
|
||||||
|
if (spriteWidth != other.spriteWidth) return false
|
||||||
|
if (spriteHeight != other.spriteHeight) return false
|
||||||
|
if (discoveredOn != other.discoveredOn) return false
|
||||||
|
if (requiredTrophies != other.requiredTrophies) return false
|
||||||
|
if (requiredVitals != other.requiredVitals) return false
|
||||||
|
if (requiredBattles != other.requiredBattles) return false
|
||||||
|
if (requiredWinRate != other.requiredWinRate) return false
|
||||||
|
if (changeTimerHours != other.changeTimerHours) return false
|
||||||
|
if (requiredAdventureLevelCompleted != other.requiredAdventureLevelCompleted) return false
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
var result = charaId.hashCode()
|
||||||
|
result = 31 * result + fromCharaId.hashCode()
|
||||||
|
result = 31 * result + spriteIdle.contentHashCode()
|
||||||
|
result = 31 * result + spriteWidth
|
||||||
|
result = 31 * result + spriteHeight
|
||||||
|
result = 31 * result + (discoveredOn?.hashCode() ?: 0)
|
||||||
|
result = 31 * result + requiredTrophies
|
||||||
|
result = 31 * result + requiredVitals
|
||||||
|
result = 31 * result + requiredBattles
|
||||||
|
result = 31 * result + requiredWinRate
|
||||||
|
result = 31 * result + changeTimerHours
|
||||||
|
result = 31 * result + requiredAdventureLevelCompleted
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
data class FusionsWithSpritesAndObtained(
|
data class FusionsWithSpritesAndObtained(
|
||||||
val charaId: Long,
|
val charaId: Long,
|
||||||
@@ -131,5 +385,33 @@ object CharacterDtos {
|
|||||||
val spriteHeight: Int,
|
val spriteHeight: Int,
|
||||||
val discoveredOn: Long?,
|
val discoveredOn: Long?,
|
||||||
val fusionAttribute: NfcCharacter.Attribute
|
val fusionAttribute: NfcCharacter.Attribute
|
||||||
)
|
) {
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (this === other) return true
|
||||||
|
if (javaClass != other?.javaClass) return false
|
||||||
|
|
||||||
|
other as FusionsWithSpritesAndObtained
|
||||||
|
|
||||||
|
if (charaId != other.charaId) return false
|
||||||
|
if (fromCharaId != other.fromCharaId) return false
|
||||||
|
if (!spriteIdle.contentEquals(other.spriteIdle)) return false
|
||||||
|
if (spriteWidth != other.spriteWidth) return false
|
||||||
|
if (spriteHeight != other.spriteHeight) return false
|
||||||
|
if (discoveredOn != other.discoveredOn) return false
|
||||||
|
if (fusionAttribute != other.fusionAttribute) return false
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
var result = charaId.hashCode()
|
||||||
|
result = 31 * result + fromCharaId.hashCode()
|
||||||
|
result = 31 * result + spriteIdle.contentHashCode()
|
||||||
|
result = 31 * result + spriteWidth
|
||||||
|
result = 31 * result + spriteHeight
|
||||||
|
result = 31 * result + (discoveredOn?.hashCode() ?: 0)
|
||||||
|
result = 31 * result + fusionAttribute.hashCode()
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,15 @@
|
|||||||
package com.github.nacabaro.vbhelper.screens
|
package com.github.nacabaro.vbhelper.screens
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is me, nacabaro.
|
||||||
|
* File related to battles.
|
||||||
|
* TODO: Refactor this, 2400+ lines feels like a lot.
|
||||||
|
*/
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
//import androidx.compose.foundation.layout.Row
|
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
@@ -28,10 +33,6 @@ import androidx.compose.ui.unit.sp
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.material3.LinearProgressIndicator
|
import androidx.compose.material3.LinearProgressIndicator
|
||||||
//import androidx.compose.material3.ExposedDropdownMenuBox
|
|
||||||
//import androidx.compose.material3.ExposedDropdownMenuDefaults
|
|
||||||
//import androidx.compose.material3.OutlinedTextField
|
|
||||||
//import androidx.compose.material3.DropdownMenuItem
|
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
@@ -51,8 +52,6 @@ import android.content.Intent
|
|||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.media.MediaPlayer
|
import android.media.MediaPlayer
|
||||||
import android.os.Environment
|
import android.os.Environment
|
||||||
//import androidx.compose.animation.core.animateFloatAsState
|
|
||||||
//import androidx.compose.animation.core.tween
|
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
@@ -72,13 +71,8 @@ import com.github.nacabaro.vbhelper.battle.AnimatedSpriteImage
|
|||||||
import com.github.nacabaro.vbhelper.battle.HitEffectOverlay
|
import com.github.nacabaro.vbhelper.battle.HitEffectOverlay
|
||||||
import com.github.nacabaro.vbhelper.battle.BattleAuthContainer
|
import com.github.nacabaro.vbhelper.battle.BattleAuthContainer
|
||||||
import kotlinx.coroutines.flow.first
|
import kotlinx.coroutines.flow.first
|
||||||
import kotlinx.coroutines.flow.collect
|
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
//import kotlin.math.sin
|
|
||||||
//import kotlin.math.PI
|
|
||||||
//import androidx.compose.animation.core.animateDpAsState
|
|
||||||
//import androidx.compose.animation.core.animateIntAsState
|
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.draw.alpha
|
import androidx.compose.ui.draw.alpha
|
||||||
import androidx.compose.ui.graphics.Shadow
|
import androidx.compose.ui.graphics.Shadow
|
||||||
@@ -86,24 +80,20 @@ import androidx.compose.ui.text.TextStyle
|
|||||||
import androidx.compose.foundation.Image
|
import androidx.compose.foundation.Image
|
||||||
import androidx.compose.ui.graphics.asImageBitmap
|
import androidx.compose.ui.graphics.asImageBitmap
|
||||||
import android.graphics.BitmapFactory
|
import android.graphics.BitmapFactory
|
||||||
//import android.os.Environment
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import androidx.compose.ui.platform.LocalDensity
|
import androidx.compose.ui.platform.LocalDensity
|
||||||
import androidx.compose.ui.platform.LocalConfiguration
|
import androidx.compose.ui.platform.LocalConfiguration
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import com.github.nacabaro.vbhelper.di.VBHelper
|
import com.github.nacabaro.vbhelper.di.VBHelper
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
//import kotlinx.coroutines.launch
|
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import androidx.compose.ui.window.Dialog
|
|
||||||
import androidx.compose.ui.window.DialogProperties
|
|
||||||
import androidx.compose.material3.AlertDialog
|
import androidx.compose.material3.AlertDialog
|
||||||
|
|
||||||
@Composable
|
/*@Composable
|
||||||
fun isLandscapeMode(): Boolean {
|
fun isLandscapeMode(): Boolean {
|
||||||
val configuration = LocalConfiguration.current
|
val configuration = LocalConfiguration.current
|
||||||
return configuration.screenWidthDp > configuration.screenHeightDp
|
return configuration.screenWidthDp > configuration.screenHeightDp
|
||||||
}
|
}*/
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun getLandscapeModifier(): Modifier {
|
fun getLandscapeModifier(): Modifier {
|
||||||
@@ -1735,7 +1725,7 @@ fun BattlesScreen() {
|
|||||||
val currentCharacter = activeUserCharacter
|
val currentCharacter = activeUserCharacter
|
||||||
if (currentCharacter != null && canBattle && playerBattleType != null) {
|
if (currentCharacter != null && canBattle && playerBattleType != null) {
|
||||||
try {
|
try {
|
||||||
RetrofitHelper().getOpponents(context, playerBattleType!!) { opponents ->
|
RetrofitHelper().getOpponents(context, playerBattleType) { opponents ->
|
||||||
try {
|
try {
|
||||||
// Create a new list to trigger UI recomposition
|
// Create a new list to trigger UI recomposition
|
||||||
opponentsList = ArrayList(opponents.opponentsList)
|
opponentsList = ArrayList(opponents.opponentsList)
|
||||||
@@ -2415,7 +2405,7 @@ fun BattlesScreen() {
|
|||||||
|
|
||||||
// Also check winner field if it's not empty
|
// Also check winner field if it's not empty
|
||||||
val playerWonFromWinner = activeCardId?.let { cardId ->
|
val playerWonFromWinner = activeCardId?.let { cardId ->
|
||||||
val winner = apiResult.winner ?: ""
|
val winner = apiResult.winner
|
||||||
if (winner.isNotEmpty()) {
|
if (winner.isNotEmpty()) {
|
||||||
if (winner.contains("|")) {
|
if (winner.contains("|")) {
|
||||||
// Pipe-separated format: first part is the winner
|
// Pipe-separated format: first part is the winner
|
||||||
@@ -2442,7 +2432,7 @@ fun BattlesScreen() {
|
|||||||
println("BATTLESCREEN: Battle result (first call) - winner: '${apiResult.winner}', playerHP: ${apiResult.playerHP}, opponentHP: ${apiResult.opponentHP}, playerWonFromHP: $playerWonFromHP, playerWonFromWinner: $playerWonFromWinner, final playerWon: $playerWon")
|
println("BATTLESCREEN: Battle result (first call) - winner: '${apiResult.winner}', playerHP: ${apiResult.playerHP}, opponentHP: ${apiResult.opponentHP}, playerWonFromHP: $playerWonFromHP, playerWonFromWinner: $playerWonFromWinner, final playerWon: $playerWon")
|
||||||
|
|
||||||
// Store winner name for display (will be updated in cleanup call if available)
|
// Store winner name for display (will be updated in cleanup call if available)
|
||||||
winnerName = apiResult.winner ?: if (playerWon) "You" else "Opponent"
|
winnerName = apiResult.winner
|
||||||
isWinnerLoaded = true
|
isWinnerLoaded = true
|
||||||
|
|
||||||
// Then send the cleanup call - this will have the actual winner name
|
// Then send the cleanup call - this will have the actual winner name
|
||||||
@@ -2465,7 +2455,7 @@ fun BattlesScreen() {
|
|||||||
// Primary method: Check HP values (opponentHP <= 0 means opponent lost = player won)
|
// Primary method: Check HP values (opponentHP <= 0 means opponent lost = player won)
|
||||||
// Secondary method: Check winner name (if winner doesn't match opponent, player won)
|
// Secondary method: Check winner name (if winner doesn't match opponent, player won)
|
||||||
val opponentName = selectedOpponent?.name ?: ""
|
val opponentName = selectedOpponent?.name ?: ""
|
||||||
val winner = cleanupResult.winner ?: ""
|
val winner = cleanupResult.winner
|
||||||
|
|
||||||
// Primary: HP-based determination (most reliable)
|
// Primary: HP-based determination (most reliable)
|
||||||
// If opponentHP <= 0, opponent is dead = player won
|
// If opponentHP <= 0, opponent is dead = player won
|
||||||
|
|||||||
+6
-5
@@ -45,11 +45,12 @@ class HomeScreenControllerImpl(
|
|||||||
.clearSpecialMission(missionId)
|
.clearSpecialMission(missionId)
|
||||||
|
|
||||||
if (missionStatus.status == SpecialMission.Status.COMPLETED) {
|
if (missionStatus.status == SpecialMission.Status.COMPLETED) {
|
||||||
val randomItem = database
|
val allItems = database.itemDao().getAllItems().first()
|
||||||
.itemDao()
|
if (allItems.isEmpty()) {
|
||||||
.getAllItems()
|
onCleared(null, null)
|
||||||
.first()
|
return@launch
|
||||||
.random()
|
}
|
||||||
|
val randomItem = allItems.random()
|
||||||
|
|
||||||
val randomItemAmount = (Random.nextFloat() * 5).roundToInt()
|
val randomItemAmount = (Random.nextFloat() * 5).roundToInt()
|
||||||
|
|
||||||
|
|||||||
+4
-4
@@ -142,7 +142,7 @@ fun VBDiMHomeScreen(
|
|||||||
ItemDisplay(
|
ItemDisplay(
|
||||||
icon = R.drawable.baseline_swords_24,
|
icon = R.drawable.baseline_swords_24,
|
||||||
textValue = when {
|
textValue = when {
|
||||||
activeMon.totalBattlesLost == 0 -> "0.00 %"
|
activeMon.currentPhaseBattlesWon + activeMon.currentPhaseBattlesLost == 0 -> "0.00 %"
|
||||||
else -> {
|
else -> {
|
||||||
val battleWinPercentage =
|
val battleWinPercentage =
|
||||||
activeMon.currentPhaseBattlesWon.toFloat() / (activeMon.currentPhaseBattlesWon + activeMon.currentPhaseBattlesLost).toFloat()
|
activeMon.currentPhaseBattlesWon.toFloat() / (activeMon.currentPhaseBattlesWon + activeMon.currentPhaseBattlesLost).toFloat()
|
||||||
@@ -150,7 +150,7 @@ fun VBDiMHomeScreen(
|
|||||||
Locale.getDefault(),
|
Locale.getDefault(),
|
||||||
"%.2f",
|
"%.2f",
|
||||||
battleWinPercentage * 100
|
battleWinPercentage * 100
|
||||||
) + " %" // Specify locale
|
) + " %"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
definition = stringResource(R.string.home_vbdim_current_phase_win),
|
definition = stringResource(R.string.home_vbdim_current_phase_win),
|
||||||
@@ -193,9 +193,9 @@ fun VBDiMHomeScreen(
|
|||||||
onClickMission = { missionId ->
|
onClickMission = { missionId ->
|
||||||
selectedSpecialMissionId = missionId
|
selectedSpecialMissionId = missionId
|
||||||
},
|
},
|
||||||
onClickCollect = {
|
onClickCollect = { missionId ->
|
||||||
homeScreenController
|
homeScreenController
|
||||||
.clearSpecialMission(selectedSpecialMissionId, onClickCollect)
|
.clearSpecialMission(missionId, onClickCollect)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ import androidx.compose.foundation.layout.Box
|
|||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
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.PrimaryTabRow
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.Tab
|
import androidx.compose.material3.Tab
|
||||||
import androidx.compose.material3.TabRow
|
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
@@ -34,7 +34,7 @@ fun ItemsScreen(
|
|||||||
topBar = {
|
topBar = {
|
||||||
Column {
|
Column {
|
||||||
TopBanner(text = stringResource(R.string.items_title))
|
TopBanner(text = stringResource(R.string.items_title))
|
||||||
TabRow(
|
PrimaryTabRow(
|
||||||
selectedTabIndex = selectedTabItem,
|
selectedTabIndex = selectedTabItem,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -44,14 +44,8 @@ fun ScanScreen(
|
|||||||
|
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
|
|
||||||
LaunchedEffect(storageRepository) {
|
LaunchedEffect(characterId) {
|
||||||
withContext(Dispatchers.IO) {
|
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) {
|
if (characterId != null && nfcCharacter == null) {
|
||||||
nfcCharacter = scanScreenController.characterToNfc(characterId)
|
nfcCharacter = scanScreenController.characterToNfc(characterId)
|
||||||
}
|
}
|
||||||
|
|||||||
+100
-17
@@ -3,6 +3,7 @@ package com.github.nacabaro.vbhelper.screens.scanScreen
|
|||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.nfc.NfcAdapter
|
import android.nfc.NfcAdapter
|
||||||
import android.nfc.Tag
|
import android.nfc.Tag
|
||||||
|
import android.nfc.tech.IsoDep
|
||||||
import android.nfc.tech.NfcA
|
import android.nfc.tech.NfcA
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.provider.Settings
|
import android.provider.Settings
|
||||||
@@ -15,12 +16,16 @@ import com.github.cfogrady.vbnfc.be.BENfcCharacter
|
|||||||
import com.github.cfogrady.vbnfc.data.NfcCharacter
|
import com.github.cfogrady.vbnfc.data.NfcCharacter
|
||||||
import com.github.cfogrady.vbnfc.vb.VBNfcCharacter
|
import com.github.cfogrady.vbnfc.vb.VBNfcCharacter
|
||||||
import com.github.nacabaro.vbhelper.ActivityLifecycleListener
|
import com.github.nacabaro.vbhelper.ActivityLifecycleListener
|
||||||
|
import com.github.nacabaro.vbhelper.database.AppDatabase
|
||||||
import com.github.nacabaro.vbhelper.domain.card.Card
|
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.FromNfcConverter
|
||||||
import com.github.nacabaro.vbhelper.screens.scanScreen.converters.ToNfcConverter
|
import com.github.nacabaro.vbhelper.screens.scanScreen.converters.ToNfcConverter
|
||||||
|
import com.github.nacabaro.vbhelper.source.VitalWearCharacterExporter
|
||||||
|
import com.github.nacabaro.vbhelper.source.VitalWearCharacterImporter
|
||||||
import com.github.nacabaro.vbhelper.source.getCryptographicTransformerMap
|
import com.github.nacabaro.vbhelper.source.getCryptographicTransformerMap
|
||||||
import com.github.nacabaro.vbhelper.source.isMissingSecrets
|
import com.github.nacabaro.vbhelper.source.isMissingSecrets
|
||||||
import com.github.nacabaro.vbhelper.source.proto.Secrets
|
import com.github.nacabaro.vbhelper.source.proto.Secrets
|
||||||
|
import com.github.nacabaro.vbhelper.transfer.hce.VitalWearHceReaderClient
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.stateIn
|
import kotlinx.coroutines.flow.stateIn
|
||||||
@@ -32,8 +37,10 @@ class ScanScreenControllerImpl(
|
|||||||
private val componentActivity: ComponentActivity,
|
private val componentActivity: ComponentActivity,
|
||||||
private val registerActivityLifecycleListener: (String, ActivityLifecycleListener)->Unit,
|
private val registerActivityLifecycleListener: (String, ActivityLifecycleListener)->Unit,
|
||||||
private val unregisterActivityLifecycleListener: (String)->Unit,
|
private val unregisterActivityLifecycleListener: (String)->Unit,
|
||||||
|
private val database: AppDatabase,
|
||||||
): ScanScreenController {
|
): ScanScreenController {
|
||||||
private var lastScannedCharacter: NfcCharacter? = null
|
private var lastScannedCharacter: NfcCharacter? = null
|
||||||
|
private var pendingExportCharacterId: Long? = null
|
||||||
private val nfcAdapter: NfcAdapter
|
private val nfcAdapter: NfcAdapter
|
||||||
|
|
||||||
init {
|
init {
|
||||||
@@ -46,7 +53,9 @@ class ScanScreenControllerImpl(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onClickRead(secrets: Secrets, onComplete: ()->Unit, onMultipleCards: (List<Card>) -> Unit) {
|
override fun onClickRead(secrets: Secrets, onComplete: ()->Unit, onMultipleCards: (List<Card>) -> Unit) {
|
||||||
handleTag(secrets) { tagCommunicator ->
|
handleTag(
|
||||||
|
secrets,
|
||||||
|
handlerFunc = { tagCommunicator ->
|
||||||
try {
|
try {
|
||||||
val character = tagCommunicator.receiveCharacter()
|
val character = tagCommunicator.receiveCharacter()
|
||||||
val resultMessage = characterFromNfc(character) { cards, nfcCharacter ->
|
val resultMessage = characterFromNfc(character) { cards, nfcCharacter ->
|
||||||
@@ -63,7 +72,27 @@ class ScanScreenControllerImpl(
|
|||||||
onComplete.invoke()
|
onComplete.invoke()
|
||||||
componentActivity.getString(R.string.scan_error_generic)
|
componentActivity.getString(R.string.scan_error_generic)
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
hceHandler = { isoDep ->
|
||||||
|
try {
|
||||||
|
val client = VitalWearHceReaderClient(isoDep)
|
||||||
|
val character = client.receiveCharacterFromWatch()
|
||||||
|
val importer = VitalWearCharacterImporter(database)
|
||||||
|
val result = importer.importCharacter(character)
|
||||||
|
Log.i("NFC_READ", "VitalWear HCE import: ${result.message}")
|
||||||
|
componentActivity.runOnUiThread {
|
||||||
|
Toast.makeText(componentActivity, result.message, Toast.LENGTH_SHORT).show()
|
||||||
}
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e("NFC_READ", "Error reading character from VitalWear HCE", e)
|
||||||
|
componentActivity.runOnUiThread {
|
||||||
|
Toast.makeText(componentActivity, componentActivity.getString(R.string.scan_error_generic) + ": " + (e.message ?: e.javaClass.simpleName), Toast.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
onComplete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun cancelRead() {
|
override fun cancelRead() {
|
||||||
@@ -83,29 +112,45 @@ class ScanScreenControllerImpl(
|
|||||||
unregisterActivityLifecycleListener.invoke(key)
|
unregisterActivityLifecycleListener.invoke(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
// EXTRACTED DIRECTLY FROM EXAMPLE APP
|
private fun handleTag(
|
||||||
private fun handleTag(secrets: Secrets, handlerFunc: (TagCommunicator)->String) {
|
secrets: Secrets,
|
||||||
|
handlerFunc: (TagCommunicator) -> String,
|
||||||
|
hceHandler: ((IsoDep) -> Unit)? = null,
|
||||||
|
) {
|
||||||
if (!nfcAdapter.isEnabled) {
|
if (!nfcAdapter.isEnabled) {
|
||||||
showWirelessSettings()
|
showWirelessSettings()
|
||||||
} else {
|
} else {
|
||||||
val options = Bundle()
|
val options = Bundle()
|
||||||
// Work around for some broken Nfc firmware implementations that poll the card too fast
|
// Work around for some broken Nfc firmware implementations that poll the card too fast
|
||||||
options.putInt(NfcAdapter.EXTRA_READER_PRESENCE_CHECK_DELAY, 250)
|
options.putInt(NfcAdapter.EXTRA_READER_PRESENCE_CHECK_DELAY, 250)
|
||||||
nfcAdapter.enableReaderMode(componentActivity, buildOnReadTag(secrets, handlerFunc), NfcAdapter.FLAG_READER_NFC_A or NfcAdapter.FLAG_READER_SKIP_NDEF_CHECK,
|
nfcAdapter.enableReaderMode(
|
||||||
|
componentActivity,
|
||||||
|
buildOnReadTag(secrets, handlerFunc, hceHandler),
|
||||||
|
NfcAdapter.FLAG_READER_NFC_A or NfcAdapter.FLAG_READER_SKIP_NDEF_CHECK,
|
||||||
options
|
options
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// EXTRACTED DIRECTLY FROM EXAMPLE APP
|
private fun buildOnReadTag(
|
||||||
private fun buildOnReadTag(secrets: Secrets, handlerFunc: (TagCommunicator)->String): (Tag)->Unit {
|
secrets: Secrets,
|
||||||
|
handlerFunc: (TagCommunicator) -> String,
|
||||||
|
hceHandler: ((IsoDep) -> Unit)? = null,
|
||||||
|
): (Tag) -> Unit {
|
||||||
return { tag ->
|
return { tag ->
|
||||||
|
val isoDep = IsoDep.get(tag)
|
||||||
|
if (isoDep != null && hceHandler != null) {
|
||||||
|
// VitalWear watch via HCE / ISO-DEP
|
||||||
|
isoDep.connect()
|
||||||
|
isoDep.use { hceHandler(isoDep) }
|
||||||
|
} else {
|
||||||
|
// Physical Vital Bracelet via raw NFC-A
|
||||||
val nfcData = NfcA.get(tag)
|
val nfcData = NfcA.get(tag)
|
||||||
if (nfcData == null) {
|
if (nfcData == null) {
|
||||||
componentActivity.runOnUiThread {
|
componentActivity.runOnUiThread {
|
||||||
Toast.makeText(componentActivity, componentActivity.getString(R.string.scan_tag_not_vb), Toast.LENGTH_SHORT).show()
|
Toast.makeText(componentActivity, componentActivity.getString(R.string.scan_tag_not_vb), Toast.LENGTH_SHORT).show()
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
nfcData.connect()
|
nfcData.connect()
|
||||||
nfcData.use {
|
nfcData.use {
|
||||||
val tagCommunicator = TagCommunicator.getInstance(nfcData, secrets.getCryptographicTransformerMap())
|
val tagCommunicator = TagCommunicator.getInstance(nfcData, secrets.getCryptographicTransformerMap())
|
||||||
@@ -116,6 +161,8 @@ class ScanScreenControllerImpl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun checkSecrets() {
|
private fun checkSecrets() {
|
||||||
componentActivity.lifecycleScope.launch(Dispatchers.IO) {
|
componentActivity.lifecycleScope.launch(Dispatchers.IO) {
|
||||||
@@ -132,16 +179,16 @@ class ScanScreenControllerImpl(
|
|||||||
nfcCharacter: NfcCharacter,
|
nfcCharacter: NfcCharacter,
|
||||||
onComplete: () -> Unit
|
onComplete: () -> Unit
|
||||||
) {
|
) {
|
||||||
handleTag(secrets) { tagCommunicator ->
|
handleTag(
|
||||||
|
secrets,
|
||||||
|
handlerFunc = { tagCommunicator ->
|
||||||
try {
|
try {
|
||||||
if (nfcCharacter is VBNfcCharacter) {
|
if (nfcCharacter is VBNfcCharacter) {
|
||||||
Log.d("SendCharacter", "VBNfcCharacter")
|
Log.d("SendCharacter", "VBNfcCharacter")
|
||||||
val castNfcCharacter: VBNfcCharacter = nfcCharacter
|
tagCommunicator.sendCharacter(nfcCharacter)
|
||||||
tagCommunicator.sendCharacter(castNfcCharacter)
|
|
||||||
} else if (nfcCharacter is BENfcCharacter) {
|
} else if (nfcCharacter is BENfcCharacter) {
|
||||||
Log.d("SendCharacter", "BENfcCharacter")
|
Log.d("SendCharacter", "BENfcCharacter")
|
||||||
val castNfcCharacter: BENfcCharacter = nfcCharacter
|
tagCommunicator.sendCharacter(nfcCharacter)
|
||||||
tagCommunicator.sendCharacter(castNfcCharacter)
|
|
||||||
}
|
}
|
||||||
onComplete.invoke()
|
onComplete.invoke()
|
||||||
componentActivity.getString(R.string.scan_sent_character_success)
|
componentActivity.getString(R.string.scan_sent_character_success)
|
||||||
@@ -149,7 +196,12 @@ class ScanScreenControllerImpl(
|
|||||||
Log.e("TAG", e.stackTraceToString())
|
Log.e("TAG", e.stackTraceToString())
|
||||||
componentActivity.getString(R.string.scan_error_generic)
|
componentActivity.getString(R.string.scan_error_generic)
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
hceHandler = { _ ->
|
||||||
|
// Character was already sent in onClickCheckCard's HCE handler.
|
||||||
|
onComplete()
|
||||||
}
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onClickCheckCard(
|
override fun onClickCheckCard(
|
||||||
@@ -157,11 +209,44 @@ class ScanScreenControllerImpl(
|
|||||||
nfcCharacter: NfcCharacter,
|
nfcCharacter: NfcCharacter,
|
||||||
onComplete: () -> Unit
|
onComplete: () -> Unit
|
||||||
) {
|
) {
|
||||||
handleTag(secrets) { tagCommunicator ->
|
handleTag(
|
||||||
|
secrets,
|
||||||
|
handlerFunc = { tagCommunicator ->
|
||||||
tagCommunicator.prepareDIMForCharacter(nfcCharacter.dimId)
|
tagCommunicator.prepareDIMForCharacter(nfcCharacter.dimId)
|
||||||
onComplete.invoke()
|
onComplete.invoke()
|
||||||
componentActivity.getString(R.string.scan_sent_dim_success)
|
componentActivity.getString(R.string.scan_sent_dim_success)
|
||||||
|
},
|
||||||
|
hceHandler = { isoDep ->
|
||||||
|
// VitalWear watch: send the character proto directly via HCE.
|
||||||
|
val characterId = pendingExportCharacterId
|
||||||
|
if (characterId == null) {
|
||||||
|
Log.e("NFC_WRITE", "No pending character to send to VitalWear")
|
||||||
|
componentActivity.runOnUiThread {
|
||||||
|
Toast.makeText(componentActivity, componentActivity.getString(R.string.scan_error_generic), Toast.LENGTH_SHORT).show()
|
||||||
}
|
}
|
||||||
|
onComplete()
|
||||||
|
return@handleTag
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
val proto = VitalWearCharacterExporter(componentActivity, database).buildCharacterProto(characterId)
|
||||||
|
val client = VitalWearHceReaderClient(isoDep)
|
||||||
|
client.sendCharacterToWatch(proto)
|
||||||
|
database.userCharacterDao().deleteCharacterById(characterId)
|
||||||
|
pendingExportCharacterId = null
|
||||||
|
Log.i("NFC_WRITE", "Character sent to VitalWear and deleted from app (id=$characterId)")
|
||||||
|
componentActivity.runOnUiThread {
|
||||||
|
Toast.makeText(componentActivity, componentActivity.getString(R.string.scan_sent_character_success), Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e("NFC_WRITE", "Error sending character to VitalWear HCE", e)
|
||||||
|
componentActivity.runOnUiThread {
|
||||||
|
Toast.makeText(componentActivity, componentActivity.getString(R.string.scan_error_generic) + ": " + (e.message ?: e.javaClass.simpleName), Toast.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
onComplete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// EXTRACTED DIRECTLY FROM EXAMPLE APP
|
// EXTRACTED DIRECTLY FROM EXAMPLE APP
|
||||||
@@ -181,10 +266,8 @@ class ScanScreenControllerImpl(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun characterToNfc(characterId: Long): NfcCharacter {
|
override suspend fun characterToNfc(characterId: Long): NfcCharacter {
|
||||||
val nfcGenerator = ToNfcConverter(
|
pendingExportCharacterId = characterId
|
||||||
componentActivity = componentActivity
|
val nfcGenerator = ToNfcConverter(componentActivity = componentActivity)
|
||||||
)
|
|
||||||
|
|
||||||
val character = nfcGenerator.characterToNfc(characterId)
|
val character = nfcGenerator.characterToNfc(characterId)
|
||||||
Log.d("CharacterType", character.toString())
|
Log.d("CharacterType", character.toString())
|
||||||
return character
|
return character
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import com.github.nacabaro.vbhelper.domain.device_data.VBCharacterData
|
|||||||
import com.github.nacabaro.vbhelper.utils.DeviceType
|
import com.github.nacabaro.vbhelper.utils.DeviceType
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
import timber.log.Timber
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility for converting character device types.
|
* Utility for converting character device types.
|
||||||
@@ -31,7 +32,7 @@ class DeviceTypeConverter(private val database: AppDatabase) {
|
|||||||
true
|
true
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
// Log but don't crash - character can still be sent as-is
|
// Log but don't crash - character can still be sent as-is
|
||||||
android.util.Log.e("DeviceTypeConverter", "Failed to convert character $characterId to VB type", e)
|
Timber.e(e, "Failed to convert character $characterId to VB type")
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -42,7 +43,7 @@ class DeviceTypeConverter(private val database: AppDatabase) {
|
|||||||
val vbCharacterData = VBCharacterData(
|
val vbCharacterData = VBCharacterData(
|
||||||
id = characterId,
|
id = characterId,
|
||||||
generation = 0,
|
generation = 0,
|
||||||
totalTrophies = 0
|
totalTrophies = 0,
|
||||||
)
|
)
|
||||||
database.userCharacterDao().insertVBCharacterData(vbCharacterData)
|
database.userCharacterDao().insertVBCharacterData(vbCharacterData)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,14 +15,13 @@ class VitalWearCharacterExporter(
|
|||||||
private val context: Context,
|
private val context: Context,
|
||||||
private val database: AppDatabase
|
private val database: AppDatabase
|
||||||
) {
|
) {
|
||||||
fun buildShareIntent(characterId: Long): Intent {
|
fun buildCharacterProto(characterId: Long): Character = runBlocking {
|
||||||
return runBlocking {
|
|
||||||
val characterWithSprites = database.userCharacterDao().getCharacterWithSprites(characterId)
|
val characterWithSprites = database.userCharacterDao().getCharacterWithSprites(characterId)
|
||||||
val userCharacter = database.userCharacterDao().getCharacter(characterId)
|
val userCharacter = database.userCharacterDao().getCharacter(characterId)
|
||||||
val card = database.cardDao().getCardByCharacterIdSync(characterId)
|
val card = database.cardDao().getCardByCharacterIdSync(characterId)
|
||||||
?: error("Card not found for character $characterId")
|
?: error("Card not found for character $characterId")
|
||||||
val cardProgress = database.cardProgressDao().getCardProgressSync(card.id) ?: 0
|
val cardProgress = database.cardProgressDao().getCardProgressSync(card.id) ?: 0
|
||||||
val proto = Character.newBuilder()
|
Character.newBuilder()
|
||||||
.setCardId(card.cardId)
|
.setCardId(card.cardId)
|
||||||
.setCardName(card.name)
|
.setCardName(card.name)
|
||||||
.setCharacterStats(
|
.setCharacterStats(
|
||||||
@@ -61,6 +60,11 @@ class VitalWearCharacterExporter(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
.build()
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun buildShareIntent(characterId: Long): Intent {
|
||||||
|
return runBlocking {
|
||||||
|
val proto = buildCharacterProto(characterId)
|
||||||
|
|
||||||
val exportDir = File(context.cacheDir, "exports").apply { mkdirs() }
|
val exportDir = File(context.cacheDir, "exports").apply { mkdirs() }
|
||||||
val exportFile = File(exportDir, "vbhelper_character_$characterId.vitalwear")
|
val exportFile = File(exportDir, "vbhelper_character_$characterId.vitalwear")
|
||||||
|
|||||||
@@ -13,7 +13,27 @@ data class BitmapData (
|
|||||||
val bitmap: ByteArray,
|
val bitmap: ByteArray,
|
||||||
val width: Int,
|
val width: Int,
|
||||||
val height: Int
|
val height: Int
|
||||||
)
|
) {
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (this === other) return true
|
||||||
|
if (javaClass != other?.javaClass) return false
|
||||||
|
|
||||||
|
other as BitmapData
|
||||||
|
|
||||||
|
if (!bitmap.contentEquals(other.bitmap)) return false
|
||||||
|
if (width != other.width) return false
|
||||||
|
if (height != other.height) return false
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
var result = bitmap.contentHashCode()
|
||||||
|
result = 31 * result + width
|
||||||
|
result = 31 * result + height
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
data class ImageBitmapData(
|
data class ImageBitmapData(
|
||||||
val imageBitmap: ImageBitmap,
|
val imageBitmap: ImageBitmap,
|
||||||
|
|||||||
@@ -23,6 +23,13 @@ message Character {
|
|||||||
int32 total_wins = 13;
|
int32 total_wins = 13;
|
||||||
int32 current_phase_wins = 14;
|
int32 current_phase_wins = 14;
|
||||||
int32 mood = 15;
|
int32 mood = 15;
|
||||||
|
|
||||||
|
enum TransferDeviceType {
|
||||||
|
TRANSFER_DEVICE_TYPE_UNSPECIFIED = 0;
|
||||||
|
TRANSFER_DEVICE_TYPE_VB = 1;
|
||||||
|
TRANSFER_DEVICE_TYPE_BE = 2;
|
||||||
|
}
|
||||||
|
TransferDeviceType transfer_device_type = 16;
|
||||||
}
|
}
|
||||||
CharacterStats character_stats = 3;
|
CharacterStats character_stats = 3;
|
||||||
|
|
||||||
|
|||||||
@@ -17,21 +17,21 @@
|
|||||||
<string name="nav_adventure">アドベンチャー</string>
|
<string name="nav_adventure">アドベンチャー</string>
|
||||||
<string name="nav_credits">クレジット</string>
|
<string name="nav_credits">クレジット</string>
|
||||||
<string name="adventure_title">アドベンチャー</string>
|
<string name="adventure_title">アドベンチャー</string>
|
||||||
<string name="adventure_empty_state">何もありません。</string>
|
<string name="adventure_empty_state">No Data</string>
|
||||||
<string name="home_title">VB Helper</string>
|
<string name="home_title">VB Helper</string>
|
||||||
<string name="scan_secrets_not_imported">"APKの読み込みがされていません。設定から読み込をして下さい。 "</string>
|
<string name="scan_secrets_not_imported">"APKの読み込みがされていません。設定から読み込をして下さい。 "</string>
|
||||||
<string name="scan_title">デバイスをスキャン</string>
|
<string name="scan_title">デバイスをスキャン</string>
|
||||||
<string name="scan_sent_dim_success">DIMの転送に成功しました。</string>
|
<string name="scan_sent_dim_success">DIMの転送に成功しました。</string>
|
||||||
<string name="scan_nfc_must_be_enabled">NFC機能を有効にして下さい。</string>
|
<string name="scan_nfc_must_be_enabled">NFC機能を有効にして下さい。</string>
|
||||||
<string name="settings_title">設定</string>
|
<string name="settings_title">設定</string>
|
||||||
<string name="scan_missing_secrets">必要なデータが不足しています。設定を開き、Vital ArenaのAPKを読み込んでください。</string>
|
<string name="scan_missing_secrets">Vital ArenaのAPKを読み込みして下さい。</string>
|
||||||
<string name="scan_sent_character_success">転送が成功しました。</string>
|
<string name="scan_sent_character_success">転送が成功しました。</string>
|
||||||
<string name="scan_error_generic">エラー</string>
|
<string name="scan_error_generic">エラー</string>
|
||||||
<string name="settings_about_desc">このアプリについて</string>
|
<string name="settings_about_desc">このアプリについて</string>
|
||||||
<string name="settings_export_data_title">データベースの書き出し</string>
|
<string name="settings_export_data_title">データベースのエクスポート</string>
|
||||||
<string name="settings_export_data_desc">" データベースを書き出します。"</string>
|
<string name="settings_export_data_desc">データベースの書き出しをします。</string>
|
||||||
<string name="settings_import_data_title">データベースの読み込み</string>
|
<string name="settings_import_data_title">データベースのインポート</string>
|
||||||
<string name="settings_import_data_desc">" データベースを読み込みます。"</string>
|
<string name="settings_import_data_desc">データベースの読み込みをします。</string>
|
||||||
<string name="credits_title">クレジット</string>
|
<string name="credits_title">クレジット</string>
|
||||||
<string name="battles_coming_soon">Coming soon</string>
|
<string name="battles_coming_soon">Coming soon</string>
|
||||||
<string name="cards_my_cards_title">カードリスト</string>
|
<string name="cards_my_cards_title">カードリスト</string>
|
||||||
@@ -42,46 +42,44 @@
|
|||||||
<string name="scan_vb_to_app">デバイスからアプリへ</string>
|
<string name="scan_vb_to_app">デバイスからアプリへ</string>
|
||||||
<string name="scan_app_to_vb">アプリからデバイスへ</string>
|
<string name="scan_app_to_vb">アプリからデバイスへ</string>
|
||||||
<string name="read_character_title">キャラクターを読み込みます。</string>
|
<string name="read_character_title">キャラクターを読み込みます。</string>
|
||||||
<string name="read_character_prepare_device">バイタルブレスを準備してください。</string>
|
<string name="read_character_prepare_device">デバイスを準備してください。</string>
|
||||||
<string name="read_character_go_to_connect">準備が出来たら転送を押してください。</string>
|
<string name="read_character_go_to_connect">準備が出来たら転送を押してください。</string>
|
||||||
<string name="read_character_confirm">転送</string>
|
<string name="read_character_confirm">転送</string>
|
||||||
<string name="card_adventure_missions_title">アドベンチャーミッション</string>
|
<string name="card_adventure_missions_title">アドベンチャーミッション</string>
|
||||||
<string name="card_entry_characters_obtained">%1$d / %2$d のキャラクターを取得しました。</string>
|
<string name="card_entry_characters_obtained">%1$d / %2$d のキャラクターを取得しました。</string>
|
||||||
<string name="card_entry_edit">編集</string>
|
<string name="card_entry_edit">編集</string>
|
||||||
<string name="card_entry_edit">編集</string>
|
|
||||||
<string name="card_entry_edit">編集</string>
|
|
||||||
<string name="card_entry_edit">編集</string>
|
|
||||||
<string name="card_entry_delete">削除</string>
|
<string name="card_entry_delete">削除</string>
|
||||||
<string name="home_vbdim_current_phase_win">フェーズ勝率 %</string>
|
<string name="home_vbdim_current_phase_win">フェーズ勝率 %</string>
|
||||||
<string name="home_vbdim_special_missions">スペシャルミッション</string>
|
<string name="home_vbdim_special_missions">スペシャルミッション</string>
|
||||||
<string name="storage_delete_character">削除</string>
|
<string name="storage_delete_character">削除</string>
|
||||||
<string name="storage_close">閉じる</string>
|
<string name="storage_close">閉じる</string>
|
||||||
<string name="storage_character_image_description">キャラクター画像</string>
|
<string name="storage_character_image_description">キャラクター画像</string>
|
||||||
<string name="storage_send_to_watch">転送</string>
|
<string name="storage_send_to_watch">デバイスへ転送</string>
|
||||||
|
<string name="storage_send_to_vitalwear">VitalWearへ転送</string>
|
||||||
<string name="storage_set_active">選択</string>
|
<string name="storage_set_active">選択</string>
|
||||||
<string name="storage_send_on_adventure">アドベンチャーへ出発</string>
|
<string name="storage_send_on_adventure">アドベンチャーへ出発</string>
|
||||||
<string name="scan_no_nfc_on_device">NFC機能が見つかりません。</string>
|
<string name="scan_no_nfc_on_device">NFC機能が見つかりません。</string>
|
||||||
<string name="scan_tag_not_vb">検出されたタグはバイタルブレスでは使用出来ません。</string>
|
<string name="scan_tag_not_vb">検出されたタグはバイタルブレスでは使用出来ません。</string>
|
||||||
<string name="settings_section_nfc">NFC</string>
|
<string name="settings_section_nfc">NFC通信</string>
|
||||||
<string name="settings_section_dim_bem">DiM / BEm</string>
|
<string name="settings_section_dim_bem">DiM / BEm管理</string>
|
||||||
<string name="settings_section_data">データ管理</string>
|
<string name="settings_section_data">データ管理</string>
|
||||||
<string name="settings_import_apk_title">アプリの読み込み</string>
|
<string name="settings_import_apk_title">アプリのインポート</string>
|
||||||
<string name="settings_import_apk_desc">" Vital Arena 2.1.0のAPKファイルを読み込みします。"</string>
|
<string name="settings_import_apk_desc">Vital Arena 2.1.0のAPKファイルを読み込みします。</string>
|
||||||
<string name="settings_import_card_title">カードの読み込み</string>
|
<string name="settings_import_card_title">カードのインポート</string>
|
||||||
<string name="settings_section_about">概要 / クレジット</string>
|
<string name="settings_section_about">概要 / クレジット</string>
|
||||||
<string name="dex_chara_fusions_button">ジョグレス</string>
|
<string name="dex_chara_fusions_button">ジョグレス</string>
|
||||||
<string name="dex_chara_close_button">閉じる</string>
|
<string name="dex_chara_close_button">閉じる</string>
|
||||||
<string name="settings_import_card_desc">" Dim / BEm カードファイルを読み込みます。"</string>
|
<string name="settings_import_card_desc">Dim / BEm のデータファイルを読み込みします。</string>
|
||||||
<string name="settings_credits_title">クレジット</string>
|
<string name="settings_credits_title">クレジット</string>
|
||||||
<string name="settings_credits_desc">開発者について</string>
|
<string name="settings_credits_desc">開発者について</string>
|
||||||
<string name="settings_about_title">概要</string>
|
<string name="settings_about_title">概要</string>
|
||||||
<string name="beta_warning_message_main">" このアプリは現在アルファ版です。今後のアップデートにより保存されているデータの全てが削除される可能性がある為、大切なデータの保存はしないようにお願い致します。ご不便をおかけして申し訳ございません。 "</string>
|
<string name="beta_warning_message_main">このアプリは現在開発中です。\n今後のアップデートにより、保存されているデータ全ての削除が必要になる場合があります。\nその為、大切なデータの保存には使用しないようお願い致します。\nご不便をおかけして申し訳ございません。</string>
|
||||||
<string name="beta_warning_message_compatibility">"このアプリはオリジナルのVBおよびVHで動作するようになりました。 "</string>
|
<string name="beta_warning_message_compatibility">"このアプリはオリジナルのVBおよびVHで動作するようになりました。 "</string>
|
||||||
<string name="beta_warning_message_thanks">" ご理解とご協力に感謝いたします。\n 開発チーム一同"</string>
|
<string name="beta_warning_message_thanks">ご理解とご協力に感謝致します。\n開発チーム一同</string>
|
||||||
<string name="home_vbdim_vitals">バイタル</string>
|
<string name="home_vbdim_vitals">バイタル</string>
|
||||||
<string name="home_adventure_mission_finished">"1人のキャラクターがアドベンチャーミッションを完了しました。 "</string>
|
<string name="home_adventure_mission_finished">"1人のキャラクターがアドベンチャーミッションを完了しました。 "</string>
|
||||||
<string name="scan_secrets_not_initialized">"初期化が出来ませんでした。もう一度試して下さい。 "</string>
|
<string name="scan_secrets_not_initialized">"初期化が出来ませんでした。もう一度試して下さい。 "</string>
|
||||||
<string name="home_special_mission_delete_main">現在の進捗状況が失われますが、本当にこのスペシャルミッションを削除しますか?</string>
|
<string name="home_special_mission_delete_main">現在の進捗状況が失われます。本当にこのミッションを削除しますか?</string>
|
||||||
<string name="home_special_mission_delete_dismiss">閉じる</string>
|
<string name="home_special_mission_delete_dismiss">閉じる</string>
|
||||||
<string name="home_special_mission_delete_remove">削除</string>
|
<string name="home_special_mission_delete_remove">削除</string>
|
||||||
<string name="item_dialog_use">アイテム使用</string>
|
<string name="item_dialog_use">アイテム使用</string>
|
||||||
@@ -92,28 +90,27 @@
|
|||||||
<string name="battles_online_title">オンラインバトル</string>
|
<string name="battles_online_title">オンラインバトル</string>
|
||||||
<string name="obtained_item_you_also_got_credits">%1$d クレジットを獲得しました。</string>
|
<string name="obtained_item_you_also_got_credits">%1$d クレジットを獲得しました。</string>
|
||||||
<string name="obtained_item_dismiss">閉じる</string>
|
<string name="obtained_item_dismiss">閉じる</string>
|
||||||
<string name="obtained_item_dismiss">閉じる</string>
|
|
||||||
<string name="nav_dex">図鑑</string>
|
<string name="nav_dex">図鑑</string>
|
||||||
<string name="action_place_near_reader">バイタルブレス本体を近くに置いて下さい。</string>
|
<string name="credits_RedEyez_description">献身的なデバッグ作業を行って頂きました。</string>
|
||||||
|
<string name="action_place_near_reader">デバイスを近づけて下さい。</string>
|
||||||
<string name="action_cancel">キャンセル</string>
|
<string name="action_cancel">キャンセル</string>
|
||||||
<string name="card_view_discovered_characters">解放済みキャラクター</string>
|
<string name="card_view_discovered_characters">解放済みキャラクター</string>
|
||||||
<string name="dex_chara_icon_description">キャラクターアイコン</string>
|
<string name="dex_chara_icon_description">キャラクターアイコン</string>
|
||||||
<string name="dex_chara_name_icon_description">名前</string>
|
<string name="dex_chara_name_icon_description">名前</string>
|
||||||
<string name="credits_section_reverse_engineering">リバースエンジニアリング</string>
|
<string name="credits_section_reverse_engineering">プログラム解析</string>
|
||||||
<string name="credits_section_reverse_engineering">リバースエンジニアリング</string>
|
|
||||||
<string name="credits_section_app_development">アプリケーション開発</string>
|
<string name="credits_section_app_development">アプリケーション開発</string>
|
||||||
<string name="credits_cyanic_description">" ファームウェアの解析を行い、開発をサポートしてくれました。"</string>
|
<string name="credits_cyanic_description">ファームウェアの解析を行い、開発をサポートしてくれました。</string>
|
||||||
<string name="credits_cfogrady_description">" vb-lib-nfc開発およびアプリの開発に協力して頂きました。"</string>
|
<string name="credits_cfogrady_description">vb-lib-nfc開発および本アプリの開発に協力して頂きました。</string>
|
||||||
<string name="credits_nacabaro_description">" アプリの開発をしました。"</string>
|
<string name="credits_nacabaro_description">本アプリの開発をしました。</string>
|
||||||
<string name="credits_lightheel_description">" アプリのサーバー含む対戦機能を開発して頂きました。現在も開発を継続して頂いています。"</string>
|
<string name="credits_lightheel_description">本アプリのサーバー含む対戦機能を開発して頂きました。現在も開発を継続して頂いています。</string>
|
||||||
<string name="credits_shvstrz_description">" アプリのアイコンデザインをして頂きました。"</string>
|
<string name="credits_shvstrz_description">本アプリのアイコンデザインをして頂きました。</string>
|
||||||
<string name="write_card_title">カード情報書き込み中</string>
|
<string name="write_card_title">カード情報書き込み中</string>
|
||||||
<string name="write_card_icon_description">カードアイコン</string>
|
<string name="write_card_icon_description">カードアイコン</string>
|
||||||
<string name="write_card_device_ready">デバイスの準備をして下さい。</string>
|
<string name="write_card_device_ready">デバイスの準備をして下さい。</string>
|
||||||
<string name="write_card_required_card">%1$s カードが必要です。</string>
|
<string name="write_card_required_card">%1$s カードが必要です。</string>
|
||||||
<string name="write_card_confirm">転送</string>
|
<string name="write_card_confirm">転送</string>
|
||||||
<string name="write_character_success">" カードの読み込みが完了しました。"</string>
|
<string name="write_character_success">カードの読み込みが完了しました。</string>
|
||||||
<string name="write_character_wait_ready">" デバイスの準備をしてから確認を押して下さい。"</string>
|
<string name="write_character_wait_ready">デバイスの準備をしてから確認を押して下さい。</string>
|
||||||
<string name="write_character_confirm">確認</string>
|
<string name="write_character_confirm">確認</string>
|
||||||
<string name="home_vbdim_trophies">トロフィー</string>
|
<string name="home_vbdim_trophies">トロフィー</string>
|
||||||
<string name="items_purchase_success">購入しました。</string>
|
<string name="items_purchase_success">購入しました。</string>
|
||||||
@@ -124,10 +121,10 @@
|
|||||||
<string name="dex_chara_stats">HP: %1$d, BP: %2$d, AP: %3$d</string>
|
<string name="dex_chara_stats">HP: %1$d, BP: %2$d, AP: %3$d</string>
|
||||||
<string name="dex_chara_stage_attribute_unknown">Stg: -, Atr: -</string>
|
<string name="dex_chara_stage_attribute_unknown">Stg: -, Atr: -</string>
|
||||||
<string name="dex_chara_stats_unknown">HP: -, BP: -, AP: -</string>
|
<string name="dex_chara_stats_unknown">HP: -, BP: -, AP: -</string>
|
||||||
<string name="dex_chara_requirements">" Tr: %1$d; Bt: %2$d; Vr: %3$d; Wr: %4$d%%; Ct: %5$dh "</string>
|
<string name="dex_chara_requirements">\n Tr: %1$d; Bt: %2$d; Vr: %3$d; Wr: %4$d%%; Ct: %5$dh</string>
|
||||||
<string name="dex_chara_adventure_level">AdvLvl %1$d</string>
|
<string name="dex_chara_adventure_level">AdvLvl %1$d</string>
|
||||||
<string name="storage_my_characters_title">キャラクターリスト</string>
|
<string name="storage_my_characters_title">キャラクターリスト</string>
|
||||||
<string name="storage_nothing_to_see_here">表示するデータはありません。</string>
|
<string name="storage_nothing_to_see_here">No Data</string>
|
||||||
<string name="storage_in_adventure_toast">このキャラクターは冒険中です。</string>
|
<string name="storage_in_adventure_toast">このキャラクターは冒険中です。</string>
|
||||||
<string name="home_vbdim_next_timer">次のタイマー</string>
|
<string name="home_vbdim_next_timer">次のタイマー</string>
|
||||||
<string name="home_vbdim_total_battle_win">総勝率 %</string>
|
<string name="home_vbdim_total_battle_win">総勝率 %</string>
|
||||||
@@ -148,4 +145,17 @@
|
|||||||
<string name="special_mission_steps_progress">歩数 %1$d 歩</string>
|
<string name="special_mission_steps_progress">歩数 %1$d 歩</string>
|
||||||
<string name="special_mission_battles_progress">バトル %1$d 回</string>
|
<string name="special_mission_battles_progress">バトル %1$d 回</string>
|
||||||
<string name="special_mission_wins_progress">勝利 %1$d 回</string>
|
<string name="special_mission_wins_progress">勝利 %1$d 回</string>
|
||||||
|
<string name="settings_companion_import_card_image_title">カードのインポート</string>
|
||||||
|
<string name="companion_validation_connect_vb">デバイスに接続</string>
|
||||||
|
<string name="companion_card_import_success">カードのインポートに成功しました。</string>
|
||||||
|
<string name="companion_firmware_no_watch">デバイスが見つかりません。</string>
|
||||||
|
<string name="companion_validation_insert_card">デバイスへカードを差し込んで下さい。</string>
|
||||||
|
<string name="companion_validation_success">データチェックが完了しました。</string>
|
||||||
|
<string name="companion_loading_select_firmware">ファームウェアファイルを選択</string>
|
||||||
|
<string name="companion_firmware_sent_success">ファームウェアの送信が完了しました。</string>
|
||||||
|
<string name="companion_logs_receive_failed">ログの受信に失敗しました。</string>
|
||||||
|
<string name="companion_logs_failed_request">ログの要求に失敗しました。</string>
|
||||||
|
<string name="companion_logs_warning">注意:ログの転送には時間がかかる場合があります。</string>
|
||||||
|
<string name="settings_companion_send_watch_logs_title">ログの送信</string>
|
||||||
|
<string name="companion_transfer_disabled_message">現在、転送は無効になっています。</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -219,4 +219,17 @@
|
|||||||
<string name="home_special_mission_delete_dismiss">Dismiss</string>
|
<string name="home_special_mission_delete_dismiss">Dismiss</string>
|
||||||
<string name="home_special_mission_delete_remove">Remove</string>
|
<string name="home_special_mission_delete_remove">Remove</string>
|
||||||
|
|
||||||
|
<string name="settings_companion_import_card_image_title">Import Card Image</string>
|
||||||
|
<string name="companion_validation_connect_vb">Connect Vital Bracelet</string>
|
||||||
|
<string name="companion_card_import_success">Card imported successfully!</string>
|
||||||
|
<string name="companion_firmware_no_watch">No watch found!</string>
|
||||||
|
<string name="companion_validation_insert_card">Insert card into Vital Bracelet</string>
|
||||||
|
<string name="companion_validation_success">Validation successful!</string>
|
||||||
|
<string name="companion_loading_select_firmware">Select firmware file</string>
|
||||||
|
<string name="companion_firmware_sent_success">Firmware sent successfully!</string>
|
||||||
|
<string name="companion_logs_receive_failed">Failed to receive logs</string>
|
||||||
|
<string name="companion_logs_failed_request">Failed to request logs</string>
|
||||||
|
<string name="companion_logs_warning">Warning: Log transfer might take a while</string>
|
||||||
|
<string name="settings_companion_send_watch_logs_title">Send Watch Logs</string>
|
||||||
|
<string name="companion_transfer_disabled_message">Transfer is currently disabled</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
+2
-6
@@ -3,10 +3,6 @@ plugins {
|
|||||||
alias(libs.plugins.android.application) apply false
|
alias(libs.plugins.android.application) apply false
|
||||||
alias(libs.plugins.kotlin.android) apply false
|
alias(libs.plugins.kotlin.android) apply false
|
||||||
alias(libs.plugins.kotlin.compose) apply false
|
alias(libs.plugins.kotlin.compose) apply false
|
||||||
}
|
alias(libs.plugins.vksp) apply false
|
||||||
|
alias(libs.plugins.vprotobuf) apply false
|
||||||
buildscript {
|
|
||||||
dependencies {
|
|
||||||
classpath(libs.protobuf.gradle.plugin)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -21,3 +21,6 @@ kotlin.code.style=official
|
|||||||
# resources declared in the library itself and none from the library's dependencies,
|
# resources declared in the library itself and none from the library's dependencies,
|
||||||
# thereby reducing the size of the R class for that library
|
# thereby reducing the size of the R class for that library
|
||||||
android.nonTransitiveRClass=true
|
android.nonTransitiveRClass=true
|
||||||
|
android.uniquePackageNames=false
|
||||||
|
android.dependency.useConstraints=true
|
||||||
|
android.r8.strictFullModeForKeepRules=false
|
||||||
|
|||||||
+31
-13
@@ -1,22 +1,30 @@
|
|||||||
[versions]
|
[versions]
|
||||||
agp = "8.13.2"
|
agp = "9.2.1"
|
||||||
datastore = "1.2.0"
|
datastore = "1.2.1"
|
||||||
datastorePreferences = "1.2.0"
|
datastorePreferences = "1.2.1"
|
||||||
kotlin = "2.3.0"
|
kotlin = "2.4.0"
|
||||||
coreKtx = "1.17.0"
|
coreKtx = "1.19.0"
|
||||||
junit = "4.13.2"
|
junit = "4.13.2"
|
||||||
junitVersion = "1.3.0"
|
junitVersion = "1.3.0"
|
||||||
espressoCore = "3.7.0"
|
espressoCore = "3.7.0"
|
||||||
lifecycleRuntimeKtx = "2.10.0"
|
lifecycleRuntimeKtx = "2.11.0"
|
||||||
activityCompose = "1.12.2"
|
activityCompose = "1.13.0"
|
||||||
composeBom = "2026.01.00"
|
composeBom = "2026.06.00"
|
||||||
material = "1.13.0"
|
material = "1.14.0"
|
||||||
navigationCompose = "2.9.6"
|
navigationCompose = "2.9.8"
|
||||||
protobufGradlePlugin = "0.9.6"
|
protobufGradlePlugin = "0.10.0"
|
||||||
protobufJavalite = "4.33.4"
|
protobufJavalite = "4.35.1"
|
||||||
roomRuntime = "2.8.4"
|
roomRuntime = "2.8.4"
|
||||||
vbNfcReader = "0.2.0-SNAPSHOT"
|
vbNfcReader = "0.2.0-SNAPSHOT"
|
||||||
dimReader = "2.1.0"
|
dimReader = "2.1.0"
|
||||||
|
playServicesWearable = "20.0.1"
|
||||||
|
kotlinxCoroutinesPlayServices = "1.11.0"
|
||||||
|
timber = "5.0.1"
|
||||||
|
tinylog = "2.7.0"
|
||||||
|
retrofit = "3.0.0"
|
||||||
|
gson = "2.14.0"
|
||||||
|
okhttp = "5.4.0"
|
||||||
|
ksp = "2.3.9"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
androidx-compose-material = { module = "androidx.compose.material:material" }
|
androidx-compose-material = { module = "androidx.compose.material:material" }
|
||||||
@@ -42,13 +50,23 @@ androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-man
|
|||||||
androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
|
androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
|
||||||
androidx-material3 = { group = "androidx.compose.material3", name = "material3" }
|
androidx-material3 = { group = "androidx.compose.material3", name = "material3" }
|
||||||
material = { module = "com.google.android.material:material", version.ref = "material" }
|
material = { module = "com.google.android.material:material", version.ref = "material" }
|
||||||
protobuf-gradle-plugin = { module = "com.google.protobuf:protobuf-gradle-plugin", version.ref = "protobufGradlePlugin" }
|
|
||||||
protobuf-javalite = { module = "com.google.protobuf:protobuf-javalite", version.ref = "protobufJavalite" }
|
protobuf-javalite = { module = "com.google.protobuf:protobuf-javalite", version.ref = "protobufJavalite" }
|
||||||
vb-nfc-reader = { module = "com.github.cfogrady:vb-nfc-reader", version.ref = "vbNfcReader" }
|
vb-nfc-reader = { module = "com.github.cfogrady:vb-nfc-reader", version.ref = "vbNfcReader" }
|
||||||
dim-reader = { module = "com.github.cfogrady:vb-dim-reader", version.ref = "dimReader" }
|
dim-reader = { module = "com.github.cfogrady:vb-dim-reader", version.ref = "dimReader" }
|
||||||
|
play-services-wearable = { group = "com.google.android.gms", name = "play-services-wearable", version.ref = "playServicesWearable" }
|
||||||
|
kotlinx-coroutines-play-services = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-play-services", version.ref = "kotlinxCoroutinesPlayServices" }
|
||||||
|
timber = { module = "com.jakewharton.timber:timber", version.ref = "timber" }
|
||||||
|
tinylog-api = { module = "org.tinylog:tinylog-api", version.ref = "tinylog" }
|
||||||
|
tinylog-impl = { module = "org.tinylog:tinylog-impl", version.ref = "tinylog" }
|
||||||
|
retrofit = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofit" }
|
||||||
|
retrofit-converter-gson = { module = "com.squareup.retrofit2:converter-gson", version.ref = "retrofit" }
|
||||||
|
gson = { module = "com.google.code.gson:gson", version.ref = "gson" }
|
||||||
|
okhttp-logging-interceptor = { module = "com.squareup.okhttp3:logging-interceptor", version.ref = "okhttp" }
|
||||||
|
|
||||||
[plugins]
|
[plugins]
|
||||||
android-application = { id = "com.android.application", version.ref = "agp" }
|
android-application = { id = "com.android.application", version.ref = "agp" }
|
||||||
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
|
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
|
||||||
kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
|
kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
|
||||||
|
vksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
|
||||||
|
vprotobuf = { id = "com.google.protobuf", version.ref = "protobufGradlePlugin" }
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
#Tue Dec 24 10:55:57 UTC 2024
|
#Tue Dec 24 10:55:57 UTC 2024
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-9.6.1-bin.zip
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ dependencyResolutionManagement {
|
|||||||
mavenLocal()
|
mavenLocal()
|
||||||
google()
|
google()
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
maven { url = uri("https://jitpack.io") }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user