[Android] Implement native file picker support
-
First commit, creates a new DisplayServer Flag FEATURE_NATIVE_DIALOG_FILE_EXTRA, for access to USERDATA and RESOURCES directory and OPTIONS feature. Native File Dialog will fallback to custom one if not supported.
-
Second commit, implements native file picker support for android
This Partially addresses
-
https://github.com/godotengine/godot-proposals/issues/1123
-
https://github.com/godotengine/godot-proposals/issues/10983
Demo Video
https://github.com/user-attachments/assets/2e0729e8-f79a-42df-9c97-9dcf68ea7998
Task List
- [x] Every FileMode is supported (except OPEN_ANY).
- [x] Filters are supported (MIME types).
- [x] Current directory(initial directory) supported.
- [x] Filename for SAVE mode supported.
- [x] callback supported.
- [x] ~Options support~ Not possible with system file picker.
- [x] ~show hidden~ It can be set manually with system file picker.
- [x] ~selected filter index callback~ Not possible with multi select, that's why omited this feature entirely.
- [x] ~access mode RESOURCES and USERDATA support~ Not possible with native picker, will fallback to custom one.
I think we cannot implement selected_file_index callback it would work with single file section ( I can get file mime type) but would create problem in multiple section mode (if user selects multiple type of files from storage).
Access mode RESOURCES and USERDATA, would also not be feasible due to restrictions with Storage Access Framework. I was thinking about using Godot's custom Filedialog for these two modes, even if use_native_dialog is enabled.
currently, file picker is not considering access modes and always opens Filesystem.
Now, Current_directory (initial directory) is also supported. It should be an external storage directory, users can use OS.get_system_dir(OS.SYSTEM_DIR_DESKTOP) to get root path.
example:
func _show_native_file_picker() -> void:
var filters = PackedStringArray(["image/*","text/plain","application/pdf"])
var current_directory = OS.get_system_dir(OS.SYSTEM_DIR_DOCUMENTS)
DisplayServer.file_dialog_show("title", current_directory, "filename", false, DisplayServer.FILE_DIALOG_MODE_OPEN_FILE, filters, _picker_callback)
func _picker_callback(status: bool, selected_paths: PackedStringArray, selected_filter_index: int):
$Label.text += str("Status: ",status,"\n")
$Label.text += str("Selected Paths: ",selected_paths,"\n\n")
This is now ready for review, and all features are functioning as expected in my tests.
Regarding the two unchecked tasks:
- I considered implementing the
selected_filter_indexcallback for single file selection, but it won't be feasible for multi-select scenarios. Therefore, I decided to omit this feature entirely. - As for supporting
RESOURCESandUSERDATAaccess modes, this is not possible with the android native file picker.
Please let me know if any additional adjustments are needed!
I was thinking about creating a seperate commit for documentations, after everything about implementation is discussed properly.
Also, it might be worth adding new DisplayServer feature flags for options and res/usr access, and check these flags in the FileDialog to automatically switch to embedded dialog when incompatible options are used.
As i said in my above comments, I was also thinking about something similar, was just waiting for some confirmations.
@bruvzg how about only one new feature tag FEATURE_NATIVE_DIALOG_FILE_EXTRA ?
how about only one new feature tag FEATURE_NATIVE_DIALOG_FILE_EXTRA ?
One flag should be OK (iOS will have the same limitations).
Created a new flag in Display Server FEATURE_NATIVE_DIALOG_FILE_EXTRA and updated documentation.
Native File Picker only supports Filesystem and it will fallback to custom filedialog for access mode Userdata or Resources
@syntaxerror247 can I have the APK to test?
@UnderGamer05 I have uploaded the apk, check in PR description.
Hi @m4gr3d , just checking in to see if the updates I made in the PR align with what you had in mind. Let me know if there's anything else you'd like me to tweak.
Rebased and resolved conflicts.
Thanks!
@syntaxerror247 Just tested the test apk provided here and when opening the resources it doesn't open the native file picker is that intentional? Also it says you don't have enough permissions as you can see in the screenshot below 👇
I also tested this on v4.4 Dev 5 and when I try to use the native file dialog (not through code) and setting the access as ACCESS_FILESYSTEM the app immediately closes when I press the button. I believe I have checked the needed read user directory permissions (It asked to enable the same permission as the test apk you provided). Did you do this through code?
Just tested the test apk provided here and when opening the resources it doesn't open the native file picker is that intentional?
@ThatSimpleDev Yes, that intentional. Android doesn't allow access to user:// and res:// directory, that's why it fallbacks to custom Filedialog.
I also tested this on v4.4 Dev 5 and when I try to use the native file dialog (not through code) and setting the access as ACCESS_FILESYSTEM the app immediately closes when I press the button.
Yes, This is a regression due an update to FileDialog. This issue wasn't present in dev4. This will be solved after these two PRs https://github.com/godotengine/godot/pull/99350 and https://github.com/godotengine/godot/pull/99385 are merged.
For now, you can use it via code.
@syntaxerror247 First of all, thank you for your work! Very appreciated! I tried to use the new native dialog but I have a hard time to get anything meaningful done with it. The native dialog opens and i can select a file. The "file_selected" signal also fires and it returns the correct path of the file. Up to this point everything works fine and I guess this is what this PR was implementing. Where the problem starts is when I want to read that file, as I still do not have permissions for that. I thought this was handled by the SAF (Storage Access Framework) automatically. But I guess it's not the case. I noticed that the test.apk you uploaded here asked for the "MANAGE_EXTERNAL_STORAGE" permission. I guess if I add that to my application it works as well, but that is no option. I want to release my app in Google Play and this permission is something google only allows for apps like file managers, as it is a dangerous permission. So my question is: Is there currently any way in Godot to read a file selected with the native dialog in Android other than adding that "MANAGER_EXTERNAL_STORAGE" permission? Or would I have to write a plugin that somehow reads that file with SAF access management. Or is there another effort or PR that's tackling that problem? I did not find any with a quick search. Thanks.
@PostPollux Have you found a way to achieve this yet?
@PostPollux @Rocket-007 See https://github.com/godotengine/godot-proposals/issues/12669
You can still use the file picker with the limitations that app would be only able to save to Download and Documents directory and it would be only able to access files it's has created. If you are working with media files then you can enable permission like READ_MEDIA_IMAGES to get broader read access.
@PostPollux Have you found a way to achieve this yet?
@Rocket-007 Yes, I actually used this plugin: https://github.com/SeppNel/Godot-File-Picker which is an updated fork from https://github.com/pattlebass/Godot-File-Picker
What it does is also opening the native file browser, but it then copies the selected file to a location where Godot has access to without any additional permissions needed. However, you are then responsible for deleting those files again when they are not needed anymore. But that's fine.