godotengine / godot-proposals

Godot Improvement Proposals (GIPs)
MIT License
1.13k stars 92 forks source link

Option to use publicly accessible app data folder on Android #5944

Open JasonTable opened 1 year ago

JasonTable commented 1 year ago

Describe the project you are working on

Projects that do not want to lock the user out of the app's files but also don't wanna request the external storage permission.

Describe the problem or limitation you are having in your project

When a project saves files to the user:// directory on Android, it goes to the private folder at /data/data/[unique_name]/files. By design, the user cannot access the folder unless they have a rooted device. This is good for apps that store sensitive data like DRM encrypted movies for offline viewing or account login info or saving game progress somewhere where cheating will be difficult.

But I personally don't want to restrict the user from accessing game files. I also don't wanna ask for read and write access to the entire /sdcard/ shared storage.

Describe the feature / enhancement and how it helps to overcome the problem or limitation

I am no expert but i am pretty sure the /sdcard/Android/data/[unique_name]/files is a location that does not need to request permission to write to but it's public to the user through the Files app and USB MTP.

An option to set whether the user:// location is in the private /data/data folder or in the user accessible /sdcard/Android/data folder.

Describe how your proposal will work, with code, pseudo-code, mock-ups, and/or diagrams

An option could be in the project settings Android section or the export template's settings to switch between private and public user:// locations.

If this enhancement will not be used often, can it be worked around with a few lines of script?

I noticed that as of at least Godot 3.5.1 the folder is still created at /sdcard/Android/data/[unique_name]/files/ and that directory IS writable without storage permissions. So i could make it manually put files in that location instead of user:// but that is kind of annoying since I cannot find a way to get the Android unique_name property in gdscript so i have to manually place it in a const and update the const if I change the unique_name.

Is there a reason why this should be core and not an add-on in the asset library?

I think it's a feature that should be supported natively since it is a native feature of Android.

Zireael07 commented 1 year ago

I'm not sure that sdcard/data folder is still accessible as of Android 12/13?

Calinou commented 1 year ago

Scope storage (which is enforced on Android 11 and later) makes this a lot more difficult to implement, although it's still technically feasible. However, this will require the user to grant permissions to a folder the first time you start the game, which isn't a good user experience.

JasonTable commented 1 year ago

I think scoped storage is an entirely different API. The one i am referring to is the getExternalFilesDir() function listed here https://developer.android.com/training/data-storage where permission is not required for Android 4.4 and higher.

My device I test with runs Android 13. From what i can tell, my apps created in Godot 3.4 do not create the folder on their own but the ones in 3.5.1 do create it when launched for the first time. When I hard-code the External Files Dir (which is discouraged) and write to it, it works despite me leaving the storage permissions unchecked in the Android template export options. And there were no permission prompts.

here is the code i made to test it:

extends Control

var f = File.new()

func _on_test_pressed():
    write_test("user://user-dir.txt")
    write_test("/data/data/me.testapp/files/hard-coded-internal.txt")
    write_test("/sdcard/Android/data/me.testapp/files/hard-coded-external.txt")
    write_test("/sdcard/need-perm-for-this-so-falure-is-expected.txt")

func write_test(loc):
    var status = f.open(loc,File.WRITE)
    if status == OK:
        f.store_string("this is a test. if you see this, it works. yay!")
        f.close()
        $result.text += "succesfully wrote to %s\n"%[loc]
    else:
        f.close()
        $result.text += "failed to write to %s error code %d\n"%[loc,status]

and here are screenshots of the results.

Screenshot_20221217-025422 Screenshot_20221217-025010 Screenshot_20221217-025028 and i attached the project just in case android dir write test.zip

So some way for me to use the External Files Dir as the user:// directory is what i propose.