How to open email attachment in my app on android? - android

I want to open a specific type of file that my app can already send over email as an attachment. I need to be able to have the android email app choose my app to download or open that specific file type. I can't figure out how to set up an intent filter that would let me do that though. Anyone know how this is done?

Intent filters generally work based on MIME type of the file. But if you're using a custom file format that Android's not likely to recognise, then it's not as simple. You can maybe try using the android:pathPattern attribute to try and match the filename, but it's not something I've tried.
I imagine you'd use something like this in your <activity> tag:
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="*/*" />
<data android:pathPattern=".*\\.xyz" />
</intent-filter>

Related

Android: Associate app with custom file type

I've got a file type, the extension is '.rfts' (really it's just storing a JSON string that represents user configurations for an audio amplifier). I'd like to be able to open this file when it's an attachment from an e-mail (Gmail for example) so I can import user settings from another tablet.
Here's what my manifest looks like (note that I didn't include the other activities in this, but there's 4 others that don't have intent filters).
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:supportsRtl="true"
android:theme="#style/RFtheme" >
<activity
android:name=".activity.MainActivity"
android:screenOrientation="landscape"
android:windowSoftInputMode="adjustPan" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<category android:name="android.intent.category.OPENABLE"/>
<data android:scheme="file"/>
<data android:mimeType="*/*"/>
<data android:pathPattern="\\.rfts$"/>
<data android:host="*"/>
</intent-filter>
</activity>
<provider
android:name=".model.FileProvider"
android:authorities="com.rockfordcorp.app3sixty.provider"
android:enabled="true"
android:exported="true" >
</provider>
</application>
I've been trying several other suggested fixes from other questions, but they were for things like opening a pdf from a browser.
When I try to open a .rfts attachment in Gmail, it tells me "You don't have an app that can open this file (.rfts). Try searching google play for one that can"
I'm at a loss as to what I need to be doing here. I don't have any idea what mime Gmail would be using to open a .rfts, nor what scheme it would be using. I've tried a few different combinations but nothings really worked. I just haven't put in the magic combination of category, mimetype, pattern and scheme that Android is looking for to associate this file to my app.
EDIT some success, but not quite there yet.
The questions suggested as fixes are off the mark, and the reason is because the scheme required is actually 'content', not 'file'
The intent filter that works is
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="file" android:mimeType="*/*" android:pathPattern=".*\\.rfts"/>
<data android:scheme="content" android:pathPattern=".*\\.rfts" android:mimeType="application/octet-stream" />
<!-- <data android:host="*"/> -->
</intent-filter>
Without the android:sceheme="content" it does not work.
However, a new problem arises. Gmail now opens all file types that are not previously associated with another app. For example, if I were to try to open a .rfff file, it uses my app. If you try to open a .txt it opens a chooser for Chrome or HTML viewer.
This is close, but it opening other file types is problematic. Android:pathPattern evidently has no effect on what filetype my app is associated with.
As this question was marked as a possible duplicate I want to point out the solution suggested is not working for opening a file from g-mail not web, nor does it cover opening a custom filetype. Using that 'solution' with the file type swapped out causes g-mail to continue to insist there is no app on the device capable of opening the file type.
A different solution likely needs to be provided to associate opening this custom filetype via the intent from Gmail.
Update 2020
Android has moved towards content URIs and MIME-Types for intent filters.
The Problem
A content URI does not necessarily have to contain the file's extension or name and it will be different between different applications that are providing the content/file.
Here are some example content URIs from different email applications for the same email attachment:
Gmail -> content://com.google.android.gm.sapi/some_email#gmail.com/message_attachment_external/%23thread-a%3Ar332738858767305663/%23msg-a%3Ar-5439466788231005876/0.1?account_type=com.google&mimeType=application%2Foctet-stream&rendition=1
Outlook -> content://com.microsoft.office.outlook.fileprovider/outlookfile/data/data/com.microsoft.office.outlook/cache/file-download/file--2146063402/filename.customextention
Samsung Email App -> content://com.samsung.android.email.attachmentprovider/1/1/RAW
As can see they are all different and are not guaranteed to contain anything related to your actual file. Thus, you cannot use the android:pathPattern like most have suggested.
A work around solution for email attachments
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.BROWSABLE"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:scheme="content"/>
<data android:host="*"/>
<!-- Required for Gmail and Samsung Email App -->
<data android:mimeType="application/octet-stream"/>
<!-- Required for Outlook -->
<data android:mimeType="application/my-custom-extension"/>
</intent-filter>
Through testing I found the MIME-Types that Gmail, Outlook, and Samsung Email used and added those to my intent-filter.
Caveats/Gotchas
I found that with my above solution, if I opened any file that was a binary type, it would automatically launch my app. I handled this in my activity by displaying a failed state if we could not parse the file. I figured this was a pretty rare event so it would acceptable.
I could not find any way to launch my app via the file browser without adding <data android:mimeType="*/*"/> to my intent-filter. I couldn't use this because it would then launch my app whenever the user clicked any file on their phone (not just the custom-file-extension ones). I would not recommend adding this to your intent-filter.
I had no luck using android:scheme="file" at all.
I tested this on a Samsung Galaxy S10 on Android 10
Final Thoughts
There is currently no elegant solution for associating your app with a specific extension type in Android. This was the best that I could do in my situation.

Android intent-filter for urls in general?

I have an app that should respond to the ACTION_SEND sharing intent but preferably only when an url is shared. I can't filter on mimetype alone because text/plain is used for a lot of content (although urls are the most common?)
I thought I could use
<data android:scheme="http" />
but after testing it with Chrome (sharing a page) it turned out that doesn't work. If I log the output of intent.getDataString() it returns null so I guess I can't use <data> at all?
My question in short:
If a user shares a webpage or url from for example Chrome, my app should be present in the "Share to" dialog. If it is a textfile it should not.
For completeness, here is my current filter in manifest:
<activity android:name=".ActShareUrl" android:label="Sharedr: shorten url">
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
</activity>
Note: Since I can't find the answer online on my own I think I'm searching for/with the wrong keywords so if anyone can point me to an existing answer I'll update the title of this question if needed.

Intent filter to download attachment from gmail apps on Android

I have android application with intent filter (ACTION_VIEW) to open file and import it into my application. I wish to download file attachment from gmail app into my application. Some of file type (i.e. jpg, png, txt) are saved correctly, but some are not (i.e doc, xls, ppt). I believe I have the correct intent filter for my activity since it works from other app (i.e. dropbox), but not gmail app. Is there any solution for this ?
I was able to make the download and preview buttons pop up on Android in GMail by removing the scheme data filter in my intent (delete the scheme line and give it a try):
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="file" />
<data android:mimeType="*/*" />
<data android:pathPattern=".*\\.ext" />
<data android:host="*" />
</intent-filter>
However, as per the Android documentation, "If a scheme is not specified for the intent filter, all the other URI attributes are ignored." With the scheme and URI attributes removed, the only other way to filter the intents is using Mime type, and we all know that custom file extensions do not have registered mime types.
For reference, URI are of the form:
scheme://host:port/path
pathPrefix
pathPattern
So without a scheme, all of that drops. After discovering the above, I tried the obvious -- use a " * " for the scheme, and even tried " .* ". Neither of those worked. I hope someone else can build off my trials. But I believe it has to do with selecting the correct scheme. Unfortunately, the only schemes I know of are http https content and file, and none of the above are the magic bullet.
EDIT::::::::
I solved this yesterday. Please see my solution:
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="application/*" host="*" android:pathPattern=".*.ext" android:scheme="content" />
</intent-filter>
This intent will cause gmail to display the Download / Preview buttons. In fact, this will also cause your app to open when .ext files are sent as attachments to the regular email client as well.
Since this is one of top question at google related to "gmail attachment intent filter" and I found above answer not working in my case I post the result of my research.
In order to register on intents from gmail, we need to support content scheme:
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="content" android:mimeType="*" android:host="*" />
</intent-filter>
In case of attachments that I tested, URI did not contained file extension, even if it was displayed in gmail, so usage of android:pathPattern blocked receiving gmail intents.
Due to the fact, that registering to all mimeTypes is an overkill, I debugged contents of Intent object (on Java side) and found that in my application text/plain is enough (so your homework is to find proper mimeTypes for your application). My final intent-filter looks like that:
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="content" android:mimeType="text/plain" android:host="*" />
</intent-filter>

Gmail attachment and custom extension

I work currently on an Android application that read file with a custom extension.
One of the mandatory features, is that the app must be proposed by gmail when the user receive a mail with the attachment .ourextension.
I did some research, and found that gmail client on Android doesn't rely on the extension, because in the data of the launched intent the file proposed has no extension. It only rely on the mime-type given by the mail client.
The problem is that our custom file are not detected the same way between mail clients. For example, if I send to myself with the gmail webpage our custom file, the mime-type is detect as application/octet-stream. If a friend of mine send with apple mail desktop software, it is detected as a text/xml (which would be nice). And on another mail client, Evolution, the mime-type is text/plain...
Our application can't handle all those types ! Otherwise, it would be proposed for every type of attachment...
Is there any solution for this ?
Solution to open file with custom extension tested with gmail < 4.2, gmail 4.2, Google Drive and file browser
Code to send file :
sendIntent.setType("application/calc1");
Intent-filter :
<!-- Filter to open file with gmail version < 4.2 -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="application/calc1" />
<data android:pathPattern=".*\\.calc1" />
<data android:host="*" />
</intent-filter>
<!-- Filter to open with file browser or google drive -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="file" />
<data android:mimeType="*/*" />
<data android:pathPattern=".*\\.calc1" />
<data android:host="*" />
</intent-filter>
<!-- Filter to open file with gmail version 4.2 -->
<!-- Save file first with "save" button otherwise gmail crashes -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="application/octet-stream" />
<data android:pathPattern=".*\\.calc1" />
<data android:host="*" />
</intent-filter>
It is possible to get this to work from gmail although it's taking me days of effort to figure out all the issues.
The Important Intent Data
act=android.intent.action.VIEW
dat=file:///mnt/sdcard/Download/Availability Focus.inform
typ=application/octet-stream
flg=0x80001 cmp=air.com.extension/.AppEntry
The Intent Filter that was able to capture it (some notes below)
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.BROWSABLE"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:scheme="file" android:pathPattern=".*\\.inform" android:mimeType="application/octet-stream"/>
<data android:scheme="content" android:pathPattern=".*\\.inform" android:mimeType="application/octet-stream"/>
</intent-filter>
BOTH the content and file scheme's are required. Without the CONTENT scheme gmail will ignore your filter all together. This seems like a bug. Without the FILE scheme gmail will throw an error saying it doesn't know which application to launch from the intent.
07-19 15:38:19.160: ERROR/Gmail(2220): Coun't find Activity for intent
With both scheme's in place the application receives the intent for both Downloaded and Previewed files. They have to be handled differently.
You can still match extension for gmail
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="application/*" host="*" android:pathPattern=".*\\.mht" android:scheme="content" />
</intent-filter>
Make the change for mime type to be / and itll only match on extension
Edit: This answer is 3 years old. I didn't have the time to test the other's answers but mine is clearly outdated so please looks for the others answers of this topic !
I didn't found any solutions, the only way to work is with mime-type, so you must pray for your attachment to be recognize with a known mime-type, and handle this. It means that you will interfer with other application, if your file is recognize as html for example.
You need to handle the case that a file is not readable by your application and show a popup for notifying the user that you can't read the file.
Maybe in a year or two, Gmail will provide a good api...
By the way, the last gmail version add a download button that you must handle to avoid crashes, it works by creating a new file with the uri.

How to add custom mime type?

What I want: To be able to send my custom file by mail and import it with my application from the preview button in GMail or when opening it in a file browser.
What I know: I've read a lot of custom mime type handlers, that android doesn't care about file extension etc., but how to create the mime type for my custom file?
The question: Do I need to be a content provider? I just want to import files (from backup) not provide anything. I've seen people having handlers for "application/abc" saying it's working fine, but how to add that connection for my file "myFile.abc" and the mime type?
Some direction how to register/map custom mime types would be appreciated! :)
As far as I can tell, mime types are pretty flexible (I created mine as application/whatever) and they're accepted immediately by Android, as far back as Dalvik version 2.1. To handle them properly, I added this intent-filter:
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="application/whatever" />
</intent-filter>
There is a caveat though. Even though I always set the type of the send Intent with intent.setType("application/whatever");, on some phones I've seen the actual data on arrival as application/octet (to see the value, I assigned the incoming Intent and inspected its value directly Intent currentIntent = getIntent();). The receiving Android device didn't know what to do with the incoming data and told me so. So I added
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="application/octet-stream" />
</intent-filter>
This approach could be troublesome of course, but the problem with Gmail at least is that it doesn't necessarily write the file with the name as it comes in, which renders any Path I choose to define useless. And at least with an incoming octet-stream you know it's not any app's specific data you're stealing away... Still, you should validate the data afterwards and not just assume it's valid for your app.
I have added custom mime type in android contacts list. After a long research i decided to share this with you guys, i have tested this on all Android cell phone including android 9.0.
here is my Github link
Untested, but something like this should work. Put it in your AndroidManifest.xml with the activity you want to open the file:
<activity name=".ActivityHere">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="file" />
<data android:mimeType="mimeTypeHere" />
</intent-filter>
</activity>
<activity
android:name="MainActivity"
android:label="#string/app_name"
android:theme="#android:style/Theme.NoTitleBar.Fullscreen" >
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:host="{your mime}.com"
android:scheme="http" >
</data>
</intent-filter>
</activity>
<!--
android:scheme="http" will make android "think" thats this is a link
-->
Now, when you receiving a sms with the text "http://{your mime}.com" or clicking link on the web with this text, your activity (MainActivity) will run.
You also can add parameters:
text = "http://{your mime}.com/?number=111";
Then in onCreate() or onResume() methods you'll add:
Intent intentURI = getIntent();
Uri uri = null;
String receivedNum = "";
Log.d("TAG", "intent= "+intentURI);
if (Intent.ACTION_VIEW.equals(intentURI.getAction())) {
if (intentURI!=null){
uri = intentURI.getData();
Log.d("TAG", "uri= "+uri);
}
if (uri!=null)
receivedNum = uri.getQueryParameter("number");
}
Register a custom mime type using android.webkit.MimeTypeMap

Categories

Resources