SD-Card access API Android for Lollipop? - android

In this other question
How to use the new SD card access API presented for Android 5.0 (Lollipop)?
It is explained how to use the new API to access the "external SDCard".
But, how can I know the actual directory returned in the result activity?
I mean in function
public void onActivityResult(int requestCode, int resultCode, Intent resultData) {
if (resultCode == RESULT_OK) {
Uri treeUri = resultData.getData();
DocumentFile pickedDir = DocumentFile.fromTreeUri(this, treeUri);
......
How can I get the actual path where "Uri treeUri" points?
Thanks,
Francis.

You can't get the absolute path because the Documents API is designed to abstract this from you.
However, the treeUri does contain the relative path to the file. Try examining the Uri using Uri#getPathSegments()

Use
FileUtil.getFullPathFromTreeUri(treeUri, this)
from
https://github.com/jeisfeld/Augendiagnose/blob/f24ddd7d3da4df94552ca0a9e658399602855a67/AugendiagnoseIdea/augendiagnoseLib/src/main/java/de/jeisfeld/augendiagnoselib/util/imagefile/FileUtil.java
to get the full path. Seperate the path using
final String[] seperated = treeUri.toString().split("\\/");
to get the current dir name.

Related

I Use Storage Access Framework to browse a file link f.cfg, when you select the file, the app need to load another file like f.dat automatically, how?

In Android Studio:
This app must use COMTRADE files. The COMTRADE files are *.cfg *.dat and *.rio files. For example, (user.cfg, user.dat and user.rio). The main file is *.cfg file, so the user must browse by SAF to find *.cfg files, when the user select this file, the app must load other files ( *.dat and *.rio) automatically.
I can not use <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/> for android level 30
So I have to use Storage Access Framework, By below code the user browse *.cfg files and select a file like user.cfg
if(SDK_INT >= Build.VERSION_CODES.R)
{
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("*/*");
Act.startActivityForResult(intent, 111);
}
else
{...}
Then SAF return URI of that user.cfg file, and the app does not have any URI of other files to load.
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
super.onActivityResult(requestCode, resultCode, data);
if(requestCode == 111)
{
if (resultCode == Activity.RESULT_OK)
{
if (data != null)
{
try
{
Uri uri = null;
uri = data.getData();
InputStreamReader inputStreamReader = new InputStreamReader(getContentResolver().openInputStream(uri));
BufferedReader br = new BufferedReader(inputStreamReader);
// I want URI of user.dat how?
InputStreamReader inputStreamReader1 = new InputStreamReader(getContentResolver().openInputStream(uri1));
BufferedReader br1 = new BufferedReader(inputStreamReader1);
....
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
}
}
}
Please help me how can I do this? Thanks.
Please help me how can I do this?
You cannot do this, at least by means of using ACTION_OPEN_DOCUMENT, unless you ask the user to open each of your three documents in turn. Just because the user selected a .cfg file does not mean that you have any rights to any other files, including those adjacent to it.
You could try using ACTION_OPEN_DOCUMENT_TREE and let the user choose the document tree that contains your desired files. From there, you can:
Use DocumentFile.fromTreeUri() to get a DocumentFile representing the tree
Call listFiles() on that DocumentFile to get the direct contents of that tree, in the form of a list of DocumentFile objects
Call getName() on each of those to get their display names, then see which names have your desired file extensions and matching base names (foo.cfg and foo.rio and foo.dat)
For those that you want to use, call getUri() on the DocumentFile to get a Uri to use with ContentResolver and openInputStream() to read in the content

Is there any way of writing files to SDCard with flutter File.writeAsStringSync

I am trying to write to a file that is located in the SDCard, I found out that I need special permission for removable storage something that is not found in any known permission handler plugin for flutter (i tried simple_permission and permission_handler with no use).
I tried to acquire those permissions using the android side of things, so I wrote a simple function that would show the dialog and the user would allow the app to modify the content of the SDCard.
even after acquiring the rights to the SDCARD, I still get the same permissions denied error when trying to save files to the SDCard when using File.writeAsStringSync method.
I want to know if there is any known way/hack/workaround to save files in SDCards in flutter.
The android code i used is the same from this answer : https://stackoverflow.com/a/55024683/6641693
NOTE : I am targetting android 7 and beyond but not android 11.
I solved This, by ditching the dart file saving and using the android SAF.
First, what I did was try to get the sdCard modification permissions.
After that, I get to save the files I need.
here is the code I used to get the permissions ( aka the "allow this app to modify content on your sdCard" dialog )
public void takeCardUriPermission(String sdCardRootPath) {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
File sdCard = new File(sdCardRootPath);
StorageManager storageManager = (StorageManager) getSystemService(Context.STORAGE_SERVICE);
StorageVolume storageVolume = storageManager.getStorageVolume(sdCard);
Intent intent = storageVolume.createAccessIntent(null);
try {
startActivityForResult(intent, 4010);
} catch (ActivityNotFoundException e) {
Log.e("TUNE-IN ANDROID", "takeCardUriPermission: "+e);
}
}
}
protected void onActivityResult(int requestCode, int resultCode, #Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == 4010) {
Uri uri = data.getData();
grantUriPermission(getPackageName(), uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION |
Intent.FLAG_GRANT_READ_URI_PERMISSION);
final int takeFlags = data.getFlags() & (Intent.FLAG_GRANT_WRITE_URI_PERMISSION |
Intent.FLAG_GRANT_READ_URI_PERMISSION);
getContentResolver().takePersistableUriPermission(uri, takeFlags);
methodChannel.invokeMethod("resolveWithSDCardUri",getUri().toString());
}
}
public Uri getUri() {
List<UriPermission> persistedUriPermissions = getContentResolver().getPersistedUriPermissions();
if (persistedUriPermissions.size() > 0) {
UriPermission uriPermission = persistedUriPermissions.get(0);
return uriPermission.getUri();
}
return null;
}
So in order to start the whole permissions acquiring process, you have to first call takeCardUriPermission and passing the URI of the sdCard path.
Note: on my FlutterActivity, i am able to get the sdCardPath directly using getExternalCacheDirs()[1].toString()
After calling takeCardUriPermission and once the allow button is pressed (or the decline) an activity result event will be called and the onActivtyResult method will be called. the requestCode check is useful when you have multiple events and you need to filter this one out.
The activity result code will give the app permissions to modify the files on the sdCard.
The getUri method is the one that we will be using afterwards when trying to save bytes to a file, it returns the URI of the SDCard that we selected (you can have multiple sdCards).
Saving Files
What I used to save a file is a straightforward method. First we need to get the URI of the sdCard and create a Documentfile out of it, then we go through the hierarchy of that directory (DocumentFile can reference files and directories) to find the needed file based on it's URI.
We do this search by splitting the file URI into parts and then navigating the hierarchy by testing if each part exists or not. Once we test all the parts we would have reached our file, if it exists, or we were stuck at the last directory we got to.
the resulting of this iteration is a DocumentFile that we can execute operations on and with.
the following is the full file saving code :
String filepath = (String) arguments.get("filepath");
final byte[] bytes = methodCall.argument("bytes");
try{
if(filepath==null || bytes==null)throw new Exception("Arguments Not found");
DocumentFile documentFile = DocumentFile.fromTreeUri(getApplicationContext(), getUri());
String[] parts = filepath.split("/");
for (int i = 0; i < parts.length; i++) {
DocumentFile nextfile = documentFile.findFile(parts[i]);
if(nextfile!=null){
documentFile=nextfile;
}
}
if(documentFile!=null && documentFile.isFile()){
OutputStream out = getContentResolver().openOutputStream(documentFile.getUri());
out.write(bytes);
out.close();
}else{
throw new Exception("File Not Found");
}
}catch (Exception e){
result.error("400",e.getMessage(),e);
return;
}
result.success(true);
Note: in my code, I am calling this under the MethodChannel's MethodCallHandler which will give me the argument I need: filePath which is the String URI of the file I want to write to and the bytes byte array representing the data I want to save. The same can be said for the result.success
The file writing code is simple: open the file, write the data and close the file.

Attaching files and uploading to server - Android Nougat

I am trying to upload files to server from my application. The user is allowed to attach the file from his internal storage. But some how the path I get from a Nougat phone is different from the standard /storage/emulated/0/... . in few phones I get external_files/... and in others I get root_files/storage/999/.... . In this case I am not getting the right path of the file. How do we handle this scenario?
My code :
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == RESULT_OK) {
Uri selectedImageUri = data.getData();
String filePath = data.getData().getPath();
File sourceFile = new File(fileFields.get(i).getFileName());
FileInputStream fileInputStream = new FileInputStream(sourceFile);
}
}
A Uri is not a file. getPath() is meaningless unless the scheme of the Uri happens to be file. That will not be very common on newer Android devices. Instead, the scheme will be content, and getPath() will be useless to you.
Instead, for both file and content Uri values, use a ContentResolver and openInputStream() to get an InputStream on the content identified by the Uri. This approach works all the way back to Android 1.0.

Get Cursor of given path

I have uri object that contains real path of an image. I know that we need to use selection string while query but i am bad at sql.
How can i get the cursor represent that image?
This is my projection string for my DTO.
String[] projection = {
MediaStore.Images.ImageColumns._ID,
MediaStore.Images.ImageColumns.DISPLAY_NAME,
MediaStore.Images.ImageColumns.DATA,
MediaStore.Images.ImageColumns.WIDTH,
MediaStore.Images.ImageColumns.HEIGHT,
MediaStore.Images.ImageColumns.MIME_TYPE};
And the Uri returns from cropping library.
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == RESULT_OK && requestCode == UCrop.REQUEST_CROP) {
final Uri resultUri = UCrop.getOutput(data);
} else if (resultCode == UCrop.RESULT_ERROR) {
final Throwable cropError = UCrop.getError(data);
}
}
Thanks for helps.
In general, a Uri does not have to represent a file on the filesystem, let alone have a path that you can determine, let alone be a file that you can access.
For a Uri whose scheme is file, getPath() returns the filesystem path to the file. However, you may not have read or write access to that file through the filesystem (though this would indicate a coding error on the part of whoever created the Uri and gave it to you).
For any other sort of Uri — the most common being those with a content scheme — there is no way to determine a filesystem path, because there is no requirement that the content be an ordinary file. It might be an encrypted file, content stored in a BLOB column in a database, content to be downloaded from the network, or countless other patterns.

How to save video file to secondary SD card using MediaMuxer on Android 5.0?

I'm facing a big problem. I use MediaMuxer to create a mp4 file. Here 's constructor of MediaMuxer:
public MediaMuxer (String path, int format)
For example:
sMediaMuxer = new MediaMuxer("/storage/emulated/0/OuputVideos/output1.mp4",OutputFormat.MUXER_OUTPUT_MPEG_4
The problem is that: Everything is ok with primary storage (internal storage), but I can not save output video to secondary SD card (external). My app runs in Lollipop devices (5.0). I read a lot of topics about working with SD card on Lollipop (for example: How to use the new SD card access API presented for Android 5.0 (Lollipop)?). All of them talk about using new Intent ACTION_OPEN_DOCUMENT_TREE to pick an output directory.
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
startActivityForResult(intent, 42);
On result of this Intent:
public void onActivityResult(int requestCode, int resultCode, Intent resultData) {
if (resultCode == RESULT_OK) {
Uri treeUri = resultData.getData();
DocumentFile pickedDir = DocumentFile.fromTreeUri(this, treeUri);
But I don't know how to use pickedDir for MediaMuxer. Can any body help me? Thanks a lot!

Categories

Resources