How do we check if an Android Uri(android.net.Uri) object points to a file or a directory, assuming that the Uri exists?
Thanks.
You can parse the URI and identify is it file or not.
val pointToFile = uri.scheme == "file"
Try to open an InputStream for the uri.
If you succeed it's a file.
Don't forget to close the stream.
Further you can use a content resolver to query for mime type. There exists mime type dir.
Related
I'm trying to adapt a File-based document system to something using DocumentFile in order to allow external storage read/write access on API >= 29.
I get the user to select the SD card root using Intent.ACTION_OPEN_DOCUMENT_TREE, and I get back a Uri as expected, which I can then handle using:
getContentResolver().takePersistableUriPermission(resultData.getData(),
Intent.FLAG_GRANT_READ_URI_PERMISSION
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
I can browse successfully through the external storage contents up to the selected root. All good.
But what I need to be able to do is write an arbitrary file in the chosen (sub)folder, and that's where I'm running into problems.
DocumentFile file = DocumentFile.fromSingleUri(mContext, Uri.parse(toPath));
Uri uri = file.getUri();
FileOutputStream output = mContext.getContentResolver().openOutputStream(uri);
Except on the openOutputStream() call I get:
java.io.FileNotFoundException: Failed to open for writing: java.io.FileNotFoundException: open failed: EISDIR (Is a directory)
That's slightly confusing to me, but the "file not found" part suggests I might need to create the blank output file first, so I try that, like:
DocumentFile file = DocumentFile.fromSingleUri(mContext, Uri.parse(toPath));
Uri uri = file.getUri();
if (file == null) {
return false;
}
if (file.exists()) {
file.delete();
}
DocumentFile.fromTreeUri(mContext, Uri.parse(getParentPath(toPath))).createFile("", uri.getLastPathSegment());
FileOutputStream output = mContext.getContentResolver().openOutputStream(uri);
I get a java.io.IOException:
java.lang.IllegalStateException: Failed to touch /mnt/media_rw/0B07-1910/Testing.tmp: java.io.IOException: Read-only file system
at android.os.Parcel.createException(Parcel.java:2079)
at android.os.Parcel.readException(Parcel.java:2039)
at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:188)
at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:140)
at android.content.ContentProviderProxy.call(ContentProviderNative.java:658)
at android.content.ContentResolver.call(ContentResolver.java:2042)
at android.provider.DocumentsContract.createDocument(DocumentsContract.java:1327)
at androidx.documentfile.provider.TreeDocumentFile.createFile(TreeDocumentFile.java:53)
at androidx.documentfile.provider.TreeDocumentFile.createFile(TreeDocumentFile.java:45)
Which doesn't make sense to me, since the tree should be writeable.
For what it's worth, the Uri I get back from Intent.ACTION_OPEN_DOCUMENT_TREE looks like this:
content://com.android.externalstorage.documents/tree/0B07-1910%3A
Interestingly, when I use that Uri to create a DocumentFile object to browse, using documentFile = DocumentFile.fromTreeUri(context, uri), then documentFile.getURI().toString() looks like:
content://com.android.externalstorage.documents/tree/0B07-1910%3A/document/0B07-1910%3A
i.e., it's had something appended to the end of it.
Then, I descend into what should be a writeable folder (like "Download"), and try creating a writeable file as described above. The "Download" folder gets the Uri:
content://com.android.externalstorage.documents/tree/0B07-1910%3A/document/0B07-1910%3ADownload
and the Uri I'm using for toPath, above, is then:
content://com.android.externalstorage.documents/tree/0B07-1910%3A/document/0B07-1910%3ADownload/Testing.tmp
which leads to the problems described previously trying to create it.
I haven't actually found any decent information about writing an arbitrary file under Storage Access Framework restrictions.
What am I doing wrong? Thanks. :)
Uri uri = uri obtained from ACTION_OPEN_DOCUMENT_TREE
String folderName = "questions.59189631";
DocumentFile documentDir = DocumentFile.fromTreeUri(context, uri);
DocumentFile folder = documentDir.createDirectory(folderName);
return folder.getUri();
Use createFile() for a writable file.
I have a File object and I want to get its Uri, that looks like:
content://media/external/images/media/2683
I've tried the following code:
val uri = FileProvider.getUriForFile(activity, "${BuildConfig.APPLICATION_ID}.file_provider", pictureFile)
But I'm getting this:
content://my.package.name.file_provider/external_files/Pictures/photo-1574403815163.jpg
Is it possible to get the Uri I need?
UPD: The reason I want to get another Uri - I can't create File object from this Uri later.
val uri = FileProvider.getUriForFile(activity, "${BuildConfig.APPLICATION_ID}.file_provider", pictureFile)
val file = File(uri.path)
When I try to file.exists() I'm always getting false.
I can't create File object with this Uri
That's because you don't do that. Moreover, you don't need that. pictureFile is a File.
If your are creating file with actual path then you just concad the content:// to that path
Iam not sure this is correct method or not but i get that what you expected , Try this one
I am using directory selection as described in this Google Sample. It does provide file name and mime type of the children of the selected directory. I can get Document ID of the file too, if I use COLUMN_DOCUMENT_ID on the Cursor Query.
I am interested in the file URI of the children instead. When I use ACTION_OPEN_DOCUMENT instead of ACTION_OPEN_DOCUMENT_TREE, I get the child uri easily which is just obtained from adding a %2Fchildfile.extention (%2F is just a forward slash). So I tried to get child file uri using the following code -
uri = Uri.parse(docUri.toString()+"%2F"+fileName);
I got the file name, however when I run exists() method on it (By converting it into DocumentFile), it returns false. That means, either I don't have the permission of the file or it's not the correct way to get children uri.
Am I missing something here or is there any other way I can select a folder and get file uri of all of it's children easily.
PS: I am currently checking it in Marshamallow.
After reading the doc and trying out certain examples, I got the following way to get a single file Uri from a selected docUri/treeUri
uri = DocumentsContract.buildDocumentUriUsingTree(docUri,docId);
And then you can convert it anytime into a DocumentFile using following code -
DocumentFile file = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
if (DocumentsContract.isDocumentUri(context, uri)) {
file = DocumentFile.fromSingleUri(context, uri);
} else {
file = DocumentFile.fromTreeUri(context, uri);
}
}
fromTreeUri() method is required for the selected Tree Directory, so that it can return true on file.exists() method call.
You need to remember that if the children contain any directory, then you can't call childDirectory.listFiles() on it. It'll give UnsupportedOperationException, because you don't have permission to access the child directory's file. Read more about this here.
I want to get fileName from a uri... I can get a fullpath from Other apps like gallery or other third party file managers, but I can't get fileName from KitKat Document, I don't need a fullPath just the fileName with an inputStream is good for me...
Thanks
Edit: I found a piece of code that solves the issue almost, but it can't get filePath of none known types like *.exe *.vcf , ...
Here's the source
If you have the absolute path of file then simpley create the object of file and use getName method
File f = new File(<Full Path of your file>);
This will return file name with extension.
ANd if you do not want extension
then use CommonsI/O libarary
And from that library you can use
String fileNameWithOutExt = FilenameUtils.removeExtension(fileNameWithExt);
I'm trying to post a notification that lets the user open a locally stored file. My code looks like this:
Intent notificationIntent = new Intent(Intent.ACTION_VIEW);
notificationIntent.setAction(android.content.Intent.ACTION_VIEW);
Uri uri = Uri.fromFile(new File(filename));
notificationIntent.setData(uri);
Where "filename" is the full path to a locally stored file, usually in the /mnt/sdcard/download directory. The files I want to display are of various types: images, PDF documents, HTML, etc.
This works, but sometimes Android tries to open the file as the wrong type. For example, a jpeg file will open in a web browser view and instead of seeing the image, I see the binary data from the file displayed as text. Other times it works file. For example, some PDF files correctly open in a PDF viewer and some do not.
I'm not sure why this is. The documentation says I should not have to pass an explicit content type. If I do set the content type explicitly, things seem to work fine. The problem is, I don't always know what the content type should be (the file is downloaded from an external source and can be anything, and no, the MIME type is not in the HTTP headers, I checked for that).
What can I do here? Is there some function I can call with a filename to have Android return me the best content type for that file? Moreover, why is this not happening automatically when the Intent is processed?
Thanks.
You've most likely figured this out; I'm posting in case someone else is stuck on this. I do the following to get the mime-type of the file:
//Get the file path
Uri path = Uri.fromFile(file);
MimeTypeMap type_map = MimeTypeMap.getSingleton();
//Get the extension from the path
String extension = MimeTypeMap.getFileExtensionFromUrl(path.toString());
extension = extension.toLowerCase();
if (extension.contains(".")) {
extension = extension.substring(extension.lastIndexOf("."));
}
String mime_type = type_map.getMimeTypeFromExtension(extension);