Android intent filter for a particular file extension?

Viewed 88254

I want to be able to download a file with a particular extension from the 'net, and have it passed to my application to deal with it, but I haven't been able to figure out the intent filter. The filetype is not included in the mimetypes, and I tried using

<data android:path="*.ext" />

but I couldn't get that to work.

15 Answers

Here is how I defined my activity in my AndroidManifest.xml to get this to work.

<activity android:name="com.keepassdroid.PasswordActivity">
    <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=".*\\.kdb" />
        <data android:host="*" />
    </intent-filter>
</activity>

The scheme of file indicates that this should happen when a local file is opened (rather than protocol like HTTP).

mimeType can be set to */* to match any mime type.

pathPattern is where you specify what extension you want to match (in this example .kdb). The .* at the beginning matches any squence of characters. These strings require double escaping, so \\\\. matches a literal period. Then, you end with your file extension. One caveat with pathPattern is that .* is not a greedy match like you would expect if this was a regular expression. This pattern will fail to match paths that contain a . before the .kdb. For a more detailed discussion of this issue and a workaround see here

Finally, according to the Android documentation, both host and scheme attributes are required for the pathPattern attribute to work, so just set that to the wildcard to match anything.

Now, if you select a .kdb file in an app like Linda File Manager, my app shows up as an option. I should note that this alone does not allow you to download this filetype in a browser, since this only registers with the file scheme. Having an app like Linda File Manager on your phone resisters itself generically allowing you to download any file type.

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.

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.

When an Intent meets a intent-filter, these are the intent-filter requirements: (imagine a checklist).

  • Any matching <action>
  • Any matching <category>
  • Any matching <data mimeType> (easy fix: "/")
  • Optionally:

    • Any matching <data scheme> (easy fix: <data android:scheme="file" /> <data android:scheme="content" />)

    • Any matching <data host> (easy fix: "*")

    • Any matching <data pathPattern/etc.> (for example .*\\.0cc)

Defining multiple <data $type=""> elements checks the $type box iff any <data $type=> matches the Intent.

Omitting mimeType breaks your intent-filter, even though it's seemingly redundant. Omitting <data scheme/host/pathPattern> causes your filter to match everything.

https://f-droid.org/en/packages/de.k3b.android.intentintercept/ is an app designed to receive all intents, and allows you to inspect the intent. I learned that unrecognized file extensions opened via Simple File Manager are delivered with MIME type application/octet-stream.

https://stackoverflow.com/a/4621284/2683842 reports that <data pathPattern=> .*xyz aborts at the first x it sees, and will fail immediately if not followed by yz. So /sdcard/.hidden/foo.0cc will not pass .*\\.0cc unless you try .*\\..*\\.0cc instead.

  • I did not verify whether this workaround is necessary.

End result:

<activity android:name=".Ft2NsfActivity">

    <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:scheme="content" />
        <data android:host="*" />
        <data android:pathPattern=".*\\.ftm"/>
        <data android:pathPattern=".*\\..*\\.ftm"/>
        <data android:pathPattern=".*\\..*\\..*\\.ftm"/>
        <data android:pathPattern=".*\\..*\\..*\\..*\\.ftm"/>
        <data android:pathPattern=".*\\.0cc"/>
        <data android:pathPattern=".*\\..*\\.0cc"/>
        <data android:pathPattern=".*\\..*\\..*\\.0cc"/>
        <data android:pathPattern=".*\\..*\\..*\\..*\\.0cc"/>
        <data android:mimeType="*/*" />
    </intent-filter>

</activity>

Read this https://developer.android.com/training/sharing/receive , this could help to get started. then in the manifest file try the below intent filter when registering receiver in the activity to be opened

<intent-filter>
            <action android:name="android.intent.action.VIEW" />
            <data android:scheme="content"/>
            <category android:name="android.intent.category.BROWSABLE"/>
            <category android:name="android.intent.category.DEFAULT" />
            <data android:mimeType="application/your_extension" />
</intent-filter>

make sure that the extension is provided without "." in it

Related