mirror of
https://github.com/yairm210/Unciv.git
synced 2025-02-20 19:56:51 +01:00
* Allow typing Y and N in file names in Linux custom save file dialog * Fix default custom save location suggestion on X11 * Minor linting * Allow clean reactivation of custom buttons after cancelling the file chooser * User-Cancel support on Android and non-X11 desktop * Block custom save for online multiplayer games * Fix X11 custom save remembering location * Fix X11 custom save "Save" button enabling, forbid potentially bad names * Add overwrite confirmation to X11 custom save * Redefine how local saving of online multiplayer games is blocked
123 lines
4.1 KiB
Kotlin
123 lines
4.1 KiB
Kotlin
package com.unciv.app
|
|
|
|
import android.app.Activity
|
|
import android.content.Intent
|
|
import android.net.Uri
|
|
import android.provider.DocumentsContract
|
|
import android.provider.OpenableColumns
|
|
import com.unciv.logic.files.PlatformSaverLoader
|
|
import com.unciv.utils.Log
|
|
import java.io.InputStream
|
|
import java.io.OutputStream
|
|
|
|
|
|
class AndroidSaverLoader(private val activity: Activity) : PlatformSaverLoader {
|
|
|
|
private val contentResolver = activity.contentResolver
|
|
private val requests = HashMap<Int, Request>()
|
|
private var requestCode = 100
|
|
|
|
private class Request(
|
|
val onFileChosen: (Uri) -> Unit,
|
|
onError: (ex: Exception) -> Unit
|
|
) {
|
|
val onCancel: () -> Unit = { onError(PlatformSaverLoader.Cancelled()) }
|
|
}
|
|
|
|
override fun saveGame(
|
|
data: String,
|
|
suggestedLocation: String,
|
|
onSaved: (location: String) -> Unit,
|
|
onError: (ex: Exception) -> Unit
|
|
) {
|
|
|
|
// When we loaded, we returned a "content://" URI as file location.
|
|
val suggestedUri = Uri.parse(suggestedLocation)
|
|
val fileName = getFilename(suggestedUri, suggestedLocation)
|
|
|
|
val onFileChosen = { uri: Uri ->
|
|
var stream: OutputStream? = null
|
|
try {
|
|
stream = contentResolver.openOutputStream(uri, "rwt")
|
|
stream!!.writer().use { it.write(data) }
|
|
onSaved(uri.toString())
|
|
} catch (ex: Exception) {
|
|
onError(ex)
|
|
} finally {
|
|
stream?.close()
|
|
}
|
|
}
|
|
|
|
requests[requestCode] = Request(onFileChosen, onError)
|
|
openSaveFileChooser(fileName, suggestedUri, requestCode)
|
|
requestCode += 1
|
|
}
|
|
|
|
override fun loadGame(
|
|
onLoaded: (data: String, location: String) -> Unit,
|
|
onError: (ex: Exception) -> Unit
|
|
) {
|
|
|
|
val onFileChosen = {uri: Uri ->
|
|
var stream: InputStream? = null
|
|
try {
|
|
stream = contentResolver.openInputStream(uri)
|
|
val text = stream!!.reader().use { it.readText() }
|
|
onLoaded(text, uri.toString())
|
|
} catch (ex: Exception) {
|
|
onError(ex)
|
|
} finally {
|
|
stream?.close()
|
|
}
|
|
}
|
|
|
|
requests[requestCode] = Request(onFileChosen, onError)
|
|
openLoadFileChooser(requestCode)
|
|
requestCode += 1
|
|
}
|
|
|
|
fun onActivityResult(requestCode: Int, data: Intent?) {
|
|
val request = requests.remove(requestCode) ?: return
|
|
// data is null if the user back out of the activity without choosing a file
|
|
if (data == null) return request.onCancel()
|
|
val uri: Uri = data.data ?: return
|
|
request.onFileChosen(uri)
|
|
}
|
|
|
|
private fun openSaveFileChooser(fileName: String, uri: Uri, requestCode: Int) {
|
|
val intent = Intent(Intent.ACTION_CREATE_DOCUMENT)
|
|
intent.type = "application/json"
|
|
intent.putExtra(Intent.EXTRA_TITLE, fileName)
|
|
if (uri.scheme == "content")
|
|
intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, uri)
|
|
activity.startActivityForResult(intent, requestCode)
|
|
}
|
|
|
|
private fun openLoadFileChooser(requestCode: Int) {
|
|
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT)
|
|
intent.type = "*/*"
|
|
/* It is theoretically possible to use an initial URI here,
|
|
however, the only Android URIs we have are obtained from here, so, no dice */
|
|
activity.startActivityForResult(intent, requestCode)
|
|
}
|
|
|
|
private fun getFilename(uri: Uri, suggestedLocation: String): String {
|
|
|
|
if (uri.scheme != "content")
|
|
return suggestedLocation
|
|
|
|
try {
|
|
contentResolver.query(uri, null, null, null, null).use {
|
|
if (it?.moveToFirst() == true)
|
|
return it.getString(it.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME))
|
|
else
|
|
return ""
|
|
}
|
|
} catch(ex: Exception) {
|
|
Log.error("Failed to get filename from Uri", ex)
|
|
return suggestedLocation.split("2F").last() // I have no idea why but the content path ends with this before the filename
|
|
}
|
|
|
|
}
|
|
}
|