diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml
index d3d8fc51..c11a0735 100644
--- a/android/AndroidManifest.xml
+++ b/android/AndroidManifest.xml
@@ -6,6 +6,7 @@
+
, grantResults: IntArray) {
+ if (requestCode == REQ_PERMISSION_EXTERNAL_STORAGE && grantResults[0] == PERMISSION_GRANTED) {
+ saveFolderHelper.chooseFolder()
+ }
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults)
+ }
+
+ override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+ if (requestCode == REQ_SAVE_FOLDER && resultCode == Activity.RESULT_OK) {
+ Log.d("Unciv", "Intent data: ${intent?.data}")
+ }
+ super.onActivityResult(requestCode, resultCode, data)
+ }
}
\ No newline at end of file
diff --git a/android/src/com/unciv/app/SaveFolderHelperAndroid.kt b/android/src/com/unciv/app/SaveFolderHelperAndroid.kt
index 1d3a59b8..d845b9ad 100644
--- a/android/src/com/unciv/app/SaveFolderHelperAndroid.kt
+++ b/android/src/com/unciv/app/SaveFolderHelperAndroid.kt
@@ -1,25 +1,27 @@
package com.unciv.app
+import android.Manifest.permission.WRITE_EXTERNAL_STORAGE
import android.app.Activity
import android.content.Intent
import android.content.Intent.ACTION_OPEN_DOCUMENT_TREE
import android.os.Build
import androidx.annotation.RequiresApi
+import androidx.core.content.PermissionChecker
+import androidx.core.content.PermissionChecker.PERMISSION_GRANTED
import com.badlogic.gdx.Gdx
import com.badlogic.gdx.files.FileHandle
-import com.unciv.logic.GameSaver
-import com.unciv.logic.GameSaver.getSubfolder
-import com.unciv.logic.SaveFolderHelper
+import com.unciv.logic.SaveFolderHelperInternal
import com.unciv.models.metadata.GameSettings
import java.io.File
const val REQ_SAVE_FOLDER = 1
+const val REQ_PERMISSION_EXTERNAL_STORAGE = 2
-class SaveFolderHelperAndroid(private val activity: Activity) : SaveFolderHelper {
+class SaveFolderHelperAndroid(private val activity: Activity) : SaveFolderHelperInternal() {
/** When set, we know we're on Android and can save to the app's personal external file directory
* Only allow mods on KK+, to avoid READ_EXTERNAL_STORAGE permission earlier versions need
* See https://developer.android.com/training/data-storage/app-specific#external-access-files */
- val externalFilesDir = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+ private val externalFilesDir = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
copyMods(activity)
activity.getExternalFilesDir(null)?.path ?: ""
} else ""
@@ -27,7 +29,7 @@ class SaveFolderHelperAndroid(private val activity: Activity) : SaveFolderHelper
override fun canChooseFolder(): Boolean = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP
override fun getSave(settings: GameSettings, GameName: String, multiplayer: Boolean): FileHandle {
- val localfile = Gdx.files.local("${getSubfolder(multiplayer)}/$GameName")
+ val localfile = super.getSave(settings, GameName, multiplayer)
if (externalFilesDir == "" || !Gdx.files.isExternalStorageAvailable) return localfile
val externalFile = Gdx.files.absolute(externalFilesDir + "/${getSubfolder(multiplayer)}/$GameName")
if (localfile.exists() && !externalFile.exists()) return localfile
@@ -42,6 +44,11 @@ class SaveFolderHelperAndroid(private val activity: Activity) : SaveFolderHelper
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
override fun chooseFolder(): FileHandle? {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
+ && PermissionChecker.checkSelfPermission(activity, WRITE_EXTERNAL_STORAGE) != PERMISSION_GRANTED) {
+ activity.requestPermissions(arrayOf(WRITE_EXTERNAL_STORAGE), REQ_PERMISSION_EXTERNAL_STORAGE)
+ return null
+ }
activity.startActivityForResult(Intent(ACTION_OPEN_DOCUMENT_TREE), REQ_SAVE_FOLDER)
return null
}
diff --git a/core/src/com/unciv/UncivGame.kt b/core/src/com/unciv/UncivGame.kt
index ce73f44b..fd00de51 100644
--- a/core/src/com/unciv/UncivGame.kt
+++ b/core/src/com/unciv/UncivGame.kt
@@ -13,7 +13,10 @@ import com.unciv.models.metadata.GameSettings
import com.unciv.models.ruleset.RulesetCache
import com.unciv.models.translations.Translations
import com.unciv.ui.LanguagePickerScreen
-import com.unciv.ui.utils.*
+import com.unciv.ui.utils.CameraStageBaseScreen
+import com.unciv.ui.utils.CrashController
+import com.unciv.ui.utils.ImageGetter
+import com.unciv.ui.utils.center
import com.unciv.ui.worldscreen.WorldScreen
import java.util.*
import kotlin.concurrent.thread
@@ -91,17 +94,21 @@ class UncivGame(parameters: UncivGameParameters) : Game() {
// This stuff needs to run on the main thread because it needs the GL context
Gdx.app.postRunnable {
ImageGetter.ruleset = RulesetCache.getBaseRuleset() // so that we can enter the map editor without having to load a game first
- thread(name="Music") { startMusic() }
+ thread(name = "Music") { startMusic() }
restoreSize()
if (settings.isFreshlyCreated) {
setScreen(LanguagePickerScreen())
- } else { setScreen(MainMenuScreen()) }
+ } else {
+ setScreen(MainMenuScreen())
+ }
isInitialized = true
}
}
crashController = CrashController.Impl(crashReportSender)
- GameSaver.saveFolderHelper = saveFolderHelper
+ saveFolderHelper?.let {
+ GameSaver.saveFolderHelper = it
+ }
}
fun restoreSize() {
diff --git a/core/src/com/unciv/logic/GameSaver.kt b/core/src/com/unciv/logic/GameSaver.kt
index 1be5c6c0..6b22701a 100644
--- a/core/src/com/unciv/logic/GameSaver.kt
+++ b/core/src/com/unciv/logic/GameSaver.kt
@@ -11,14 +11,14 @@ import java.io.File
import kotlin.concurrent.thread
object GameSaver {
- private const val saveFilesFolder = "SaveFiles"
- private const val multiplayerFilesFolder = "MultiplayerGames"
+ const val saveFilesFolder = "SaveFiles"
+ const val multiplayerFilesFolder = "MultiplayerGames"
private const val settingsFileName = "GameSettings.json"
- lateinit var saveFolderHelper: SaveFolderHelper
+
+ var saveFolderHelper: SaveFolderHelper = SaveFolderHelperInternal()
fun json() = Json().apply { setIgnoreDeprecated(true); ignoreUnknownFields = true } // Json() is NOT THREAD SAFE so we need to create a new one for each function
- fun getSubfolder(multiplayer: Boolean = false) = if (multiplayer) multiplayerFilesFolder else saveFilesFolder
fun getSave(GameName: String, multiplayer: Boolean = false): FileHandle = saveFolderHelper.getSave(getGeneralSettings(), GameName, multiplayer)
diff --git a/core/src/com/unciv/logic/SaveFolderHelper.kt b/core/src/com/unciv/logic/SaveFolderHelper.kt
index 100df0b6..6fef88a1 100644
--- a/core/src/com/unciv/logic/SaveFolderHelper.kt
+++ b/core/src/com/unciv/logic/SaveFolderHelper.kt
@@ -1,6 +1,9 @@
package com.unciv.logic
+import com.badlogic.gdx.Gdx
import com.badlogic.gdx.files.FileHandle
+import com.unciv.logic.GameSaver.multiplayerFilesFolder
+import com.unciv.logic.GameSaver.saveFilesFolder
import com.unciv.models.metadata.GameSettings
interface SaveFolderHelper {
@@ -8,4 +11,16 @@ interface SaveFolderHelper {
fun chooseFolder(): FileHandle?
fun getSave(settings: GameSettings, GameName: String, multiplayer: Boolean = false): FileHandle
fun getSaves(settings: GameSettings, multiplayer: Boolean = false): Sequence
+}
+
+open class SaveFolderHelperInternal : SaveFolderHelper {
+ override fun canChooseFolder(): Boolean = false
+
+ override fun chooseFolder(): FileHandle? = null
+
+ override fun getSave(settings: GameSettings, GameName: String, multiplayer: Boolean): FileHandle = Gdx.files.local("${getSubfolder(multiplayer)}/$GameName")
+
+ override fun getSaves(settings: GameSettings, multiplayer: Boolean): Sequence = Gdx.files.local(getSubfolder(multiplayer)).list().asSequence()
+
+ fun getSubfolder(multiplayer: Boolean = false) = if (multiplayer) multiplayerFilesFolder else saveFilesFolder
}
\ No newline at end of file
diff --git a/desktop/src/com/unciv/app/desktop/SaveFolderHelperDesktop.kt b/desktop/src/com/unciv/app/desktop/SaveFolderHelperDesktop.kt
index db274d5a..b8664195 100644
--- a/desktop/src/com/unciv/app/desktop/SaveFolderHelperDesktop.kt
+++ b/desktop/src/com/unciv/app/desktop/SaveFolderHelperDesktop.kt
@@ -2,15 +2,14 @@ package com.unciv.app.desktop
import com.badlogic.gdx.Gdx
import com.badlogic.gdx.files.FileHandle
-import com.unciv.logic.GameSaver
-import com.unciv.logic.SaveFolderHelper
+import com.unciv.logic.SaveFolderHelperInternal
import com.unciv.models.metadata.GameSettings
import java.awt.event.WindowEvent
import java.io.File
import javax.swing.JFileChooser
import javax.swing.JFrame
-class SaveFolderHelperDesktop: SaveFolderHelper {
+class SaveFolderHelperDesktop : SaveFolderHelperInternal() {
override fun chooseFolder(): FileHandle? {
val fileChooser = JFileChooser().apply fileChooser@{
fileSelectionMode = JFileChooser.DIRECTORIES_ONLY
@@ -30,19 +29,17 @@ class SaveFolderHelperDesktop: SaveFolderHelper {
override fun canChooseFolder(): Boolean = true
override fun getSave(settings: GameSettings, GameName: String, multiplayer: Boolean): FileHandle {
- val gamePath = "${GameSaver.getSubfolder(multiplayer)}/$GameName"
return settings.savesFolder?.let {
- Gdx.files.absolute("$it/$gamePath")
- }?: Gdx.files.local(gamePath)
+ Gdx.files.absolute("$it/${getSubfolder(multiplayer)}/$GameName")
+ } ?: super.getSave(settings, GameName, multiplayer)
}
override fun getSaves(settings: GameSettings, multiplayer: Boolean): Sequence {
- val savesPath = GameSaver.getSubfolder(multiplayer)
return settings.savesFolder?.let {
- val file = Gdx.files.absolute("$it/$savesPath")
+ val file = Gdx.files.absolute("$it/${getSubfolder(multiplayer)}")
file.mkdirs()
return file.list().asSequence()
- }?: Gdx.files.local(savesPath).list().asSequence()
+ } ?: super.getSaves(settings, multiplayer)
}
}
\ No newline at end of file