Skip to content

Commit bed803f

Browse files
committed
Merge pull request #113367 from syntaxerror247/persistable_uri_perm
Android: Add method to take persistable URI permission
2 parents 0289309 + 3989272 commit bed803f

File tree

3 files changed

+37
-18
lines changed

3 files changed

+37
-18
lines changed

doc/classes/DisplayServer.xml

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -790,8 +790,17 @@
790790
[b]Note:[/b] On macOS, sandboxed apps will save security-scoped bookmarks to retain access to the opened folders across multiple sessions. Use [method OS.get_granted_permissions] to get a list of saved bookmarks.
791791
[b]Note:[/b] On Android, this method uses the Android Storage Access Framework (SAF).
792792
The file picker returns a URI instead of a filesystem path. This URI can be passed directly to [FileAccess] to perform read/write operations.
793-
When using [constant FILE_DIALOG_MODE_OPEN_DIR], it returns a tree URI that grants full access to the selected directory. File operations inside this directory can be performed by passing a path in the form [code]treeUri#relative/path/to/file[/code] to [FileAccess].
794-
Tree URIs should be saved and reused; they remain valid across app restarts as long as the directory is not moved, renamed, or deleted.
793+
When using [constant FILE_DIALOG_MODE_OPEN_DIR], it returns a tree URI that grants full access to the selected directory. File operations inside this directory can be performed by passing a path on the form [code]treeUri#relative/path/to/file[/code] to [FileAccess].
794+
To avoid opening the file picker again after each app restart, you can take persistable URI permission as follows:
795+
[codeblocks]
796+
[gdscript]
797+
val uri = "content://com.android..." # URI of the selected file or folder.
798+
val persist = true # Set to false to release the persistable permission.
799+
var android_runtime = Engine.get_singleton("AndroidRuntime")
800+
android_runtime.updatePersistableUriPermission(uri, persist)
801+
[/gdscript]
802+
[/codeblocks]
803+
The persistable URI permission remains valid across app restarts as long as the directory is not moved, renamed, or deleted.
795804
</description>
796805
</method>
797806
<method name="file_dialog_with_options_show">

platform/android/java/lib/src/main/java/org/godotengine/godot/io/FilePicker.kt

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -85,28 +85,12 @@ internal class FilePicker {
8585
for (i in 0 until clipData.itemCount) {
8686
val uri = clipData.getItemAt(i).uri
8787
uri?.let {
88-
try {
89-
context.contentResolver.takePersistableUriPermission(
90-
it,
91-
Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
92-
)
93-
} catch (e: SecurityException) {
94-
Log.d(TAG, "Unable to persist URI: $it", e)
95-
}
9688
selectedFiles.add(it.toString())
9789
}
9890
}
9991
} else {
10092
val uri: Uri? = data?.data
10193
uri?.let {
102-
try {
103-
context.contentResolver.takePersistableUriPermission(
104-
it,
105-
Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
106-
)
107-
} catch (e: SecurityException) {
108-
Log.w(TAG, "Unable to persist URI: $it", e)
109-
}
11094
selectedFiles.add(it.toString())
11195
}
11296
}

platform/android/java/lib/src/main/java/org/godotengine/godot/plugin/AndroidRuntimePlugin.kt

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@
3030

3131
package org.godotengine.godot.plugin
3232

33+
import android.content.Intent
34+
import android.util.Log
35+
import androidx.core.net.toUri
36+
3337
import org.godotengine.godot.Godot
3438
import org.godotengine.godot.variant.Callable
3539

@@ -39,6 +43,8 @@ import org.godotengine.godot.variant.Callable
3943
* @see <a href="https://docs.godotengine.org/en/latest/tutorials/platform/android/javaclasswrapper_and_androidruntimeplugin.html">Integrating with Android APIs</a>
4044
*/
4145
class AndroidRuntimePlugin(godot: Godot) : GodotPlugin(godot) {
46+
private val TAG = AndroidRuntimePlugin::class.java.simpleName
47+
4248
override fun getPluginName() = "AndroidRuntime"
4349

4450
/**
@@ -68,4 +74,24 @@ class AndroidRuntimePlugin(godot: Godot) : GodotPlugin(godot) {
6874
fun createCallableFromGodotCallable(godotCallable: Callable): java.util.concurrent.Callable<Any> {
6975
return java.util.concurrent.Callable { godotCallable.call() }
7076
}
77+
78+
/**
79+
* Helper method to take/release persistable URI permission.
80+
*/
81+
@UsedByGodot
82+
fun updatePersistableUriPermission(uriString: String, persist: Boolean): Boolean {
83+
try {
84+
val uri = uriString.toUri()
85+
val contentResolver = context.contentResolver
86+
if (persist) {
87+
contentResolver.takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
88+
} else {
89+
contentResolver.releasePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
90+
}
91+
} catch (e: RuntimeException) {
92+
Log.d(TAG, "Error updating persistable permission: ", e)
93+
return false
94+
}
95+
return true
96+
}
7197
}

0 commit comments

Comments
 (0)