Fetching attachments with custom extension in my Android application - android

What I am trying to achieve is sounds very familiar, it has been posted many times here and there in Stack Overflow as well, but I'm unable to get it done.
The scenario is, I receive a mail with attachment having custom extension in it. The extension is recognized by my app and it needs the FilePath to process it.
Currently, when I get the attachment in my app using getIntent().getData() all I get is path of the form content://
I have seen methods to convert media content of the type content:// to FilePath like /sdcard/file.ext but I was unable to convert the attachment using that. May be its obvious.
Is there any way that I can process the content:// type without actually downloading it.
Currently from the k9 mail app, when I get the custom extension, it shows my app in the list and opens it through it, but I need FilePath like /sdcard/file.ext and I'm only able to get content:// type.
I hope I made the question clear.
Please Help.
Regards.

A content:// Uri does not necessarily point to a file on the sdcard.
It is more likely that it points to any kind of data stored in a database
or to a content provider that gives you access to the private file storage of another app.
I think the later one is the case with mail attachments (if the content provider is not requesting it directly from a web server). So converting the content:// Uri to a path will not work.
I did the following (not sure if it works also for k9 mail app)
Uri uri = intent.getData();
if (uri.getScheme().equals("content")) {
String fileName = ContentProviderUtils.getAttachmentName(this, uri);
if (fileName.toLowerCase().endsWith(".ext")) {
InputStream is = this.getContentResolver().openInputStream(uri);
// do something
} else {
// not correct extension
return;
}
} else if (uri.getScheme().equals("file")) {
String path = uri.getPath();
if (path.toLowerCase().endsWith(".ext")) {
InputStream is = new FileInputStream(path);
// do something
} else {
// not correct extension
return;
}
}
The attachment name can be found by
public static String getAttachmentName(Context ctxt, Uri contentUri) {
Cursor cursor = ctxt.getContentResolver().query(contentUri, new String[]{MediaStore.MediaColumns.DISPLAY_NAME}, null, null, null);
String res = "";
if (cursor != null){
cursor.moveToFirst();
int nameIdx = cursor.getColumnIndex(MediaStore.MediaColumns.DISPLAY_NAME);
res = cursor.getString(nameIdx);
cursor.close();
}
return res;
}

Related

Is there a way to get 100% correct file extension in Android with scoped storage enabled?

I'm trying to implement a file sending functionality in my Android app (any files are allowed, and the files don't belong to my app). From the ACTION_OPEN_DOCUMENT I receive an InputStream, then I make a temp File object with the name I'm getting from ContentResolver's OpenableColumns.DISPLAY_NAME, and then send the file. The reason I do all of this is that I work with a 3rd party API which allows for File objects only.
But the OpenableColumns.DISPLAY_NAME doesn't guarantee that I get the file name with a file extension as stated in the docs. As far as I understand, there is no way to get the actual filename or physical path of a file with the Scoped Storage enforced in the newer versions of Android. Therefore, I have to check if a filename contains an extension, and if not - get the file's MIME type with ContentResolver and the most common extension for it using the MimeTypeMap. This approach feels to be not very reliable since I have to rely on both ContentResolver correctly determining the MIME type and MimeTypeMap retrieving the correct extension. Getting the extension is crucial at least because users should be able to download and open files on their PC from a desktop app.
So, is it possible to get a filename or at least file extension with a 100% guarantee with scoped storage enabled? Or maybe is there a more efficient way to handle my situation? I'd appreciate some help with this.
Try this method, it helped me:
public static String getFileName(Uri uri, Context context) {
String result = null;
if (uri.getScheme().equals("content")) {
Cursor cursor = context.getContentResolver().query(uri, null, null, null, null);
try {
if (cursor != null && cursor.moveToFirst()) {
result = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
}
} finally {
cursor.close();
}
}
if (result == null) {
result = uri.getPath();
int cut = result.lastIndexOf('/');
if (cut != -1) {
result = result.substring(cut + 1);
}
}
return result;
}

Getting file path from uri on emulator

I want to get file path from Uri for a video. The following method works fine when testing with a real device, however, it fails (returns null) when testing on emulator.
public String getRealPathFromURI(Context context, Uri contentUri) {
Cursor cursor = null;
try {
String[] proj = {MediaStore.Video.Media.DATA};
cursor = context.getContentResolver().query(contentUri, proj, null, null, null);
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DATA);
cursor.moveToFirst();
return cursor.getString(column_index);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (cursor != null) {
cursor.close();
}
}
return null;
}
What is the correct way of getting file path from uri on emulator?
The following method works fine when testing with a real device
Only on the the device that you tried, and only for the app that you tried. Particularly on Android 4.4+, your approach will be unreliable. That is because a Uri is not a file. On older versions of Android, for a Uri from the MediaStore, your approach might work.
Nowadays, do not attempt to get a file for a Uri. Consume the Uri as you are supposed to, using methods on ContentResolver to get an InputStream, the MIME type, etc.
What is the correct way of getting file path from uri on emulator?
There is none. There does not have to be a file path associated with a Uri, let alone a path that your app is able to access using Java file I/O.
As CommonsWare mentioned, an Uri is NOT a File. The general way to deal with Uri is to use an inputstream and save the content as a file (assuming that's what you are looking for). What i typically do is
get the metadata associated with the Uri (to get title / type of data / size)
get the content via an input stream to save it on the device as a file.
Take a look at the "Examine document metadata" and "get an inputstream" on this page: https://developer.android.com/guide/topics/providers/document-provider.html

QuickOffice: get file uri from share function of quickoffice

In my app I can download a document (docx for example) and open it in QuickOffice. After edditing the document I use the save button and after succesfully saved it, I hit the share button and select my app so I can reupload it.
My problem is, is that the uri i got is not the usual uri you would expect as content://storage/map/file.docx or something like that. I get this from quickoffice:
content://com.quickoffice.android.quickcommon.FileContentProvider/zEV5qmvBJOg2GGWldHMJnNK687Ur6qLGbbMbxj0IxV9cDv2mN8XTGqRrEqU4KIfeZuQNMKMJ_eDx%0AN4YiNZwDShhb4E8%3D%0A
My question is, how can I turn this uri to the real path uri from the file (content://storage/map/file.docx for example)
There is no "real path".
A ContentProvider is welcome to store its content wherever it wants, which may not be a file (e.g., BLOB column in a database) and, even if it is, it may not be a file which you can access (e.g., internal storage for the app hosting the ContentProvider.
Please use the various methods on ContentResolver, such as openInputStream(), to access the contents of this provider.
Please use the following code. it worked fine for me.
public static String getContentName(ContentResolver resolver, Uri uri){
String[] ATTACHMENT_META_COLUMNS = {
OpenableColumns.DISPLAY_NAME,
OpenableColumns.SIZE
};
String name = "";
int size= 0;
Cursor metadataCursor = resolver.query(uri, ATTACHMENT_META_COLUMNS, null, null, null);
if (metadataCursor != null) {
try {
if (metadataCursor.moveToFirst()) {
name = metadataCursor.getString(0);
size = metadataCursor.getInt(1);
}
} finally {
metadataCursor.close();
}
}
if (name == null) {
name = uri.getLastPathSegment();
}
return name;
}

Android: resolving uri from content provider, fetching player avatar (Android Game services)

I'm stucked during the Game Services implementation in some of our games. I can't retrieve the user image (using Unity). I can get the Uri of the image (Uri from a content provider), but I cant use ImageManager because it stores the data in a imageView, and I want the url/path of the image to let Unity handle all the data.
My best try is setting a ContentResolver but I get a security error:
Permission Denial: opening provider com.google.android.gms.games.provider.GamesContentProvider
At this point I can't find any solution on that, in fact I'm not able to find anything related with GamesContentProvider at all. So I don't know where to find the right way to retrieve the data or find the permission.
Here you have part of the code:
public String getAvatarUrlGame(boolean highRes)
{
String n = "";
if (isSignedIn()) {
Uri uri;
if(highRes) uri = getGamesClient().getCurrentPlayer().getHiResImageUri();
else uri = getGamesClient().getCurrentPlayer().getIconImageUri();
if (uri != null)
{
Cursor cursor = this.getContentResolver().query(uri, new String[] { android.provider.MediaStore.Images.ImageColumns.DATA }, null, null, null);
cursor.moveToFirst();
n = cursor.getString(0);
cursor.close();
}
}
return n;
}
Any help will be apreciated. Thanks in advance!
The only way to access the images is via ImageManager. You can't use a content resolver. If you only want the images and don't want to handle ImageView's, you can use ImageManager.loadImage(OnImageLoadedListener, Uri), which will call your OnImageLoadedListener with a Drawable representing the image.
Then, to get a Unity-compatible bitmap from the Drawable, just convert it to a bitmap. For info on how to do this step, see this answer, particularly André's answer (the one that has the drawableToBitmap method).
Hope this helps!

How to pass a single Image with Intent which uses content uri (_not_ file uri) in Android

(I have read a lot of similar questions, but bear with me here)
I need to send an image from one Activity (custom camera acitvity), where the second Activity is to upload the image to Picasa Web Album via Google API.
Every example I've found goes something like this:
File f = new File(cacheDir, "image_name.jpg");
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("image/jpeg");
intent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(f));
startActivity(intent);
This works perfectly fine when I use the standard Android Picasa upload activity (or any other sharing app). I can also upload photos via the Picasa example app I am using, when sharing the image from gallery/camera etc.
But I cannot figure out how to build an Intent which uses a "content://---" uri and pass this to another application (neither for this example app or the Picasa standard app)...
Specificially: How can I create an Intent which is compatible with the code below (i.e. uses "content://" uri instead of "file://" uri)?
static class SendData {
String fileName;
Uri uri;
String contentType;
long contentLength;
SendData(Intent intent, ContentResolver contentResolver) {
Bundle extras = intent.getExtras();
if (extras.containsKey(Intent.EXTRA_STREAM)) {
Uri uri = this.uri = (Uri) extras.get(Intent.EXTRA_STREAM);
String scheme = uri.getScheme();
if (scheme.equals("content")) {
Cursor cursor = contentResolver.query(uri, null, null, null, null);
cursor.moveToFirst();
this.fileName = cursor.getString(cursor.getColumnIndexOrThrow(Images.Media.DISPLAY_NAME));
this.contentType = intent.getType();
this.contentLength = cursor.getLong(cursor.getColumnIndexOrThrow(Images.Media.SIZE));
}
}
}
}
Retrieving the File-information from a File uri manually leads to NullPointerException with the Google Http Request used in the app.
Hardcoding the Content uri works. E.g:
uploadIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse("content://media/external/images/media/11"));
Information related to Media files is stored by the MediaStore, which is a ContentProvider (http://developer.android.com/reference/android/provider/MediaStore.Images.html)
MediaStore.Images.DATA column corresponds to the file path and MediaStore.Images._ID column corresponds to the ID.
You need to query for the ID corresponding to your file path and then create a ContentUri out of it (which will be MediaStore.Images.Media.EXTERNAL_CONTENT_URI + id if the image is on the external storage, I'll try to think of a better way to translate the ID into a Content Uri).
http://developer.android.com/reference/android/provider/MediaStore.Images.Media.html#query(android.content.ContentResolver, android.net.Uri, java.lang.String[]

Categories

Resources