[TIMOB-27201] Android: Update "Ti.Filesystem.File" to support unimplemented APIs when wrapping a "content://" URL
GitHub Issue | n/a |
---|---|
Type | Improvement |
Priority | Medium |
Status | Closed |
Resolution | Fixed |
Resolution Date | 2020-10-22T22:24:23.000+0000 |
Affected Version/s | n/a |
Fix Version/s | Release 9.3.0 |
Components | Android |
Labels | android, blob, content, file, url |
Reporter | Joshua Quick |
Assignee | Joshua Quick |
Created | 2019-07-01T23:34:37.000+0000 |
Updated | 2020-10-22T22:24:23.000+0000 |
Description
*Summary:*
When passing an Android "content://" URL to the
Ti.Filesystem.getFile()
method, the returned File
object does not support the following methods/properties. They will return failure results and log a "Method is not supported" warning.
* [File.createdAt()](https://docs.appcelerator.com/platform/latest/#!/api/Titanium.Filesystem.File-method-createdAt)
* [File.deleteFile()](https://docs.appcelerator.com/platform/latest/#!/api/Titanium.Filesystem.File-method-deleteFile)
* [File.exists()](https://docs.appcelerator.com/platform/latest/#!/api/Titanium.Filesystem.File-method-exists)
* [File.extension()](https://docs.appcelerator.com/platform/latest/#!/api/Titanium.Filesystem.File-method-extension)
* [File.modifiedAt()](https://docs.appcelerator.com/platform/latest/#!/api/Titanium.Filesystem.File-method-modifiedAt)
* [File.read()](https://docs.appcelerator.com/platform/latest/#!/api/Titanium.Filesystem.File-method-read)
* [File.readonly](https://docs.appcelerator.com/platform/latest/#!/api/Titanium.Filesystem.File-property-readonly)
* [File.write()](https://docs.appcelerator.com/platform/latest/#!/api/Titanium.Filesystem.File-method-write)
* [File.writable](https://docs.appcelerator.com/platform/latest/#!/api/Titanium.Filesystem.File-property-writable)
We should add the above methods for consistency.
*Note:*
The File
object already correctly implements all other methods and properties for a "content://" URL such as the copy()
method and "nativePath" property. Adding the above methods will help make the interface more complete and avoid confusion.
*Implementation:*
We need to add the above methods to our TitaniumBlob
Java class.
https://github.com/appcelerator/titanium_mobile/blob/master/android/titanium/src/java/org/appcelerator/titanium/io/TitaniumBlob.java
We can test the inclusion of these methods with the attached [^FileContentUrlTest.js] file. This test script assumes its was created via a Classic Default project template.
Attachments
File | Date | Size |
---|---|---|
FileContentUrlTest.js | 2019-07-02T00:17:46.000+0000 | 1275 |
It seems like it's also not possible to handle content URL's right after granting permissions:
We use the following to grant permissions before accessing the URL, but it only works at the second attempt and crashes before:
-And as usual, no workaround applicable :(- *EDIT*: Workaround found:
intent.addFlags(Titanium.Android.FLAG_GRANT_READ_URI_PERMISSION | Titanium.Android.FLAG_GRANT_WRITE_URI_PERMISSION);
+ thecom.google.android.apps.photos.permission.GOOGLE_PHOTOS
permission fixed it. No idea why exactly that would work better.[~hknoechel], the external storage permissions only applies to direct file system access. It has no impact on content URLs because you do not have direct file access to another app's sandboxed files. Instead, a
ContentProvider
provides access to files via inter-process communications. So, an app has to grant temporary access to the content URL's file via the intent'sFLAG_GRANT_READ_URI_PERMISSION
as you have noted. This is the correct native behavior. Google documents this below. Also note that theFLAG_GRANT_READ_URI_PERMISSION
flag only grants *+temporary+* permission. The permission will be lost after rebooting the device. https://developer.android.com/guide/topics/providers/content-provider-creating Also, would you mind next time writing this up as a separate "story" ticket? Because what you've posted has nothing to do with this ticket and I don't want to add any confusion to it. (Titanium is handling intents correctly. It just sounds like more examples are needed.)@jquick This would be great for consistency as you say. If I am looking to do now what read() would offer, what would be a recommended workaround? Namely the user is selecting a video from their gallery and I want to upload that to our server. In our current app flow, we queue up the request and asynchronously look to access the content/file at the time of upload. Usually, as you say, that is by doing getFile(nativePath) and read() to load the Blob data to then give to the HttpClient instance. Since I can't do that for content:// URLs now, I'm struggling on the correct roundabout way of accomplishing the same thing. Any help is appreciated.
[~mpoole_tp], The
HTTPClient.send()
method supports aFile
object. So, you don't need to do aFile.read()
call and send its blob. Alternatively, since you're selecting the video viaTi.Media.openPhotoGallery()
, thee.media
object that references the selected video is aBlob
object. You can simply send that viaHTTPClient
as well. This is probably the most optimized way of doing it. Have a look at the test code in the link below. It plays ane.media
video references viaVideoPlayer
. In your case, you'll want to send it viaHTTPClient
instead. https://github.com/appcelerator/titanium_mobile/pull/9223 Also note that aFile
object referencing a "content://" URL still supports theopen()
andcopy()
methods. So, you have other options as well. For example, for photo selection, I would normally tell devs tocopy()
the photo to their app's own sandbox. Partly because "content://" URL access is typically temporary and access will be revoked after rebooting the device. I hope this helps.Thanks. Yeah, I was able to confirm that if I use the e.media immediately I'm able to post it fine. What my issue within my current code structure seems to be is that I can't use the content:// url to re-load it instead (i.e. ignore the initial e.media). At least not by doing Ti.Filesystem.getFile(nativePath), because of what you're saying in this ticket. Trying to get it to load as a stream and copy it at that point seems to not be working for me as well.. I think if I make a copy of the initial e.media into an app directory, though, I probably will be ok and that's my best way forward if I don't want to immediately upload it. thanks for your quick response.
PR (master): https://github.com/appcelerator/titanium_mobile/pull/12143
FR Passed for this ticket.
Verified the fix with SDK 9.3.0.v20201022111908. Closing.