file_picker_writable
file_picker_writable copied to clipboard
Persistable access to a directory
Thanks as always for the great plugin.
I have a use case for selecting a directory, rather than a file: I want to be able to resolve files relative to a file, e.g. images referenced from a document.
For instance I open a file foo/bar/baz.html and find that it references several other files in ./blah. I want to be able to obtain a persistent reference to foo/bar/blah and resolve the files within.
This plugin currently doesn't offer that functionality. Would you be interested in adding it, or would you accept a PR that implements it for iOS and Android only?
I am looking at these APIs:
- iOS:
UIDocumentPickerViewController(documentTypes: [kUTTypeFolder as String], in: .open)(docs) - Android:
Intent.ACTION_OPEN_DOCUMENT_TREE(docs, sample code)
On the Dart side, it looks like in addition to an openDirectory(path) method we may need some sort of resolveRelative(parent, child) that would take a known security-scoped URL or bookmark and return a new security-scoped URL/bookmark for the child.
Does ACTION_OPEN_DOCUMENT_TREE even work with Google Drive yet? See articles such as this one:
The file requester opened up by ACTION_OPEN_DOCUMENT_TREE does not list providers such as Google Drive or OneDrive, However, these providers DO show up when using ACTION_OPEN_DOCUMENT.
I worked around this issue by using ACTION_OPEN_DOCUMENT and specifying that the user could select multiple files.
-- https://stackoverflow.com/questions/28663778/android-open-document-tree-intent-root-locations
If Google Drive has not yet implemented ACTION_OPEN_DOCUMENT_TREE, I suspect it is even less likely to be implemented by other document providers.
Perhaps the situation is better on iOS.
At the very least it should work for local storage. I won't be holding back on features just because of Google Drive being lazy.
For instance I open a file
foo/bar/baz.html
But the user would first select foo/bar and then your application would show another selector where he chooses the file?
Would you be interested in adding it, or would you accept a PR that implements it for iOS and Android only?
Since i personally don't have a use case for this right now, I don't think i'll have the time to research and implement it right now. But I'd be happy to review and merge a PR. iOS/Android should be fine.. it shouldn't be hard to port it to macos afterwards, if necessary.
But the user would first select
foo/barand then your application would show another selector where he chooses the file?
No, sorry I should have given the full scenario.
- User chooses a file. The file references other files via relative path.
- I want to compute the common root directory of all referenced files, and prompt the user to obtain permissions for that directory. Note that the root could be above or below the original file.
- Having obtained permissions for the root, I can then access all referenced files.
There are several reasons why I want the above:
- I don't want to have to make an in-app directory browser
- Even if I had an in-app directory browser I can imagine scenarios where I would still find myself with permission to open a file but not any of its parent directories, so the scheme above is still needed
- I forget what else
But I'd be happy to review and merge a PR. iOS/Android should be fine..
Thanks. I'll open a PR at some point then.
Perhaps the situation is better on iOS.
Neither Google Drive nor Dropbox support persistent access to directories. Local storage and iCloud Drive do.
One thing that throws a bit of a wrench in my plans is that it seems that on Android you can't resolve the parent of a known content:// URI. For instance if I have a persistent URI to some directory, I can get URIs for children but I can't get a URI to ...
(On iOS the persistent identifiers are file:// URLs that can be manipulated with standard filesystem semantics.)
If anyone knows a way to solve this on Android I'd love to know.
@hpoul Would it be OK with you if I got rid of the deprecated file property on FileInfo?
I find there is a need to resolve a relative path and return FileInfo for it without actually reading the file (because the decision as to whether it should be read as a string or as bytes happens somewhere totally different). So I don't want to copy the file on the native side at all in some cases, but the requirement of a non-null File in FileInfo would mean I would need to make a different class.
(Resolving relative paths needs computation on the native side because Android content URIs are opaque and can't be reasoned about like regular paths.)
I am still working on this. It's taken a lot longer than I thought due to the complexity of the Android Storage Access Framework, but I've got it mostly under control now.
@hpoul Some questions about your preferences on nitty-gritty implementation details, if you don't mind:
Directory representation
I want to add these Dart-side methods:
FilePickerWritable.openDirectory(String? initialDir): counterpart toopenFilethat shows a directory picker, with an optional initial directoryFilePickerWritable.getDirectory(String rootIdentifier, String fileIdentifier): given a root directory identifier and a file identifier, return info about the parent directory of the fileFilePickerWritable.resolveRelativePath(String directoryIdentifier, String relativePath): given a directory identifier and a relative path like./foo/bar.txt, resolve the relative path and return info about the entity it points to
There is a need to represent info for a directory (as opposed to a file, which we have in FileInfo). Do you prefer:
- A
isDirectoryflag onFileInfo? - A separate class,
DirectoryInfo?
If you prefer a separate class like DirectoryInfo then it should share a base class with FileInfo because resolveRelativePath could return either a directory or a file. In fact DirectoryInfo, as far as I can tell, would have the exact same properties as FileInfo, so it could look like:
abstract class EntityInfo {
// Most of current contents of FileInfo
}
class FileInfo extends EntityInfo {
// Just {de,}serialization stuff
}
class DirectoryInfo extends EntityInfo {
// Just {de,}serialization stuff
}
Then we're basically using file types as a boolean flag. Though there may be a need for further specialization for either files or directories in the future, so I don't really see it as a problem.
(Note that the Dart standard library types FileSystemEntity, Directory, and File follow this general template.)
Platform support
Currently the plugin supports back to Android API 19, but the directory picking stuff requires API 21.
Do you prefer to:
- Drop support for pre-21?
- Just let those operations fail without special handling?
- Have special handling so the application knows why the operations failed?
I have been assuming (3): I was thinking of adding a specific Dart-side exception such as OperationUnsupported that users could catch if they wanted.