Despite this being a simple question I cannot find the answer on google or stackoverflow.
When I use the following code I get this result //com.android.externalstorage.documents/tree/primary:Podcasts
var intent = new Intent(Intent.ActionOpenDocumentTree);
intent.PutExtra("android.content.extra.SHOW_ADVANCED", true);
intent.PutExtra("android.content.extra.FANCY", true);
intent.PutExtra("android.content.extra.SHOW_FILESIZE", true);
Can you help me understand the parts of my result?
How Android Storage works?
To ensure security between Android apps, Android didn't let you directly access every file within the storage system. They have something called ContentProvider.
Think of this content provider like a waiter, that your apps can ask for a certain file/folder (through Content Uri).
Content Uri will look like this: content://[Authority]/[path]/[id] is just an example of Content Uri. com.android.externalstorage.documents is an example of authority (for access to External Storage providers).
So in your case, your Uri will gain you access to the directory of Podcasts in your External Storage.
By having Uri, you can communicate between apps or service provider easily without having to pass real file every time you ask or give one. Just pass a lightweight simple Uri.
What happened in your code?
If you're wondering what happens in your code, try to look at the Reference.
It says:
Allow the user to pick a directory subtree. When invoked, the system will display the various DocumentsProvider instances installed on the device, letting the user navigate through them. Apps can fully manage documents within the returned directory.
I'm not sure what you're trying to achieve (please clarify if you can, so I can help out), but I hope my answer contains enough information.
Related
In my app, I'd like to store a persistent read permission to content provided by Dropbox (among other content providers). The Android Dropbox app doesn't support the Storage Access Framework, so to be able to select content I can't use ACTION_OPEN_DOCUMENT -- instead I need to use ACTION_GET_CONTENT.
However, it seems that some content providers, such as Drive, don't return persistable permissions for URIs returned via ACTION_GET_CONTENT. I believe this is as expected, because GET_CONTENT URIs are not supposed to be persistable. Unfortunately I do need to persist the reference across restarts.
It seems that there is no way to get persistent permissions to a URI in recent API versions if the content provider doesn't support SAF. Is that true? What is a good workaround?
Bad (for my use case) workarounds would be: copying the content and storing it locally, relying on implementation details which are not in spec (e.g. it seems that Dropbox URIs returned by GET_CONTENT are in fact persistable), or not persisting the permission.
It seems that there is no way to get persistent permissions to a URI in recent API versions if the content provider doesn't support SAF. Is that true?
Based on my experiments, yes. More accurately, AFAICT, only Uri values obtained from a DocumentsProvider have a shot at having persistable permissions, in terms of what the framework offers. I do not see how an ordinary ContentProvider can offer this.
What is a good workaround?
Given your list of "bad" workarounds, your best workaround is to use some Dropbox-specific API to allow the user to choose the content and for you to access it over time, if Dropbox offers one.
Of the "bad" workarounds, copying the content is a likely choice — adjust your UI to tell the user that you are "importing" the content, for example, to help indicate that it is indeed a copy.
I'm trying to pick an image from a specific folder using intent in the following manner:
private void selectPicture(){
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
Uri uri = Uri.parse(Environment.getExternalStorageDirectory().getPath() + "/Pictures/");
intent.setDataAndType(uri, "image/*");
startActivityForResult(intent, 1);
}
The image chooser is launched correctly. However, I see all the images (both those from my specified directory and those from other locations on the phone) instead of those from the specified directory only.
I'd appreciate any tips on how to get this to work.
I moved the app's images to the Pictures directory only because I couldn't get to show those in the app's private externalFilesDir(). If you know how to get this functionality to work for images in the app's external files directory, I'd really appreciate that as well.
Thanks!
ACTION_GET_CONTENT was never designed to use a Uri, and it is certainly not documented to only allow the user to choose "those from the specified directory only". Quoting the documentation for ACTION_GET_CONTENT:
Note that no URI is supplied in the intent, as there are no constraints on where the returned data originally comes from.
You are welcome to create your own content browsing UI that constrains the user to a particular directory, perhaps using MediaStore to find the relevant media to allow the user to choose from.
Also, on Android 4.4+, you could use the Storage Access Framework to allow the user to pick content. There may be a flow that allows you to programmatically "pick" the directory, then use that as the basis for the system to allow the user to pick content in that directory. However, I have not seen this done -- usually, the user gets to start at the root of all available storage providers.
Using the manifest I can associate my application with a mime-type (so the app starts when the user click a suitable link with that content-type in the browser). But I can't get the contents of the file. Is there any way to get this?
I know I can obtain the URI from the Intent instance, but I'd rather avoid the second call to the URI.
Thanks,
John.
I'm not sure I understand your question, but at the risk of stating the obvious:
Obtaining the URI from the intent instance needs close to no processing.
You're not making two calls to the URI. One operation is getting the URI string from the intent, then the second operation is using that URI as an address, fetching the image.
But thinking about what you could be doing, I assume when you say URI, you're talking about an address on the net which was passed to your app via another? In which case, if you have no control over the calling application, the only option you have is to fetch the image again. If you know the workings of the app, and it happens to store the images in a globally available area such as the SD card, you'd have to get the file location some how. If you have full control of the app, then it's easy, just send the address of the image on the SD card within the intent.
When querying a ContentProvider on Android, one specifies the ContentProvider of interest by providing the "content URI" for that ContentProvider. What happens when multiple ContentProvider's serve that same URI, either intentionally? or maliciously?
When trying to open a pic on my phone, I've seen it prompt with apps that can "handle" the image. Will the same kind of thing happen here?
Multiple ContentProviders can't do this. The first application that registers a content provider using the element in its manifest has control over the URI pattern. I'm pretty sure that you'll get an installation error if you try to add another provider that uses the same URI pattern. Android keeps track of providers and URIs.
When you see a prompt with multiple apps for handling a file or situation, that's because the apps have specified an with a child that includes
android.intent.category.CATEGORY_ALTERNATIVE or android.intent-category.CATEGORY_SELECTED_ALTERNATVE. In essence, the app or apps are declaring themselves to be alternatives to the action specified in the child. This allows the user to have multiple choices for handling a type of data.
It makes sense to provide alternatives: a user might want to edit a picture, share it via Twitter, or e-mail it.
Note that two content providers can do the same thing, but they can't use the same URI. An app has to make a conscious choice of which one to use, or provide some mechanism of choosing between the two.
To preface my question, couple things to note. I don't want to store said file on the sdcard in this case. The file also cannot be storage directly in the apps local files directory. It needs to be in a subdirectory, so it cannot write the file using openFileOutput() and MODE_WORLD_READABLE.
The app may download files small files like pdfs and store them locally in a subdirectory. I would like to be able to have the user open these files if they have an app that can open them.
For example here is an intent for sending a pdf:
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(path), "application/pdf");
startActivity(intent);
path being something like: /data/data/packagename/files/subdir/example.pdf
That intent will open a pdf viewer, but the viewer is unable to open the file. I assume this is a permissions issue. I tried Mark Murphy's suggestion here: http://groups.google.com/group/android-developers/browse_thread/thread/4e55d869213483a9/b7270078ac1a2744?lnk=raot of using Runtime.getRuntime().exec("chmod 755 " + fileName); but it didn't make any difference. He also suggested a Content Provider but I would like to avoid it if I can because it seems like a lot just to get this file over to another app.
If the content provider is the only option, do I have to save the file to the content provider or can I just use the content provider as a pass through to get it to the other app when I need to?
Thanks, let me know if I'm not being clear.
I also tried all the options you mentioned (and more) and a content provider was the only way that worked for me. I'm not exactly sure what you mean by "save the file to the content provider", but you don't actually need anything in a database for the provider to serve up the file to the other application.
You might like to look at the android:sharedUserId manifest property:
The name of a Linux user ID that will be shared with other
applications. By default, Android assigns each application its own
unique user ID. However, if this attribute is set to the same value
for two or more applications, they will all share the same ID —
provided that they are also signed by the same certificate.
Application with the same user ID can access each other's data and, if
desired, run in the same process.
Be aware that your applications already have a default one that you can't determine (AFAIK). It may be as well to define the same sharedUserId for both apps, then install them on a new emulator.
As a second point, are you sure that you will be able to view a PDF file? I thought that there was no support for it in Android yet.