How to add custom mime type? - android

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

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.

How can I have my app appear in the intent chooser only for certain urls?

I am developing an app that can extract information from certain web pages. The idea is that when the user is within a specific url path in the browser and press the share button, my app will show up in the list of receiver apps.
I can do that easily by adding this to the manifest:
<intent-filter android:label="#string/app_name" >
<action android:name="android.intent.action.SEND" />
<data android:mimeType="text/plain" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
However, this will make my app appear on the list on all urls, also all those where it will have nothing to do. Instead I would like the app appear in the chooser only from these urls:
www.example.com/foo/bla.html
www.example.com/foo/bar/blabla.html
But not from these:
www.example.com
www.foobar.com
etc. Ie. only from within a certain path on a certain host. Also note that I do not want my app to be launched when the user clicks on links matching the criteria. It should only be invoked from the share menu.
So my question is: How can I limit my app to show up in the intent choose only for certain urls?
Add the following filter to the destination Activity ..
1. host your site name.
2. scheme scheme of your site http or https.
3. path for the file path your app should display.
<intent-filter>
<data
android:host="www.example.com"
android:scheme="http"
android:path="/foo/bla.html"
/>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" >
</category>
<category android:name="android.intent.category.BROWSABLE" >
</category>
</intent-filter>
How can I limit my app to show up in the intent choose only for certain urls?
You can't. ACTION_SEND has no notion of "only from certain URLs", or "only from certain WhatsApp users", or "only from left-handed Alaskan pipe-welders".
This is not significantly different than any other activity. An <intent-filter> only controls what the Intent is being delivered to. Nothing controls what the Intent is delivered from, with the exception of security-related items (permissions, whether or not the component is exported, etc.).
So, with ACTION_VIEW, the URL is something being delivered to another activity ("I wish to view this URL"). This works because the URL is turned into the Uri of the Intent, and that can be filtered upon via <data> elements in the <intent-filter>.
But ACTION_SEND doesn't have to have a URL, and even if there is one, it is merely payload in the form of an extra. It is not something that can be filtered upon.
Hence, what you want is not supported.
Use pathPrefix
<intent-filter>
<data
android:host="www.example.com"
android:scheme="http"
android:pathPrefix="/foo/"
/>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" >
</category>
<category android:name="android.intent.category.BROWSABLE" >
</category>
</intent-filter>

Open my app when an nfc tag of a custom format is discovered

I'm making an app for stock management. Each stock_item will have a tag, with a unique identifier, with a custom_format.
Like this:
stock_manager_pro:stock_item:12345
or
stock_manager_pro:user:john
I want to make my app open automatically when the device discovers a tag with whose content that starts with stock_manager_pro
With this code my apps its opened when the phone discovers a nfc tag with plain/text content, but I want to be able to filter customer texts.
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED"/>
<data android:mimeType="text/plain" />
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
I m working on same problem...
I Found that you should make your own Custom MIME type.
and in manifest file You should simply remove
<action android:name="android.nfc.action.NDEF_DISCOVERED"/>
eg
<intent-filter >
<!-- <action android:name="android.nfc.action.NDEF_DISCOVERED" />-->
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="application/vnd.com.nfctagwriter" />
</intent-filter>
You can add your own mime type and it doesn't have to have the fully qualified "application" at the begining. It also understands "app/customname" .. this is useful for keeping the data size small.
For example stack overflow mime type could be "app/so" instead of "application/stackoverflow".
Although I guess it depends how likely you feel someone else would be using "app/so". In our case it doesn't matter because we are only using it internally.

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>

Launch custom android application from android browser

Can anybody please guide me regarding how to launch my android application from the android browser?
Use an <intent-filter> with a <data> element. For example, to handle all links to twitter.com, you'd put this inside your <activity> in your AndroidManifest.xml:
<intent-filter>
<data android:scheme="http" android:host="twitter.com"/>
<action android:name="android.intent.action.VIEW" />
</intent-filter>
Then, when the user clicks on a link to twitter in the browser, they will be asked what application to use in order to complete the action: the browser or your application.
Of course, if you want to provide tight integration between your website and your app, you can define your own scheme:
<intent-filter>
<data android:scheme="my.special.scheme" />
<action android:name="android.intent.action.VIEW" />
</intent-filter>
Then, in your web app you can put links like:
<a href="my.special.scheme://other/parameters/here">
And when the user clicks it, your app will be launched automatically (because it will probably be the only one that can handle my.special.scheme:// type of uris). The only downside to this is that if the user doesn't have the app installed, they'll get a nasty error. And I'm not sure there's any way to check.
Edit: To answer your question, you can use getIntent().getData() which returns a Uri object. You can then use Uri.* methods to extract the data you need. For example, let's say the user clicked on a link to http://twitter.com/status/1234:
Uri data = getIntent().getData();
String scheme = data.getScheme(); // "http"
String host = data.getHost(); // "twitter.com"
List<String> params = data.getPathSegments();
String first = params.get(0); // "status"
String second = params.get(1); // "1234"
You can do the above anywhere in your Activity, but you're probably going to want to do it in onCreate(). You can also use params.size() to get the number of path segments in the Uri. Look to javadoc or the android developer website for other Uri methods you can use to extract specific parts.
All above answers didn't work for me with CHROME as of 28 Jan 2014
my App launched properly from http://example.com/someresource/ links from apps like hangouts, gmail etc but not from within chrome browser.
to solve this, so that it launches properly from CHROME you have to set intent filter like this
<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="example.com"
android:pathPrefix="/someresource/"
android:scheme="http" />
<data
android:host="www.example.com"
android:pathPrefix="/someresource/"
android:scheme="http" />
</intent-filter>
note the pathPrefix element
your app will now appear inside activity picker whenever user requests http://example.com/someresource/ pattern from chrome browser by clicking a link from google search results or any other website
Please see my comment here: Make a link in the Android browser start up my app?
We strongly discourage people from using their own schemes, unless they are defining a new world-wide internet scheme.
In my case I had to set two categories for the <intent-filter> and then it worked:
<intent-filter>
<data android:scheme="my.special.scheme" />
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
</intent-filter>
For example, You have next things:
A link to open your app: http://example.com
The package name of your app: com.example.mypackage
Then you need to do next:
Add an intent filter to your Activity
(Can be any activity you want. For more info check the documentation).
<activity
android:name=".MainActivity">
<intent-filter android:label="#string/filter_title_view_app_from_web">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<!-- Accepts URIs that begin with "http://example.com" -->
<data
android:host="example.com"
android:scheme="http" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
Create a HTML file to test the link or use this methods.
Open your Activity directly (just open your Activity, without a choosing dialog).
Open this link with browser or your programm (by choosing dialog).
Use Mobile Chrome to test
That's it.
And its not necessary to publish app in market to test deep linking =)
Also, for more information, check documentation and useful presentation.
There should also be <category android:name="android.intent.category.BROWSABLE"/> added to the intent filter to make the activity recognized properly from the link.
The following link gives information on launching the app (if installed) directly from browser. Otherwise it directly opens up the app in play store so that user can seamlessly download.
https://developer.chrome.com/multidevice/android/intents
Please note if your icon is disappear from android launcher when you implement this feature, than you have to split intent-filter.
<activity
android:name=".MainActivity"
android:label="#string/app_name" >
<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" />
<data android:scheme="your-own-uri" />
</intent-filter>
</activity>
Yeah, Chrome searches instead of looking for scheme. If you want to launch your App through URI scheme, use this cool utility App on the Play store. It saved my day :)
https://play.google.com/store/apps/details?id=com.naosim.urlschemesender
Xamarin port of Felix's answer
In your MainActivity, add this (docs: Android.App.IntentFilterAttribute Class):
....
[IntentFilter(new[] {
Intent.ActionView },
Categories = new[] { Intent.CategoryDefault, Intent.CategoryBrowsable },
DataScheme = "my.special.scheme")
]
public class MainActivity : Activity
{
....
Xamarin will add following in the AndroidManifest.xml for you:
<activity
android:label="Something"
android:screenOrientation="portrait"
android:theme="#style/MyTheme"
android:name="blah.MainActivity">
<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="my.special.scheme" />
</intent-filter>
</activity>
And in order to get params (I tested in OnCreate of MainActivity):
var data = Intent.Data;
if (data != null)
{
var scheme = data.Scheme;
var host = data.Host;
var args = data.PathSegments;
if (args.Count > 0)
{
var first = args[0];
var second = args[1];
...
}
}
As far as I know, above can be added in any activity, not only MainActivity
Notes:
When user click on the link, Android OS relaunch your app (kill prev instance, if any, and run new one), means the OnCreate event of app's MainLauncher Activity will be fired again.
With this link: <a href="my.special.scheme://host/arg1/arg2">, in above last code snippet values will be:
scheme: my.special.scheme
host: host
args: ["arg1", "arg2"]
first: arg1
second: arg2
Update: if android creates new instance of your app, you should add android:launchMode="singleTask" too.
Felix's approach to handling deep links is the typical approach to handling deep links. I would also suggest checking out this library to handle the routing and parsing of your deep links:
https://github.com/airbnb/DeepLinkDispatch
You can use annotations to register your Activity for a particular deep link URI, and it will extract out the parameters for you without having to do the usual rigmarole of getting the path segments, matching it, etc. You could simply annotate and activity like this:
#DeepLink("somePath/{someParameter1}/{someParameter2}")
public class MainActivity extends Activity {
...
}
Hey I got the solution. I did not set the category as "Default". Also I was using the Main activity for the intent Data. Now i am using a different activity for the intent data. Thanks for the help. :)
You need to add a pseudo-hostname to the CALLBACK_URL 'app://' doesn't make sense as a URL and cannot be parsed.
example.php:
<?php
if(!isset($_GET['app_link'])){ ?>
<iframe src="example.php?app_link=YourApp://blabla" style="display:none;" scrolling="no" frameborder="0"></iframe>
<iframe src="example.php?full_link=http://play.google.com/xyz" style="display:none;" scrolling="no" frameborder="0"></iframe>
<?php
}
else { ?>
<script type="text/javascript">
self.window.location = '<?php echo $_GET['app_link'];?>';
window.parent.location.href = '<?php echo $_GET['full_link'];?>';
</script>
<?php
}
Look #JRuns answer in here. The idea is to create html with your custom scheme and upload it somewhere. Then if you click on your custom link on your html-file, you will be redirected to your app. I used this article for android. But dont forget to set full name Name = "MyApp.Mobile.Droid.MainActivity" attribute to your target activity.
As of 15/06/2019
what I did is include all four possibilities to open url.
i.e, with http / https and 2 with www in prefix and 2 without www
and by using this my app launches automatically now without asking me to choose a browser and other option.
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https" android:host="example.in" />
<data android:scheme="https" android:host="www.example.in" />
<data android:scheme="http" android:host="example.in" />
<data android:scheme="http" android:host="www.example.in" />
</intent-filter>

Categories

Resources