Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for Android scoped storage requirements #38913

Closed
HEAVYPOLY opened this issue May 21, 2020 · 12 comments · Fixed by #50360
Closed

Add support for Android scoped storage requirements #38913

HEAVYPOLY opened this issue May 21, 2020 · 12 comments · Fixed by #50360

Comments

@HEAVYPOLY
Copy link
Contributor

HEAVYPOLY commented May 21, 2020

Godot version:
3.2.2 Beta 2
OS/device including version:
Android
Issue description:
Try to save image, get error "No permission grants found for com.google.android.apps.photos"

Read/Write external storage permssions are enabled in Android Export options. The same code worked in 3.2.1 stable.
Steps to reproduce:

Minimal reproduction project:
Logcat:
05-20 21:35:58.903 29344 29554 I godot : Loaded Strokes Drawn
05-20 21:35:57.777 1423 4285 W UriGrantsManagerService: No permission grants found for com.google.android.apps.photos
05-20 21:35:59.052 1423 4285 D ConnectivityService: filterNetworkStateForUid() uid: 10209, pid: 18529
05-20 21:35:59.052 1423 4285 D ConnectivityService: filterNetworkStateForUid() uid: 10209, pid: 18529, networkInfo: [type: WIFI[], state: DISCONNECTED/BLOCKED, reason: (unspecified), extra: (none), failover: false, available: true, roaming: false]
05-20 21:35:59.052 1423 4285 D ConnectivityService: filterNetworkStateForUid() uid: 10209, pid: 18529
05-20 21:35:59.053 1423 4285 D ConnectivityService: filterNetworkStateForUid() uid: 10209, pid: 18529, networkInfo: [type: WIFI[], state: DISCONNECTED/BLOCKED, reason: (unspecified), extra: (none), failover: false, available: true, roaming: false]
05-20 21:35:59.137 1423 4285 W UriGrantsManagerService: No permission grants found for com.google.android.apps.photos
05-20 21:35:59.137 1423 4937 W UriGrantsManagerService: No permission grants found for com.google.android.apps.photos
05-20 21:35:59.195 29344 29554 E godot : ERROR: Can't save PNG at path: '/storage/emulated/0/Pictures/HEAVYPAINT_EXPORT/asd.png'.
05-20 21:35:59.195 29344 29554 E godot : At: drivers/png/resource_saver_png.cpp:58:save_image() - Condition "err" is true. Returned: err

@HEAVYPOLY
Copy link
Contributor Author

HEAVYPOLY commented May 21, 2020

Did some digging around, could it have something to do with this?
https://developer.android.com/training/data-storage/compatibility
Also found this for API 29+
"Scoped storage"
https://developer.android.com/preview/privacy/storage
image

@m4gr3d
Copy link
Contributor

m4gr3d commented May 27, 2020

@HEAVYPOLY you're most likely running into scoped storage issues.

The reason you're seeing that is because 3.2.2 has targetSdk set to 29, while 3.2.1 has targetSdk set to 28 and the new scoped storage restriction starts at API level 29.

As mentioned in the scoped storage, you can temporarily restore the previous behavior by using the requestLegacyExternalStorage attribute, but in the long term, you should probably start migrating to the new way of using storage.

@HEAVYPOLY
Copy link
Contributor Author

Thanks for the info. I'm searching for requestLegacyExternalStorage in Android export options but don't see it, where can I find this setting?

Also, what is the proper way to save/load files for the future?

image

@m4gr3d
Copy link
Contributor

m4gr3d commented May 27, 2020

@HEAVYPOLY I didn't realize you were using an internal Godot method to save the file.

@akien-mga Now that I have a fuller understanding of the issue, it's quite tricky as we possibly have a wide surface area to investigate/fix (pretty much any logic that write to file) and we only have until August 3, 2020 since API level 29 will be required starting then.

A first approach would be to include the requestLegacyExternalStorage attribute by default to keep the current behavior. This will give us about a year to update the internal file logic before we have to switch to API level 30.

@m4gr3d m4gr3d changed the title "No permission grants found" error when trying to save on Android. Godot 3.2.2 Beta 2 Add support for Android scoped storage requirements May 27, 2020
@HEAVYPOLY
Copy link
Contributor Author

Sounds good, thank you. Is there a better way I should be handling all this? Is there a way to access native android share popup?

@m4gr3d
Copy link
Contributor

m4gr3d commented May 27, 2020

@HEAVYPOLY It's hard to say given I'm not familiar with the logic from your app.

Using scoped storage properly would require some familiarity with Android and its filesystem. If you have that experience, then you could write your own plugin that stores files properly, and use it from gdscript instead of using the Godot provided APIs.

Otherwise, the alternative is to wait until we clean those APIs.

@HEAVYPOLY
Copy link
Contributor Author

Ok, I will wait for the legacy external storage fix. Thank you!

@akien-mga
Copy link
Member

A first approach would be to include the requestLegacyExternalStorage attribute by default to keep the current behavior. This will give us about a year to update the internal file logic before we have to switch to API level 30.

I think we should do this for now, especially as it will be needed for older stable branches like 2.1 which also needs a 2.1.7 update targeting API level 29 (#41300).

We should still work to implement the new scoped storage access ASAP, but this should be a good stopgap measure for 3.2.3 and 2.1.7 (and 3.1.3 if I make one).

@m4gr3d
Copy link
Contributor

m4gr3d commented Aug 17, 2020

A first approach would be to include the requestLegacyExternalStorage attribute by default to keep the current behavior. This will give us about a year to update the internal file logic before we have to switch to API level 30.

I think we should do this for now, especially as it will be needed for older stable branches like 2.1 which also needs a 2.1.7 update targeting API level 29 (#41300).

We should still work to implement the new scoped storage access ASAP, but this should be a good stopgap measure for 3.2.3 and 2.1.7 (and 3.1.3 if I make one).

@akien-mga this has already been done by #39103.

The issue remains open to track the full implementation for scoped storage.

@akien-mga akien-mga modified the milestones: 3.2, 4.0 Aug 17, 2020
@akien-mga
Copy link
Member

Thanks, I cherry-picked the change for 2.1.7: 11fba31.

@m4gr3d
Copy link
Contributor

m4gr3d commented Jul 10, 2021

@HEAVYPOLY What's the api you were using to save the images and how are you generating the path where they are being saved?
In addition, do the images your app generates need to remain on the device even if your app is uninstalled?

@HEAVYPOLY
Copy link
Contributor Author

HEAVYPOLY commented Jul 10, 2021

I get the user pictures path with OS.get_system_dir().

        if SM.user_prefs.has("file_path"):
            var dir = Directory.new()
            dir.open(SM.user_prefs.file_path)
            if dir.get_current_dir() == "res://":
                print("RESETTING SAVE PATH")
                SM.user_prefs.file_path = OS.get_system_dir(OS.SYSTEM_DIR_PICTURES)

The images ideally should stay on device after uninstall, and using this method they do (since they are in pictures directory).

side note: on iOS we don't have access to pictures directory so everything is saved to user folder and gets deleted on uninstall

here is my save script, it uses standard save_png()

func save_layer_png(layer, path, merge = false):
    var ld = GB.get_layer_data_by_name(layer.name)
    if merge == false:
        if GB.get_layer_data_by_name(layer.name).visible == false:
            print("SKIPPING SAVE LAYER PNG, INVISIBLE")
            yield(get_tree(),"idle_frame")
            return
    layer.Export.rect_size = SM.canvas.size
    layer.V.size = SM.canvas.size

    layer.Export.modulate = layer.modulate
    layer.Export.material = layer.material


    layer.V.render_target_update_mode = Viewport.UPDATE_ONCE
    yield(get_tree(),"idle_frame")
    yield(get_tree(),"idle_frame")
    var img:Image = layer.V.get_texture().get_data()
    GB.Status.text = str("RENDERING LAYER ", layer.name.to_upper())
#    print("APPENDING ", img)
#    layerpngs.append(img)
    if SM.user_prefs.batch_export_layers:
        var img_path = path + "/" + str(SM.canvas.layers.find(ld)+1) + " " + ld.name + ".png"
        img.save_png(img_path)
        print("SAVING PNG BATCH ", img_path)
#        img.save_png(path + "/" + str(layer.get_position_in_parent()+1) + " " + layer.name + ".png")
    layer.V.render_target_update_mode = Viewport.UPDATE_DISABLED

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants