Can StorageActionFramework ACTION_OPEN_DOCUMENT request a single file? - android

Is there a way to use the StorageActionFramework ACTION_OPEN_DOCUMENT intent to request info for a specific file? More specifically, I have a URI for a particular file, which I obtained from MediaStore. I would like to find out if it has write access and, if so, delete it. Further, I'd like to do this without any UI (none is needed in this particular context).
The documentation says how to do it for a class of files using intent.addCategory (Intent.CATEGORY_OPENABLE), for example. And to restrict it to a certain type of file, intent.setType ("image/*"), for example. But I don't see anything to restrict it to a specific file.
Also, I realize that once you receive a set of files from the StorageActionFramework, you can view properties for individual files.
It appears that if I could use ACTION_OPEN_DOCUMENT to get the SAF's URI, I could then use DocumentsContract.deleteDocument() to delete it.
More Context
In my situation, the user has invoked the camera app from within my app and taken one or more pictures. My code then queries the MediaStore to determine the file name(s) for the new image(s). Then I desire to move the file(s) to a directory specific to my app. This works fine for files located in "internal" and "external" storage but not for removable storage.
In the long run, this solution is inadequate as clearly it will use to much permanent storage (although that is mitigated by the fast pace which storage size is increasing). However my app does need control over image files which are taken via the app and, thus, leaving them on the removable storage will potentially break the app.
Another complicating factor is the lack of an Android API for taking multiple photos and saving them to a prescribed location. There is such an API for taking a single photo but that will not work for my application. Thus, I am relegated to letting the camera app save its files where it wants to and then moving them afterward.
Some other apps simply make a copy of photos and store the copy in a private directory. I could do that but that exacerbates the storage problem even more. The long term solution will probably be using cloud storage in combination with a local private cache.

Is there a way to use the StorageActionFramework ACTION_OPEN_DOCUMENT intent to request info for a specific file?
No. For starters, the Storage Access Framework has little to do with files.
The closest thing that would fit the overall structure would be if you could supply a starting Uri, to allow users to choose and open a document nearby that one. That would be a nice feature but is not supported.
The documentation says how to do it for a class of files using intent.addCategory (Intent.CATEGORY_OPENABLE), for example.
No. CATEGORY_OPENABLE means that the Uri that you get back should work with openInputStream(), openOutputStream(), and related methods, and that a query() on the Uri should be able to return the OpenableColumns. It has little to do with files.
And to restrict it to a certain type of file, intent.setType ("image/*")
That limits the content to a particular MIME type (or wildcard). It has little to do with files.
I would like to find out if it has write access and, if so, delete it
You might have write access to change the contents, via openOutputStream(). I am not aware that you have a means of deleting the underlying content.
Further, I'd like to do this without any UI
The only reason to use ACTION_OPEN_DOCUMENT is to show a UI, to allow the user to choose a piece of content.
My code then queries the MediaStore to determine the file name for the new image
Since there is no requirement for a camera app to update the MediaStore, this does not seem like it will be especially reliable.
Another complicating factor is the lack of an Android API for taking multiple photos and saving them to a prescribed location. There is such an API for taking a single photo but that will not work for my application
Since there is no requirement for a camera app to allow the user to take multiple photos in succession, this does not seem like it will be especially reliable.

Related

How can I use Android scoped storage to store large app specific files?

I have an old drawing app on Android, which stores drawings (.PNG files) out on a specific folder on external storage. With Scoped Storage in Android 11, I need to find a way to save files, but I can't seem to find a solution that meets my needs. My app has its own gallery UI, like many drawing apps, so that I can control the selection/view UX.
I've tried:
MediaStore: I inserted files with MediaStore. The issue I ran into here is that I couldn't figure out a way to query just the files that my app created. Querying mediastore always returns a bunch of stuff I don't want.
Storage Access Framework. My intuition with this framework, is that I would probably spend a lot of time and potentially regret going down that road. I don't like the idea of not controlling the file selector (gallery). I also don't like relying on intents for a critical part of my app.
MANAGE_EXTERNAL_FILES. Based on the criteria defined by Google, my app wouldn't qualify to use this, though it would be great (since I could keep my old code).
Use internal file storage. I coded this up, and it's really clean, but I hesitate to roll this out, because uninstalling would mean that users lose their documents. Note that i have a Share intent, so users can "export" files one at a time.
What I want:
To write files where they aren't removed when the user uninstalls my app.
To write my files somewhere that the user can back them up one way or another.
Simplicity. I don't want to confuse existing users by changing things drastically.
Am I missing something regarding my options here? Can Mediastore effectively partition my files so that I can show them in the gallery? Does internal storage seem like the best option for me?
Just create your files in the old fashioned way in your apps sub folder in public Documents directory.
No special permissions needed.

Android Scoped Storage and External Storage

I have an app that wants to have the ability to download files locally. A user can download a file (no restrictions to file type) and should be able to save it on the device so it can be used for other purposes. I would also like the user to be able to delete the file from the app (they would know which file is downloaded and which isn't, a ticker would indicate if it's backed up locally). Say it is a pdf file -- the user would want to open it with different apps or edit it if they have the ability to, or just share it via email. Considering we cannot opt out of scoped storage anymore (requirement to target 30), I got a couple of questions.
I've tried to use Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getAbsolutePath()) and also getExternalFilesDirPath(ctx, Environment.DIRECTORY_DOWNLOADS)). While the former is deprecated, it works for me in that the Files app can be used to navigate to the file in question (and possibly open/delete the file). On the other hand, I am not being able to delete this file from my app due to lack of permissions. Obviously, the latter path is unable to be navigated to by another app (or is it? I haven't found a way, hence the question).
The other thing I've considered using is the MediaStore API but I'm struggling to see how this is an improvement over the old ways in terms of function. Disregarding moving back to manual content resolver and cursor usage, how should arbitrary files be sorted? Should I manually sort by mime types and have different methods for saving for specific media types? It sounds exceptionally tedious and counter intuitive. If this is the way, so be it, I will implement it, but it does not sound like the way to go. On the positive side, it at least sounds like a solution due to the content resolver's CRUD abilities.
I'm working on a RN app that uses a 3rd party library for the download paths, which old/new versions, respectively, use different paths (rn fetch blob and rn fetch blob util). Additionally, MediaStore API doesn't have a RN implementation as of right now, so everything would have to be done from scratch, too.
What are my options? In the short term I'm considering disabling the erase feature from the app (at least for now). Anything I am missing and should consider? Thanks in advance.

Reading custom files on storage card for android sdk 30

I have an app that creates custom files that are shown in a gallery. They are not images or other standard media files. Because the files are large, I ask users for access to a folder in which the files will be stored.
So far so good.
Reading those files or file info via DocumentFile is so much slower than via File. Operations like
DocumentsContract.findDocumentPath()
documentFile.isFile()
documentFile.getName()
take up to 15ms each, which accumulates to a large time when reading a large number of files.
What's the best way to handle this? Is there another way to do this? I don't want the files to be in internal app memory or users might lose their work when they uninstall the app.
Thanks in advance! This is stressing me out a lot.
Do it asynchronously. Do you need to get all of that data immediately? Probably not. So do it on a thread/in a coroutine, and just program your app to not display the data if you do not yet have it (and refresh itself when you do).
In addition, even if you do need it up front- do you need it for all files up front? In a gallery, you can make do with just the ones that are immediately displayed, and the next few which might be. So only fetch that, and get the others when you need them (or when its likely you'll need them soon). Treat it like fetching results from a web API- you don't fetch every post ever made, you fetch a few dozen, then you fetch a few dozen more when they're getting close to the bottom
In the end, DocumentFile is a wrapper around other APIs, a wrapper that is designed to provide a convenient, File-ish API for developers to use. It is not designed for bulk use. If you want to try to get more speed, you can look at the source code to DocumentFile and related classes used in its implementation, such as DocumentContract19, and work at a lower level.
For example, getName() winds up using this queryForString() method to do the real work. Making a query using ContentResolver adds IPC overhead, so if there are other values that you need that you can get in that same query, you could do the query yourself. For example, isFile() also winds up doing a query, so you could combine those two requests into one and cut the overall time in half (roughly).
Do not use DocumentFile class.
Only use DocumentsContract functions.
Its about twenty times as fast as DocumentFile and nearly as fast as classic file operations

How to share a generated photo from an Android app without storing a file

I have an Android app that generates photos based on a math formula. I'd like to share these photos ideally with the ShareActionProvider.
Constraints:
- Do not create files in the file system.
- Do not require any permissions. (My app requires zero permissions, and I'd like to keep it that way.)
The Android documentation suggests to create a ContentProvider for serving the files. ContentProviders return cursors of columns and rows. How do I convert a file into a Cursor?
The Android documentation says that temporary per-URI permissions have to be generated for the other app. How can I have the ShareActionProvider tell me what permissions to generate? (There is no security concern to sharing these math images with everyone. How can I give access to everyone to my provider?)
PS: I've read dozens of posts related to this topic. Most create files or use the FileProvider (which I don't know how to use without actual files).
Update: This is the closest solution that I found. Yet, it still requires creating temp files, which I'd like to avoid: Android Share Intent for a Bitmap - is it possible not to save it prior sharing?

Do I need to store paths to my files or can I simply note their storage type?

I did some testing and, with an Android emulator, outputted a few pathways:
Internal Storage context.getFilesDir() has path /data/data/package_name/files
Private External Storage with context.getExternalFilesDir(null) has path /storage/emulated/0/Android/data/packagename/files/
Public External Storage with Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) has path /storage/emulated/0/Pictures/
In my app, I've defined an object that has a timestamp string and an array for images (more specifically, paths to those images), and this information is stored in a sqlite database.
But is this a bad idea? Because if someone wanted to move this app to another device, wouldn't the paths change? I'm a little confused as to how files can be moved around, how different devices impact this situation, etc. I am confused as to what the correct approach is for storing image references in the sqlite table. Do you store absolute paths like above? Or do you store a number representing whether the pic is internal/private external/public external and pull that way regardless of device/file structure?
I'm just trying to make sure I don't publish an app that lacks some critical feature simply because I misunderstood something. Like if I start using an app that can take pictures/store them internally, it's all on internal storage, all on this phone. But if I want to start saving pics to an SD card or something else, I have to change where I store the pics and how I reference them. And what if I upgrade my phone? I'd have to somehow carry/transfer all that stuff to a new infrastructure, etc. Or the "cloud", even.
In other words I am just trying to understand all the possible use cases, here, in terms of how Android app data is stored and transferred.
Your questions are a little broad. I will try to answer them in turns:
But is this a bad idea?
Not in itself. The issue here is how are you managing changes and the Android alterations that may occur in the future (as well as the versions you are currently supporting)
Because if someone wanted to move this app to another device, wouldn't the paths change?
Relative paths would never change. They were implemented (I am guessing around API 2~6) to ensure that hard disk management would not be necessary in apps. By getting the Enviroment.getExternalFilesDir(stuffs..) you would garantee a path to the same file, regardless of readl disk allocation.
how files can be moved around, how different devices impact this situation, etc.
Files from Desktops (PC, Mac, Linux, Ubuntu, etc) follow a contracted with Android devices, that simulate the same disk system. This way, a PC can save a file (png, mp3, custom, etc) in a "root folder" that becomes the same root for Enviroment.getExternalFilesDir(stuffs..) BUT data private to your application is never accessible (on non-rooted devices), to the outside system.
Do you store absolute paths like above?
You can. But I believe this goes against the system best-practices. Saving a relative path, then building a File object, in regards to that path is the correct approach. Do note that the File class in Android expects you to know a bit about the file you are refferencing.... either its extension, name regex, physical location, relational location, etc... you need to know at least one of those.
Finally, what I think you asked:
Can the user replicate the same content in different devices?
For this, you must ensure that the user is "know" for instance, using the Play Store account, you can then have a repository, and have devices syncronize with that repository. Or even add a "export" function, that copies files, and imports them somewhere else.

Categories

Resources