diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 7e4a241..02b7baf 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -92,4 +92,11 @@ dependencies {
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
+ implementation("com.squareup.okhttp3:logging-interceptor:4.11.0")
}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index d8da12c..8c72cce 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -7,6 +7,8 @@
+
()
- // Get the internal storage directory for attack sprites
- private fun getAttackTexturesPath(): String {
- return "battle_sprites/extracted_atksprites"
+ // Get the external storage directory for attack sprites
+ private fun getAttackTexturesBaseDir(): File {
+ val externalDir = android.os.Environment.getExternalStorageDirectory()
+ return File(externalDir, "VBHelper/battle_sprites/extracted_atksprites")
}
fun getAttackSprite(characterId: String, isLarge: Boolean = false): Bitmap? {
@@ -58,9 +59,8 @@ class AttackSpriteManager(private val context: Context) {
return null
}
- // Load the attack sprite from internal storage
- val attackFilePath = "${getAttackTexturesPath()}/$attackFileName.png"
- val attackFile = File(context.filesDir, attackFilePath)
+ // Load the attack sprite from external storage
+ val attackFile = File(getAttackTexturesBaseDir(), "$attackFileName.png")
println("AttackSpriteManager: Attack file path = ${attackFile.absolutePath}")
println("AttackSpriteManager: Attack file exists = ${attackFile.exists()}")
@@ -88,8 +88,9 @@ class AttackSpriteManager(private val context: Context) {
}
try {
- // Load character data from JSON file in internal storage
- val characterDataFile = File(context.filesDir, "battle_sprites/extracted_digimon_stats/character_data/CharacterData.json")
+ // Load character data from JSON file in external storage
+ val externalDir = android.os.Environment.getExternalStorageDirectory()
+ val characterDataFile = File(externalDir, "VBHelper/battle_sprites/extracted_digimon_stats/character_data/CharacterData.json")
println("AttackSpriteManager: Character data file path = ${characterDataFile.absolutePath}")
println("AttackSpriteManager: Character data file exists = ${characterDataFile.exists()}")
diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/battle/BattleSpriteManager.kt b/app/src/main/java/com/github/nacabaro/vbhelper/battle/BattleSpriteManager.kt
index 7f1708b..1f1224f 100644
--- a/app/src/main/java/com/github/nacabaro/vbhelper/battle/BattleSpriteManager.kt
+++ b/app/src/main/java/com/github/nacabaro/vbhelper/battle/BattleSpriteManager.kt
@@ -38,9 +38,10 @@ class BattleSpriteManager(private val context: Context) {
private val gson = Gson()
private val spriteCache = mutableMapOf()
- // Get the internal storage directory for sprite files
+ // Get the external storage directory for sprite files
private fun getSpriteBaseDir(): File {
- return File(context.filesDir, "battle_sprites/extracted_assets")
+ val externalDir = android.os.Environment.getExternalStorageDirectory()
+ return File(externalDir, "VBHelper/battle_sprites/extracted_assets")
}
fun loadSprite(spriteName: String, atlasName: String): Bitmap? {
diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/battle/HitEffectSpriteManager.kt b/app/src/main/java/com/github/nacabaro/vbhelper/battle/HitEffectSpriteManager.kt
index b7fabfa..c33205c 100644
--- a/app/src/main/java/com/github/nacabaro/vbhelper/battle/HitEffectSpriteManager.kt
+++ b/app/src/main/java/com/github/nacabaro/vbhelper/battle/HitEffectSpriteManager.kt
@@ -10,9 +10,10 @@ import java.io.File
class HitEffectSpriteManager(private val context: Context) {
private val spriteCache = mutableMapOf()
- // Get the internal storage directory for hit effect sprites
+ // Get the external storage directory for hit effect sprites
private fun getHitSpritesDir(): File {
- return File(context.filesDir, "battle_sprites/extracted_hit_sprites")
+ val externalDir = android.os.Environment.getExternalStorageDirectory()
+ return File(externalDir, "VBHelper/battle_sprites/extracted_hit_sprites")
}
/**
diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/battle/IndividualSpriteManager.kt b/app/src/main/java/com/github/nacabaro/vbhelper/battle/IndividualSpriteManager.kt
index 09a87e7..56b664f 100644
--- a/app/src/main/java/com/github/nacabaro/vbhelper/battle/IndividualSpriteManager.kt
+++ b/app/src/main/java/com/github/nacabaro/vbhelper/battle/IndividualSpriteManager.kt
@@ -9,9 +9,10 @@ import java.io.File
class IndividualSpriteManager(private val context: Context) {
private val spriteCache = mutableMapOf()
- // Get the internal storage directory for sprite files
+ // Get the external storage directory for sprite files
private fun getSpriteBaseDir(): File {
- return File(context.filesDir, "battle_sprites/extracted_assets/sprites")
+ val externalDir = android.os.Environment.getExternalStorageDirectory()
+ return File(externalDir, "VBHelper/battle_sprites/extracted_assets/sprites")
}
/**
diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/battle/SpriteFileManager.kt b/app/src/main/java/com/github/nacabaro/vbhelper/battle/SpriteFileManager.kt
index 1baa2cd..40a4db0 100644
--- a/app/src/main/java/com/github/nacabaro/vbhelper/battle/SpriteFileManager.kt
+++ b/app/src/main/java/com/github/nacabaro/vbhelper/battle/SpriteFileManager.kt
@@ -9,7 +9,7 @@ import java.io.FileInputStream
class SpriteFileManager(private val context: Context) {
// Get the external storage directory where files are already located
- private fun getExternalSpriteBaseDir(): File {
+ fun getExternalSpriteBaseDir(): File {
val externalDir = android.os.Environment.getExternalStorageDirectory()
return File(externalDir, "VBHelper/battle_sprites")
}
@@ -271,7 +271,7 @@ class SpriteFileManager(private val context: Context) {
}
fun checkSpriteFilesExist(): Boolean {
- val battleSpritesDir = getInternalSpriteBaseDir()
+ val battleSpritesDir = getExternalSpriteBaseDir()
val extractedAssetsDir = File(battleSpritesDir, "extracted_assets")
val extractedStatsDir = File(battleSpritesDir, "extracted_digimon_stats")
val atkspritesDir = File(battleSpritesDir, "extracted_atksprites")
@@ -283,7 +283,7 @@ class SpriteFileManager(private val context: Context) {
val atkspritesExist = atkspritesDir.exists() && atkspritesDir.listFiles()?.isNotEmpty() == true
val battlebgsExist = battlebgsDir.exists() && battlebgsDir.listFiles()?.isNotEmpty() == true
- println("Checking sprite files exist in internal storage:")
+ println("Checking sprite files exist in external storage:")
println(" battle_sprites exists: $battleSpritesExist")
println(" extracted_assets exists: $assetsExist")
println(" extracted_digimon_stats exists: $statsExist")
diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/BattlesScreen.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/BattlesScreen.kt
index 1da9a96..91271e8 100644
--- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/BattlesScreen.kt
+++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/BattlesScreen.kt
@@ -34,6 +34,16 @@ import androidx.compose.material3.DropdownMenuItem
import androidx.compose.foundation.layout.Box
import androidx.compose.ui.platform.LocalContext
import androidx.compose.runtime.LaunchedEffect
+import android.Manifest
+import android.content.pm.PackageManager
+import androidx.activity.compose.rememberLauncherForActivityResult
+import androidx.activity.result.contract.ActivityResultContracts
+import androidx.core.content.ContextCompat
+import androidx.activity.ComponentActivity
+import android.os.Build
+import android.provider.Settings
+import android.content.Intent
+import android.net.Uri
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.tween
import kotlinx.coroutines.delay
@@ -1542,6 +1552,92 @@ fun EnemyBattleView(
@Composable
fun BattlesScreen() {
val TAG = "BattleScreen"
+ val context = LocalContext.current
+ val activity = context as? ComponentActivity
+
+ // Permission state
+ var hasStoragePermission by remember { mutableStateOf(false) }
+
+ // Check if permission is already granted
+ // For Android 11+ (API 30+), check MANAGE_EXTERNAL_STORAGE
+ // For Android 10 and below, check READ_EXTERNAL_STORAGE
+ val permissionCheck = remember {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+ // Android 11+ - check for MANAGE_EXTERNAL_STORAGE
+ android.os.Environment.isExternalStorageManager()
+ } else {
+ // Android 10 and below - check for READ_EXTERNAL_STORAGE
+ ContextCompat.checkSelfPermission(
+ context,
+ Manifest.permission.READ_EXTERNAL_STORAGE
+ ) == PackageManager.PERMISSION_GRANTED
+ }
+ }
+
+ // Permission launcher for READ_EXTERNAL_STORAGE (Android 10 and below)
+ val readStoragePermissionLauncher = rememberLauncherForActivityResult(
+ contract = ActivityResultContracts.RequestPermission()
+ ) { isGranted: Boolean ->
+ hasStoragePermission = isGranted
+ if (isGranted) {
+ println("BATTLESCREEN: READ_EXTERNAL_STORAGE permission granted")
+ } else {
+ println("BATTLESCREEN: READ_EXTERNAL_STORAGE permission denied")
+ }
+ }
+
+ // Launcher for opening settings to grant MANAGE_EXTERNAL_STORAGE (Android 11+)
+ val manageStorageSettingsLauncher = rememberLauncherForActivityResult(
+ contract = ActivityResultContracts.StartActivityForResult()
+ ) {
+ // Re-check permission after returning from settings
+ val hasPermission = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+ android.os.Environment.isExternalStorageManager()
+ } else {
+ ContextCompat.checkSelfPermission(
+ context,
+ Manifest.permission.READ_EXTERNAL_STORAGE
+ ) == PackageManager.PERMISSION_GRANTED
+ }
+ hasStoragePermission = hasPermission
+ if (hasPermission) {
+ println("BATTLESCREEN: MANAGE_EXTERNAL_STORAGE permission granted")
+ } else {
+ println("BATTLESCREEN: MANAGE_EXTERNAL_STORAGE permission not granted")
+ }
+ }
+
+ // Initialize permission state
+ LaunchedEffect(Unit) {
+ hasStoragePermission = permissionCheck
+ if (!permissionCheck && activity != null) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+ // Android 11+ - need to request MANAGE_EXTERNAL_STORAGE
+ // This requires user to go to settings
+ println("BATTLESCREEN: Android 11+ detected - opening settings for MANAGE_EXTERNAL_STORAGE")
+ try {
+ val intent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION)
+ intent.data = Uri.parse("package:${context.packageName}")
+ manageStorageSettingsLauncher.launch(intent)
+ } catch (e: Exception) {
+ println("BATTLESCREEN: Error opening settings: ${e.message}")
+ // Fallback: try the general manage external storage settings
+ try {
+ val intent = Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION)
+ manageStorageSettingsLauncher.launch(intent)
+ } catch (e2: Exception) {
+ println("BATTLESCREEN: Error opening fallback settings: ${e2.message}")
+ }
+ }
+ } else {
+ // Android 10 and below - request READ_EXTERNAL_STORAGE
+ println("BATTLESCREEN: Requesting READ_EXTERNAL_STORAGE permission...")
+ readStoragePermissionLauncher.launch(Manifest.permission.READ_EXTERNAL_STORAGE)
+ }
+ } else if (permissionCheck) {
+ println("BATTLESCREEN: Storage permission already granted")
+ }
+ }
var currentView by remember { mutableStateOf("main") }
@@ -1601,8 +1697,6 @@ fun BattlesScreen() {
else -> rookieCharacters
}
- val context = LocalContext.current
-
// Determine if player can battle based on stage (derived from activeUserCharacter)
val canBattle = activeUserCharacter?.stage?.let { it >= 2 } ?: false
@@ -1650,15 +1744,19 @@ fun BattlesScreen() {
}
}
- // Initialize sprite files on first load
- LaunchedEffect(Unit) {
- println("BATTLESCREEN: LaunchedEffect triggered - checking sprite files...")
- val spriteFileManager = SpriteFileManager(context)
- if (!spriteFileManager.checkSpriteFilesExist()) {
- println("BATTLESCREEN: Copying sprite files from external storage to internal storage...")
- spriteFileManager.copySpriteFilesToInternalStorage()
+ // Initialize sprite files on first load - check that they exist in external storage
+ // Only check if permission is granted
+ LaunchedEffect(hasStoragePermission) {
+ if (hasStoragePermission) {
+ println("BATTLESCREEN: LaunchedEffect triggered - checking sprite files...")
+ val spriteFileManager = SpriteFileManager(context)
+ if (spriteFileManager.checkSpriteFilesExist()) {
+ println("BATTLESCREEN: Sprite files exist in external storage")
+ } else {
+ println("BATTLESCREEN: Sprite files not found in external storage")
+ }
} else {
- println("BATTLESCREEN: Sprite files already exist in internal storage")
+ println("BATTLESCREEN: Cannot check sprite files - storage permission not granted")
}
}
@@ -1672,10 +1770,12 @@ fun BattlesScreen() {
kotlinx.coroutines.withContext(Dispatchers.IO) {
// First, let's check all characters to see what's in the database
val allCharacters = database.userCharacterDao().getAllCharacters()
+ /*
println("BATTLESCREEN: Found ${allCharacters.size} total characters in database")
allCharacters.forEach { char ->
println(" - Character ID: ${char.id}, CharId: ${char.charId}")
}
+ */
val activeChar = database.userCharacterDao().getActiveCharacter()
println("BATTLESCREEN: getActiveCharacter() returned: $activeChar")
@@ -2211,10 +2311,11 @@ fun AnimatedBattleBackground(
println("DEBUG: Screen dimensions = ${screenWidth.value}x${screenHeight.value}dp")
}
- // Load background image from internal storage
+ // Load background image from external storage
LaunchedEffect(Unit) {
try {
- val backgroundFile = File(context.filesDir, "battle_sprites/extracted_battlebgs/BattleBg_0015_BattleBg_0012.png")
+ val externalDir = android.os.Environment.getExternalStorageDirectory()
+ val backgroundFile = File(externalDir, "VBHelper/battle_sprites/extracted_battlebgs/BattleBg_0015_BattleBg_0012.png")
if (backgroundFile.exists()) {
backgroundBitmap = BitmapFactory.decodeFile(backgroundFile.absolutePath)
println("Successfully loaded battle background: ${backgroundFile.absolutePath}")
@@ -2317,13 +2418,14 @@ fun MultiLayerAnimatedBattleBackground(
println("DEBUG: Multi-layer screen dimensions = ${screenWidth.value}x${screenHeight.value}dp")
}
- // Load all three background layers from internal storage
+ // Load all three background layers from external storage
LaunchedEffect(backgroundSetIndex) {
try {
+ val externalDir = android.os.Environment.getExternalStorageDirectory()
val selectedSet = backgroundSets[backgroundSetIndex]
// Back layer
- val backLayerFile = File(context.filesDir, "battle_sprites/extracted_battlebgs/${selectedSet.backLayer}")
+ val backLayerFile = File(externalDir, "VBHelper/battle_sprites/extracted_battlebgs/${selectedSet.backLayer}")
if (backLayerFile.exists()) {
backLayerBitmap = BitmapFactory.decodeFile(backLayerFile.absolutePath)
println("Successfully loaded back layer background (Set ${backgroundSetIndex + 1}): ${backLayerFile.absolutePath}")
@@ -2332,7 +2434,7 @@ fun MultiLayerAnimatedBattleBackground(
}
// Middle layer
- val middleLayerFile = File(context.filesDir, "battle_sprites/extracted_battlebgs/${selectedSet.middleLayer}")
+ val middleLayerFile = File(externalDir, "VBHelper/battle_sprites/extracted_battlebgs/${selectedSet.middleLayer}")
if (middleLayerFile.exists()) {
middleLayerBitmap = BitmapFactory.decodeFile(middleLayerFile.absolutePath)
println("Successfully loaded middle layer background (Set ${backgroundSetIndex + 1}): ${middleLayerFile.absolutePath}")
@@ -2341,7 +2443,7 @@ fun MultiLayerAnimatedBattleBackground(
}
// Front layer
- val frontLayerFile = File(context.filesDir, "battle_sprites/extracted_battlebgs/${selectedSet.frontLayer}")
+ val frontLayerFile = File(externalDir, "VBHelper/battle_sprites/extracted_battlebgs/${selectedSet.frontLayer}")
if (frontLayerFile.exists()) {
frontLayerBitmap = BitmapFactory.decodeFile(frontLayerFile.absolutePath)
println("Successfully loaded front layer background (Set ${backgroundSetIndex + 1}): ${frontLayerFile.absolutePath}")