Saving files to SD card on Android Lollipop - android

Android 5 introduce new API for working with SD-card. If you want to write files into some directory on the SC-card, y need get access to it.
As far as I know, it could be done in this way:
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
startActivityForResult(intent, 42);
and then:
public void onActivityResult(int requestCode, int resultCode, Intent resultData) {
if (resultCode == RESULT_OK) {
Uri treeUri = resultData.getData();
getActivity().getContentResolver().takePersistableUriPermission(treeUri,
Intent.FLAG_GRANT_READ_URI_PERMISSION |
Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
}
}
This code launch folder picker, and when folder is picked it requires permissions on the folder by it's Uri.
But is it possible to get permissions on exact directory by it's path? Without using folder picker. I meant that I know directory, which I want get access, and I don't want to launch Folder Picker, because it will confuse user.
How to open DocumentFile without user interaction?

It's impossible. User should choose directory using android's file picker. The only way to get access to folder is using:
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
startActivityForResult(intent, 42);
Because file is provided by Storage Access Framework DocumentsProvider. Actually it could return some Uri from 3-rd party app, which is not real file, but some entity, which 3-rd party app present as file.

Related

How to get access to removable sd-card without standard SAF dialog?

Is there any way to access the external SD card without using the standard SAF dialog (with ACTION_OPEN_DOCUMENT_TREE) and onActivityResult?
I know the following possibility:
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
startActivityForResult(intent, 42);
and in onActivityResult
#Override
public void onActivityResult(int requestCode, int resultCode, Intent resultData){
if(resultCode!=RESULT_OK)
return;
Uri treeUri=resultData.getData();
pickedDir= DocumentFile.fromTreeUri(getContext(), treeUri);
getContext().getContentResolver().takePersistableUriPermission(treeUri, Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION );
// do something
// create folder, read/write files to removable sd-card, and so on
}
But this is exactly what opens the standard SAF dialog, which is (in my opinion) not user-friendly.
However, there are many file managers that allow access to the external/removable sd-card without this dialog (e.g. File Manager, RootExplorer, FX File Explorer, etc.).
This means there must be a way to bypass this default routine.
How can I realize this bypass?

No permission to subfolders to write files with DocumentFile after granting permission to root of SD Card

I'm calling an intent using this:
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
startActivityForResult(intent, 42);
and selecting the root of my external SD card then taking the persisting permissions using this:
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == 42) {
Uri treeUri = data.getData();
this.getContentResolver().takePersistableUriPermission(treeUri, (Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION));
}
}
I can write files using an outputstream to the root (my initial selection) but I can not write to any other sub folders unless I ask for permission to them again and explicitly select them.
I am not doing anything fancy right now to determine what folders I have permissions to, I'm just creating a POC and trying to write files to sub folders of a folder that I have selected and given permissions to. I get a null exception on the DocumentFile object for the destination variable below using this:
DocumentFile dir = DocumentFile.fromTreeUri(cont, Uri.parse(myUriToRoot+"/subfolder"));
DocumentFile destination = dir.createFile("image/jpeg", "myfile.jpg");
Again, if I explicitly give permission to that subfolder it works. Everything I've read says that all existing subfolders and any newly created folders underneath the folder to which you've given permissions should allow access to write files but it's not working for me.
What am I missing?
TIA
EDIT: I've also tried using the URL encoding characters for the slashes in my URI and still doesn't work unless I select the subfolder directly with another call to my intent.
I was able to resolve this by instead of setting the fromTreeUri() method to the subfolder directly, keeping that at the root and then using findFile on another DocumentFile object, then using createFile on that next DocumentFile object.

Android: reading file selected in Google Drive file picker

In my app I need to read a settings file, and that settings file can either be on local storage or on the user's Google Drive storage (with Google Drive app installed).
The following opens up a file chooser, first asking the user which file picker to use, including the option of using the Google Drive file picker:
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("file/*");
intent.addCategory(Intent.CATEGORY_OPENABLE);
Intent chooserIntent = Intent.createChooser(intent, "Open file");
startActivityForResult(chooserIntent, REQUEST_CODE_FILE_PICKER);
In my onActivityResult() function, if the user opted to use a file picker to choose a local file, then I already know how to successfully get the file path and read the file. But if the user instead used the Google Drive file picker to choose a remote file, how do I access the file that the user selected?
These are the bare bones of my onActivityResult() function:
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_CODE_FILE_PICKER && resultCode == RESULT_OK) {
if (LOCAL FILE) {
Uri uri = data.getData();
File myFile = new File(uri.getPath());
String filePath = myFile.getAbsolutePath();
// now read file store at 'filePath' from local storage (this part is fine)
} else if (GOOGLE DRIVE FILE) {
// what do I do here to retrieve the selected file?
}
}
super.onActivityResult(requestCode, resultCode, data);
}
When selecting a Google Drive file using the Google Drive file picker, the selected file seems to be downloaded (but to where??) but then nothing happens... how do I access the downloaded file to use in my app?
And in onActivityResult() above, how do I tell if the selected file is actually a Google Drive file, so that I can treat it accordingly? i.e. what is the test for if (GOOGLE DRIVE FILE)?
I believe you're using OpenFileActivityBuilder when Drive is selected as the file picker. It will return a EXTRA_RESPONSE_DRIVE_ID which is the Drive ID of the selected file. After this, you just need to call DriveFile.open to open the said file. This can be your flag to check if you selected a Drive File picker or not.
I cannot answer where the file is downloaded, but my best guess for it is its inside your data/ of the application (which you most likely don't have access to)

Storage Access Framework as a "Private" (local-only) Folder/File Picker?

My ultimate goal is to allow the user to select a folder to save a file to - the file is a video file that will be created at some point after the user has chosen the destination.
I am simply using the storage access framework picker to allow them to select a location for it to be saved in.
First of all, is there a way to allow a user to select only a folder (and not a file/filename)?
The best I can do right now is use the ACTION_CREATE_DOCUMENT Intent in order to get a save location, however I do not really want to specify the filename in the SAF picker (this will be done back in the app)...
Secondly, after reading the Storage Access Framework documentation, and cobbling together some bits from a few code samples, I've got a working DocumentsProvider which almost does what I want - which is to allow the user to browse their external storage (SD Card) directories for a suitable place to save a video file - by adding my own root which points to Environment.getExternalStorageDirectory() to the queryRoots() method.
However, what I really want is for that to be my only root (at the minute I've also got Drive, Downloads etc.).
Is it possible to remove/hide other roots so it essentially becomes an application-specific file picker?
Or even show local storage only (perhaps the Root.FLAG_LOCAL_ONLY flag can help)?
Thanks!
API 21 supports Intent.ACTION_OPEN_DOCUMENT_TREE. This allows you to select the location once and then you can use the provided URI to manipulate its content.
private static final int LOCATION_CHOOSER_REQ_CODE = 4;
public void chooseLocation() {
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
startActivityForResult(intent, LOCATION_CHOOSER_REQ_CODE);
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == LOCATION_CHOOSER_REQ_CODE && resultCode == Activity.RESULT_OK) {
if (data != null) {
Uri uri = data.getData(); // Use this URI to access files
}
}

Open file picker form my Android activity

I'm making an Android app which stores some downloaded pdf files inside the device's SD card.
Everything works fine, but now I want to add a function to just pop up the default android file/folder browser showing the directory where my app stores all the PDF (with subdirectories in it) so that the user sees where his documents are stored and can easily browse them.
I've been throught many other SO questions and forum posts, but it seems this can only be done for music/images/contacts/etc. basically those file types which have a 'dedicated browsing system' but not with general file browsing.
I'm actually using this code:
File file = new File("/sdcard/MySorgenia/Documenti/");
Intent intent = new Intent();
intent.setAction(android.content.Intent.ACTION_VIEW);
Uri data = Uri.fromFile(file);
String type = "*/*";
intent.setDataAndType(data, type);
startActivity(intent);
But this will show me a "Choose the application to complete your action" dialog with many applications such as "Music" "Gallery" etc, but no general purpose one.
Thanks!
Because In android there is no any native application which you can use as a File Explorer and responds to Intent type "*/*"
Implement your own File-Explorer Code for this purpose..
Look at these two Links..
openintents
Android-File-Explore
public void loadfile()
{
private static final int gallery=12;
private static final String type="*/*";
Intent i=new Intent(Intent.ACTION_GET_CONTENT);
i.setType(type);
startActivityForResult(Intent.createChooser(i,"select file"), gallery);
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == gallery && resultCode == RESULT_OK && data != null) {
Uri uploadfileuri = data.getData();
File file = new File(uploadfileuri.getPath());
}
}
Since Android 4.4 KitKat (API level 19), there is an Android built-in file picker: your app invokes the ACTION_OPEN_DOCUMENT and/or ACTION_CREATE_DOCUMENT intent and receives the files returned by document providers. More info about that can be found here:
Open files using storage access framework | Android Developers
Depending on where you want to store files, you may need to request permission:
Request App Permissions | Android Developers
Here is a how to:
An Android Storage Access Framework Example - Techtopia.
And a great working example is Ian Lake's Local Storage. Its source can be found on GitHub:
https://github.com/ianhanniballake/LocalStorage
And the app can be downloaded from Google Play:
https://play.google.com/store/apps/details?id=com.ianhanniballake.localstorage
Most android distributions do not come with a default file browser, and the behavior you noticed is the default android behavior. If there's any good third party file browser installed, it will automatically show up in that list. However it is not guaranteed that every end user will have a file browser installed. A general purpose fragment-widget can be created for this (and probably shared with others).
Look at this file picker, it's the best one I found:

Categories

Resources