There's a lot of misinformation on this topic, not least from Google's own documentation. The best, and given the strange logic, possibly the only real documentation is the source code.
The intent filter implementation has logic that almost defies description. The parser code is the other relevant piece of the puzzle.
The following filters get pretty close to sensible behaviour. The path patterns do apply, for "file"
scheme intents.
The global mime type pattern match will match all types so long as the file extension matches. This isn't perfect, but is the only way to match the behaviour of file managers like ES File Explorer, and it is limited to intents where the URI/file extension matches.
I haven't included other schemes like "http"
here, but they will probably work fine on all these filters.
The odd scheme out is "content"
, for which the extension is not available to the filter. But so long as the provider states your MIME type (E.g. Gmail will pass on the MIME type for the attachment unimpeded), the filter will match.
Gotchas to be aware of:
- Be aware that nothing behaves consistently in the filters, it's a maze of special cases, and treats violation of the principle of least surprise as a design goal. None of the pattern matching algorithms follow the same syntax or behaviour. Absence of a field sometimes is a wildcard and sometimes isn't. Attributes within a data element sometimes must go together and sometimes ignore grouping. It really could have been done better.
- The
scheme
AND the host
must be specified for path
rules to match (contrary to Google's API guide, currently).
- At least ES File Explorer generates intents with a MIME type of
""
, which is filtered very differently to null
, is impossible to match explicitly, and can only be matched by the risky "*/*"
filter.
- The
"*/*"
filter will NOT match Intents with a null
MIME type - that requires a separate filter for this specific case with no MIME type at all.
- The
"content"
scheme can only be matched by MIME type, because the original file name isn't available in the intent (at least with Gmail).
- The grouping of attributes in separate
"data"
elements is (almost) irrelevant to the interpretation, with the specific exception of host
and port
- which do pair together. Everything else has no specific association within a "data"
element or between "data"
elements.
With all this in mind, here's an example with comments:
<!--
Capture content by MIME type, which is how Gmail broadcasts
attachment open requests. pathPattern and file extensions
are ignored, so the MIME type *MUST* be explicit, otherwise
we will match absolutely every file opened.
-->
<intent-filter
android:icon="@drawable/icon"
android:label="@string/app_name"
android:priority="50" >
<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" />
<data android:scheme="content" />
<data android:mimeType="application/vnd.my-type" />
</intent-filter>
<!--
Capture file open requests (pathPattern is honoured) where no
MIME type is provided in the Intent. An Intent with a null
MIME type will never be matched by a filter with a set MIME
type, so we need a second intent-filter if we wish to also
match files with this extension and a non-null MIME type
(even if it is non-null but zero length).
-->
<intent-filter
android:icon="@drawable/icon"
android:label="@string/app_name"
android:priority="50" >
<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" />
<data android:host="*" />
<!--
Work around Android's ugly primitive PatternMatcher
implementation that can't cope with finding a . early in
the path unless it's explicitly matched.
-->
<data android:pathPattern=".*\\.my-ext" />
<data android:pathPattern=".*\\..*\\.my-ext" />
<data android:pathPattern=".*\\..*\\..*\\.my-ext" />
<data android:pathPattern=".*\\..*\\..*\\..*\\.my-ext" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.my-ext" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\.my-ext" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\.my-ext" />
</intent-filter>
<!--
Capture file open requests (pathPattern is honoured) where a
(possibly blank) MIME type is provided in the Intent. This
filter may only be necessary for supporting ES File Explorer,
which has the probably buggy behaviour of using an Intent
with a MIME type that is set but zero-length. It's
impossible to match such a type except by using a global
wildcard.
-->
<intent-filter
android:icon="@drawable/icon"
android:label="@string/app_name"
android:priority="50" >
<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" />
<data android:host="*" />
<data android:mimeType="*/*" />
<!--
Work around Android's ugly primitive PatternMatcher
implementation that can't cope with finding a . early in
the path unless it's explicitly matched.
-->
<data android:pathPattern=".*\\.my-ext" />
<data android:pathPattern=".*\\..*\\.my-ext" />
<data android:pathPattern=".*\\..*\\..*\\.my-ext" />
<data android:pathPattern=".*\\..*\\..*\\..*\\.my-ext" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.my-ext" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\.my-ext" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\.my-ext" />
</intent-filter>