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 (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && requestCode == REQ_WRITE_STORAGE && grantResults[0] == PERMISSION_GRANTED) {
+ customSaveLocationHelper?.continuePreviousRequest()
+ }
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults)
+ }
+
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
// This should only happen on API 19+ but it's wrapped in the if check to keep the
diff --git a/android/src/com/unciv/app/CustomSaveLocationHelperAndroid.kt b/android/src/com/unciv/app/CustomSaveLocationHelperAndroid.kt
index 146f8f87..e5fb2279 100644
--- a/android/src/com/unciv/app/CustomSaveLocationHelperAndroid.kt
+++ b/android/src/com/unciv/app/CustomSaveLocationHelperAndroid.kt
@@ -1,10 +1,13 @@
package com.unciv.app
+import android.Manifest.permission.WRITE_EXTERNAL_STORAGE
import android.app.Activity
import android.content.Intent
+import android.content.pm.PackageManager.PERMISSION_GRANTED
import android.net.Uri
import android.os.Build
import androidx.annotation.RequiresApi
+import androidx.core.content.ContextCompat
import com.unciv.logic.CustomSaveLocationHelper
import com.unciv.logic.GameInfo
import com.unciv.logic.GameSaver
@@ -13,12 +16,16 @@ import java.util.concurrent.atomic.AtomicReference
const val REQ_SAVE_GAME = 1
const val REQ_LOAD_GAME = 2
+const val REQ_WRITE_STORAGE = 3
// The Storage Access Framework is available from API 19 and up:
// https://developer.android.com/guide/topics/providers/document-provider
@RequiresApi(Build.VERSION_CODES.KITKAT)
class CustomSaveLocationHelperAndroid(private val activity: Activity) : CustomSaveLocationHelper {
private val callback = AtomicReference Unit>?>()
+ private var cachedSaveRequest: SaveRequest? = null
+ @Synchronized get
+ @Synchronized set
override fun saveGame(gameInfo: GameInfo, gameName: String, forcePrompt: Boolean, block: (() -> Unit)?) {
callback.set(Thread.currentThread() to { uri ->
@@ -33,6 +40,17 @@ class CustomSaveLocationHelperAndroid(private val activity: Activity) : CustomSa
return
}
}
+
+ // For some reason it seems you can save to an existing file that you open without the
+ // permission, but you can't write to a file that you've requested be created so if this is
+ // a "Save as" operation then we need to get permission to write first
+ if (ContextCompat.checkSelfPermission(activity, WRITE_EXTERNAL_STORAGE) != PERMISSION_GRANTED
+ && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ cachedSaveRequest = SaveRequest(gameInfo, gameName, forcePrompt, block)
+ activity.requestPermissions(arrayOf(WRITE_EXTERNAL_STORAGE), REQ_WRITE_STORAGE)
+ return
+ }
+
Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
type = "application/json"
putExtra(Intent.EXTRA_TITLE, gameName)
@@ -40,6 +58,12 @@ class CustomSaveLocationHelperAndroid(private val activity: Activity) : CustomSa
}
}
+ fun continuePreviousRequest() {
+ cachedSaveRequest?.let {
+ saveGame(it.gameInfo, it.gameName, it.forcePrompt, it.block)
+ cachedSaveRequest = null
+ }
+ }
// This will be called on the main thread
fun handleIntentData(uri: Uri?) {
@@ -63,11 +87,11 @@ class CustomSaveLocationHelperAndroid(private val activity: Activity) : CustomSa
callback.set(Thread.currentThread() to { uri ->
uri?.let {
val game = activity.contentResolver.openInputStream(it)
- ?.reader()
- ?.readText()
- ?.run {
- GameSaver.gameInfoFromString(this)
- }?: return@let
+ ?.reader()
+ ?.readText()
+ ?.run {
+ GameSaver.gameInfoFromString(this)
+ } ?: return@let
// If the user has saved the game from another platform (like Android),
// then the save location might not be right so we have to correct for that
// here
@@ -81,4 +105,11 @@ class CustomSaveLocationHelperAndroid(private val activity: Activity) : CustomSa
activity.startActivityForResult(this, REQ_LOAD_GAME)
}
}
-}
\ No newline at end of file
+}
+
+data class SaveRequest(
+ val gameInfo: GameInfo,
+ val gameName: String,
+ val forcePrompt: Boolean,
+ val block: (() -> Unit)?
+)
\ No newline at end of file