Android intent filter for a particular file extens

2018-12-31 18:31发布

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.

13条回答
冷夜・残月
2楼-- · 2018-12-31 19:11

On Android 4 the rules became more strict then they used to be. Use:

    <data
      android:host=""
      android:mimeType="*/*"
      android:pathPattern=".*\\.ext"
      android:scheme="file"
    ></data>
查看更多
还给你的自由
3楼-- · 2018-12-31 19:12

If you want the files to be opened directly from Gmail, dropbox or any of the buildin android file tools, then use the following code (delete 'android:host="*"' that made the file unreachable for gmail) :

<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" android:pathPattern=".*\\.kdb" 
          android:mimeType="application/octet-stream"/>


</intent-filter>

<intent-filter>
    <action android:name="android.intent.action.VIEW"/>
    <category android:name="android.intent.category.DEFAULT"/>
    <data android:scheme="file" android:mimeType="*/*"     
          android:pathPattern=".*\\.kdb"/>
</intent-filter>

The data filter must be written in one statement as per Android version 4.x

查看更多
笑指拈花
4楼-- · 2018-12-31 19:12

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.

查看更多
心情的温度
5楼-- · 2018-12-31 19:15

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:

  1. Be aware that nothing behaves consistently in the filters, it's a maze of specal 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.
  2. The scheme AND the host must be specified for path rules to match (contrary to Google's API guide, currently).
  3. 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.
  4. 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.
  5. 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).
  6. 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>
查看更多
忆尘夕之涩
6楼-- · 2018-12-31 19:17

Brian's answer is very close, but here's a clean and error-free way to have your app invoked when trying to open a file with your own custom extension (no need for scheme or host):

<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:mimeType="*/*" />
    <data android:pathPattern="*.*\\.kdb" />
</intent-filter>
查看更多
明月照影归
7楼-- · 2018-12-31 19:19

I've been struggling with this quite a bit for a custom file extension, myself. After a lot of searching, I found this web page where the poster discovered that Android's patternMatcher class (which is used for the pathPattern matching in Intent-Filters) has unexpected behavior when your path contains the first character of your match pattern elsewhere in the path (like if you're trying to match "*.xyz", the patternMatcher class stops if there's an "x" earlier in your path). Here's what he found for a workaround, and worked for me, although it is a bit of a hack:

PatternMatcher is used for pathPattern at IntentFilter But, PatternMatcher's algorithm is quite strange to me. Here is algorithm of Android PatternMatcher.

If there is 'next character' of '.*' pattern in the middle of string, PatternMatcher stops loop at that point. (See PatternMatcher.java of Android framework.)

Ex. string : "this is a my attachment" pattern : ".att.". Android PatternMatcher enter loop to match '.' pattern until meet the next character of pattern (at this example, 'a') So, '.' matching loop stops at index 8 - 'a' between 'is' and 'my'. Therefore result of this match returns 'false'.

Quite strange, isn't it. To workaround this - actually reduce possibility - developer should use annoying stupid pathPattern.

Ex. Goal : Matching uri path which includes 'message'.

<intent-filter>
...
<data android:pathPattern=".*message.*" />
<data android:pathPattern=".*m.*message.*" />
<data android:pathPattern=".*m.*m.*message.*" />
<data android:pathPattern=".*m.*m.*m.*message.*" />
<data android:pathPattern=".*m.*m.*m.*m.*message.*" />
...
</intent-filter>

This is especially issued when matching with custom file extention.

查看更多
登录 后发表回答